ThreadLocal此类是一个以当前线程为key的map对象的构想。
当我们在web开发中,多个浏览器访问的时候,servlet为它们各开线程执行相应代码,而事务的执行依赖于特定的一个Connection对象当中。所以用到了ThreadLocal类来封装<Connection>来取和放。
业务逻辑中不出现 Connection对象,代码风格好
数据库表
1 create table account 2 ( id int primary key, 3 name varchar(20), 4 float money(8,2) 5 )
domain中
1 package cn.itcast.domain; 2 3 import java.io.Serializable; 4 5 public class Account implements Serializable { 6 7 private int id; 8 private String name; 9 private float money; 10 public Account() { 11 } 12 public int getId() { 13 return id; 14 } 15 public void setId(int id) { 16 this.id = id; 17 } 18 public String getName() { 19 return name; 20 } 21 public void setName(String name) { 22 this.name = name; 23 } 24 public float getMoney() { 25 return money; 26 } 27 public void setMoney(float money) { 28 this.money = money; 29 } 30 @Override 31 public String toString() { 32 return "Account [id=" + id + ", name=" + name + ", money=" + money 33 + "]"; 34 } 35 36 }
utils中TransactionUtil.java
1 package cn.itcast.utils; 2 3 import java.io.InputStream; 4 import java.sql.Connection; 5 import java.sql.SQLException; 6 import java.util.Properties; 7 8 import javax.sql.DataSource; 9 10 import org.apache.commons.dbcp.BasicDataSourceFactory; 11 12 public class TransactionUtil { 13 private static ThreadLocal<Connection> t=new ThreadLocal<Connection>(); 14 15 private static DataSource ds; 16 static{ 17 try { 18 InputStream in = TransactionUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"); 19 Properties props = new Properties(); 20 props.load(in); 21 ds = BasicDataSourceFactory.createDataSource(props); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 } 26 27 public static DataSource getDataSource(){ 28 return ds; 29 } 30 31 public static Connection getConnection() 32 { 33 Connection con=null; 34 try { 35 con = t.get();// 获取 value,为 Connection对象 用于事务的处理 36 if(con==null) 37 { 38 con=ds.getConnection(); 39 t.set(con); 40 } 41 } catch (SQLException e) { 42 e.printStackTrace(); 43 } 44 return con; 45 } 46 public static void startTransaction() throws SQLException //把异常抛出去 ,让后面调用 事务 获取异常然后rollback 47 { 48 Connection con=null; 49 try { 50 con = t.get(); //如果前面调用过getConnection(),则con!=null,以下相同 51 if(con==null) 52 { 53 t.set(ds.getConnection()); 54 } 55 con.setAutoCommit(false); //开启事务 56 } catch (SQLException e) { 57 e.printStackTrace(); 58 throw e; 59 } 60 } 61 public static void commitTransaction() throws SQLException 62 { 63 Connection con=null; 64 try { 65 con = t.get(); 66 if(con==null) 67 { 68 t.set(ds.getConnection()); 69 } 70 con.commit(); 71 } catch (SQLException e) { 72 e.printStackTrace(); 73 throw e; 74 } 75 } 76 public static void rollbackTransaction() throws SQLException 77 { 78 Connection con=null; 79 try { 80 con = t.get(); 81 if(con==null) 82 { 83 t.set(ds.getConnection()); 84 } 85 con.rollback(); 86 } catch (SQLException e) { 87 e.printStackTrace(); 88 throw e; 89 } 90 } 91 public static void release() 92 { 93 Connection con=null; 94 95 try { 96 con = t.get(); 97 if(con!=null) 98 { 99 con.close(); 100 t.remove(); 101 } 102 } catch (SQLException e) { 103 e.printStackTrace(); 104 } 105 106 } 107 108 }
daoImpl中
1 package cn.itcast.dao.impl; 2 3 import java.sql.SQLException; 4 5 import org.apache.commons.dbutils.QueryRunner; 6 import org.apache.commons.dbutils.handlers.BeanHandler; 7 8 import cn.itcast.domain.Account; 9 import cn.itcast.utils.TransactionUtil; 10 11 public class AccountDaoImpl { 12 private QueryRunner qr = new QueryRunner(); 13 public void updateAccount(Account c) 14 { 15 try { 16 String sql="update account_table set money=? where name=?"; 17 qr.update(TransactionUtil.getConnection(),sql,c.getMoney(),c.getName()); 18 } catch (SQLException e) { 19 e.printStackTrace(); 20 } 21 } 22 23 public Account findAccount(String accountName) 24 { 25 try { 26 String sql="select * from account_table where name=?"; 27 return qr.query(TransactionUtil.getConnection(),sql, new BeanHandler<Account>(Account.class),accountName); 28 } catch (SQLException e) { 29 e.printStackTrace(); 30 } 31 return null; 32 } 33 }
serviceImpl中
1 package cn.itcast.ServiceImpl; 2 3 import java.sql.SQLException; 4 5 import cn.itcast.dao.impl.AccountDaoImpl; 6 import cn.itcast.domain.Account; 7 import cn.itcast.utils.TransactionUtil; 8 9 public class AccountServeImpl { 10 public AccountDaoImpl dao=new AccountDaoImpl(); 11 public void transe(String name1,String name2,int money) 12 { 13 Account c1=dao.findAccount(name1); 14 Account c2=dao.findAccount(name2); 15 16 17 c1.setMoney(c1.getMoney()-money); 18 c2.setMoney(c2.getMoney()+money); 19 20 try { 21 TransactionUtil.startTransaction(); 22 dao.updateAccount(c1); 23 int i=1/0; //发现异常 24 dao.updateAccount(c2); 25 } catch (Exception e) { //一定要用 捕获的异常来捕获 26 try { 27 TransactionUtil.rollbackTransaction(); 28 } catch (SQLException e1) { 29 e1.printStackTrace(); 30 } 31 e.printStackTrace(); 32 } 33 finally 34 { 35 try { 36 TransactionUtil.commitTransaction(); 37 TransactionUtil.release(); 38 } catch (SQLException e) { 39 e.printStackTrace(); 40 } 41 } 42 43 } 44 }
测试
1 @Test 2 public void test() { 3 AccountServeImpl service=new AccountServeImpl(); 4 service.transe("chenlongfei", "wangfang", 100); 5 }