如果使用Spring框架,它对事务做了很好的封装,通过它的AOP配置,可以灵活的配置在任何一层;但是在很多的需求和应用,直接使用JDBC事务控制还是有其优势的。所以,本文来讨论纯JDBC事务的控制问题。
其实,事务是以业务逻辑为基础的;一个完整的业务应该对应业务层里的一个方法;如果业务操作失败,则整个事务回滚;所以,事务控制是绝对应该放在业务层的;但是,持久层的设计应该遵循一个很重要的原则:持久层应该保证操作的原子性,就是说持久层里的每个方法都应该是不可以分割的。
例如针对一个部门和员工的CRUD操作。如果要删除某个部门,就应该在DeptDao中有一个删除部门的方法:
public class DeptDao {
public void deleteDept(int id) ; //删除指定ID的部门
}
在EmpDao中有一个删除指定部门下的所有员工的方法
public interface EmpDao{
public void deleteEmpByDeptId(int id); //删除指定部门下的所有员工
}
这样,就应该在业务层DeptService中的删除部门方法中组合这两个方法,即把它们放置在同一个事务中:
public class DeptService{
public void deleteDept(int id){
try{
//启动JDBC事务
//调用EmpDao中的deleteEmpByDeptId(id)方法
//调用DeptDao中的deleteDept(id)方法
//操作正常,提交事务
}catch(Exception e){
//异常,回滚事务
}
}
}
要让这两个Dao操作在同一个事务,最主要的一点就是:启动JDBC事务中使用的数据库连接和两个Dao操作方法里获得的数据库连接要是同一个连接。参照Spring的JDBC事务原理,可以使用ThreadLocal类, 在启动JDBC事务中把数据库连接绑定到线程,以保证在同一个线程下获得的都是同一个连接。这样就达到目的了。
如下数据库工具类:
- packagecom.tjitcast.common;
- importjava.io.IOException;
- importjava.sql.Connection;
- importjava.sql.SQLException;
- importjava.util.Properties;
- importjavax.sql.DataSource;
- importcom.mchange.v2.c3p0.DataSources;
- importcom.tjitcast.dao.DaoException;
- /**
- *数据库工具类
- *可以根据classpath下配置文件jdbc.properties中配置的参数来获取数据库连接并绑定到当前线程上
- *可以获取JDBC的事务管理器
- *@authorqiujy
- *@version0.9Beta
- */
- publicclassDbUtils{
- privatestaticPropertiesprop=newProperties();
- /**数据源*/
- privatestaticDataSourceds=null;
- //用来把Connection绑定到当前线程上的变量
- privatestaticThreadLocal<Connection>tl=newThreadLocal<Connection>();
- static{
- try{
- prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));
- }catch(IOExceptione){
- e.printStackTrace();
- System.out.println("在classpath下没有找到jdbc.properties文件");
- }
- //使用C3P0连接池技术
- try{
- Class.forName("com.mysql.jdbc.Driver");
- DataSourceunpooled=DataSources.unpooledDataSource(
- prop.getProperty("url"),
- prop.getProperty("user"),
- prop.getProperty("password"));
- ds=DataSources.pooledDataSource(unpooled);
- }catch(ClassNotFoundExceptione){
- e.printStackTrace();
- }catch(SQLExceptione){
- e.printStackTrace();
- }
- }
- privateDbUtils(){}
- /**
- *根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上
- *@return成功,返回Connection对象,否则返回null
- */
- publicstaticsynchronizedConnectiongetConnection(){
- Connectionconn=tl.get();//先从当前线程上取出连接实例
- if(null==conn){//如果当前线程上没有Connection的实例
- try{
- conn=ds.getConnection();//从连接池中取出一个连接实例
- tl.set(conn);//把它绑定到当前线程上
- }catch(SQLExceptione){
- e.printStackTrace();
- }
- }
- returnconn;
- }
- /**
- *获取事务管理器
- *@return事务管理实例
- */
- publicstaticsynchronizedTransactionManagergetTranManager(){
- returnnewTransactionManager(getConnection());
- }
- /**
- *关闭数据库连接,并卸装线程绑定
- *@paramconn要关闭数据库连接实例
- *@throwsDaoException
- */
- protectedstaticvoidclose(Connectionconn)throwsDaoException{
- if(conn!=null){
- try{
- conn.close();
- }catch(SQLExceptione){
- thrownewDaoException("关闭连接时出现异常",e);
- }finally{
- tl.remove();//卸装线程绑定
- }
- }
- }
- }
如下事务管理器类:
- packagecom.tjitcast.common;
- importjava.sql.Connection;
- importjava.sql.SQLException;
- importcom.tjitcast.dao.DaoException;
- /**
- *事务管理器
- *@authorqiujy
- *@version0.9Beta
- */
- publicclassTransactionManager{
- privateConnectionconn;
- protectedTransactionManager(Connectionconn){
- this.conn=conn;
- }
- /**开启事务*/
- publicvoidbeginTransaction()throwsDaoException{
- try{
- conn.setAutoCommit(false);//把事务提交方式改为手工提交
- }catch(SQLExceptione){
- thrownewDaoException("开户事务时出现异常",e);
- }
- }
- /**提交事务并关闭连接*/
- publicvoidcommitAndClose()throwsDaoException{
- try{
- conn.commit();//提交事务
- }catch(SQLExceptione){
- thrownewDaoException("提交事务时出现异常",e);
- }finally{
- DbUtils.close(conn);
- }
- }
- /**回滚并关闭连接*/
- publicvoidrollbackAndClose()throwsDaoException{
- try{
- conn.rollback();
- }catch(SQLExceptione){
- thrownewDaoException("回滚事务时出现异常",e);
- }finally{
- DbUtils.close(conn);
- }
- }
- }
如下业务层类:
- packagecom.tjitcast.service;
- importjava.util.List;
- importcom.tjitcast.common.DbUtils;
- importcom.tjitcast.common.TransactionManager;
- importcom.tjitcast.dao.DaoException;
- importcom.tjitcast.dao.DaoFactory;
- importcom.tjitcast.dao.DeptDao;
- importcom.tjitcast.dao.EmployeeDao;
- importcom.tjitcast.entity.Dept;
- importcom.tjitcast.entity.Employee;
- importcom.tjitcast.entity.PageModel;
- /**
- *业务层门面-->添加事务控制
- *@authorqiujy
- */
- publicclassServiceFacade{
- privateDeptDaodeptDao=DaoFactory.getInstance("deptDao",DeptDao.class);
- privateEmployeeDaoempDao=DaoFactory.getInstance("empDao",EmployeeDao.class);
- /**
- *新增部门
- *@paramdept
- */
- publicvoidinsertDept(Deptdept){
- TransactionManagertx=DbUtils.getTranManager();
- try{
- tx.beginTransaction();
- deptDao.insert(dept);
- tx.commitAndClose();
- }catch(DaoExceptione){
- tx.rollbackAndClose();
- }
- }
- /**
- *新增员工
- *@paramemp员工
- */
- publicvoidinsertEmp(Employeeemp){
- TransactionManagertx=DbUtils.getTranManager();
- try{
- tx.beginTransaction();
- empDao.insert(emp);
- tx.commitAndClose();
- }catch(DaoExceptione){
- tx.rollbackAndClose();
- }
- }
- /**
- *获取所有部门的列表
- *@return部门列表
- */
- publicList<Dept>getDeptList(){
- List<Dept>list=null;
- TransactionManagertx=DbUtils.getTranManager();
- try{
- tx.beginTransaction();
- list=deptDao.getDeptList();
- tx.commitAndClose();
- }catch(DaoExceptione){
- e.printStackTrace();
- tx.rollbackAndClose();
- }
- returnlist;
- }
- /**
- *获取指定部门下的员工分页列表
- *@paramdeptId
- *@parampageNo
- *@parampageSize
- *@return符合条件的PageModel
- */
- publicPageModel<Employee>getEmpListByDeptId(intdeptId,intpageNo,intpageSize){
- PageModel<Employee>pm=null;
- TransactionManagertx=DbUtils.getTranManager();
- try{
- tx.beginTransaction();
- pm=empDao.getEmpListByDeptId(deptId,pageNo,pageSize);
- tx.commitAndClose();
- }catch(DaoExceptione){
- tx.rollbackAndClose();
- }
- returnpm;
- }
- /**
- *删除指定ID的部门
- *@paramid部门ID
- */
- publicvoiddeleteDept(intid){
- TransactionManagertx=DbUtils.getTranManager();
- try{
- tx.beginTransaction();
- empDao.deleteByDeptId(id);//先删除指定ID部门下的所有员工
- deptDao.delete(id);//再删除该部门
- tx.commitAndClose();
- }catch(DaoExceptione){
- tx.rollbackAndClose();
- }
- }
- }
具体的示例代码结构如下(Eclipse工程):
原文 :