首先我们会想到改写Dao 层来开启事务,从而实现转账。
public void transfer(int sourceid,int targetid,float money) throws SQLException{
Connection conn = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
QueryRunner runner = new QueryRunner();//不要给连接池
String sql1 = "update account set money=money-100 where id=1";
runner.update(conn,sql1);
String sql2 = "update account set money=money+100 where id=2";
runner.update(conn,sql2);
conn.commit();
}catch(Exception e){
if(conn!=null){
conn.rollback();
}
}finally {
conn.close();
}
- 但是,这样在Dao 层我们就会出现了 service 层的代码,违反了三层架构模式的**。所以我们仅仅改写Dao为:
public void update(Account account) throws SQLException{
QueryRunner qr = new QueryRunner();//不传递连接池
String sql = "update account set name=?,money=? where id=?";
Object params[] = {account.getName(),account.getMoney(),account.getId()};
qr.update(conn,sql, params);
}
public Account find(int id) throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "select * from account where id=?";
Account account = qr.query(conn,sql, new BeanHandler<>(Account.class),id );
return account;
}
public void transfer(int sourceId,int targetId,float money) throws SQLException{
Connection conn = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
AccountDao dao = new AccountDao(conn);
Account source = dao.find(sourceId);
Account target = dao.find(targetId);
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
//这个要同一事务,也就是2次update方法共享同一连接
//只能在service 自己管理连接
dao.update(source);
//int i=1/0;
dao.update(target);
conn.commit();
}catch(Exception e){
if (conn!=null) {
conn.rollback();
}
e.printStackTrace();
}finally {
if(conn!=null){
conn.close();
}
}
}
- 但是这样的代码并不优雅,所以我们会用到 ThreadLocal 类来管理连接:
ThreadLocal 类:一个容器,向这个容器存储的对象,在当前线程内都可以取得出来。
public static void main(String[] args) {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread);
ThreadLocal t = new ThreadLocal<>();
t.set("aaa");//把某个对象绑定到当前线程上 map(currentThread,"aaa")
String value = (String) t.get();
System.out.println(value);
}
增加一个线程容器
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
改写方法
public static void startTransaction(){
try {
Connection conn = threadLocal.get();
if(conn==null){
conn = getConnection();
threadLocal.set(conn);//把conn绑定到当前线程
}
conn.setAutoCommit(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void rollback(){
try {
Connection conn = threadLocal.get();
if (conn!=null) {
conn.rollback();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void commit(){
try {
Connection conn = threadLocal.get();
if (conn!=null) {
conn.commit();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void release(){
try {
Connection conn = threadLocal.get();
if (conn!=null) {
conn.close();
threadLocal.remove();//接触当前线程上绑定的connection
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException{
Connection conn = threadLocal.get();
if (conn==null) {
conn = getDataSource().getConnection();
threadLocal.set(conn);
}
return conn;
}
改写Service 层:
public void transfer(int sourceId,int targetId,float money) throws SQLException{
Connection conn = null;
try{
JdbcUtils.startTransaction();
AccountDao dao = new AccountDao();
Account source = dao.find(sourceId);
Account target = dao.find(targetId);
source.setMoney(source.getMoney()-money);
target.setMoney(target.getMoney()+money);
//这个要同一事务,也就是2次update方法共享同一连接
//只能在service 自己管理连接
dao.update(source);
//int i=1/0;
dao.update(target);
JdbcUtils.commit();
}catch(Exception e){
if (conn!=null) {
JdbcUtils.rollback();
}
e.printStackTrace();
}finally {
JdbcUtils.release();
}
}
改写 Dao 层:
public void update(Account account) throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "update account set name=?,money=? where id=?";
Object params[] = {account.getName(),account.getMoney(),account.getId()};
qr.update(JdbcUtils.getConnection(),sql, params);
}
public Account find(int id) throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "select * from account where id=?";
Account account =
qr.query(JdbcUtils.getConnection(),sql, new BeanHandler<>(Account.class),id );
return account;
}