读书人

分层架构上的纯JDBC事务控制简单解决方

发布时间: 2012-11-01 11:11:31 作者: rapoo

分层架构下的纯JDBC事务控制简单解决方案

对目前的JavaEE企业应用开发来说,基本都会采用分层的架构, 这样可以分散关注、松散耦合、逻辑复用、标准定义。例如,目前使用SSH组合时典型的四层架构:表示层、业务层、持久层和数据层;那么,在四层架构中,事务的控制应该放在哪一层呢?


如果使用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事务中把数据库连接绑定到线程,以保证在同一个线程下获得的都是同一个连接。这样就达到目的了。

如下数据库工具类:

view plaincopy to clipboardprint?
package com.tjitcast.common;??
import java.io.IOException;??
import java.sql.Connection;??
import java.sql.SQLException;??
import java.util.Properties;??
import javax.sql.DataSource;??
import com.mchange.v2.c3p0.DataSources;??
import com.tjitcast.dao.DaoException;??
/**?
?*? 数据库工具类?
?*? 可以根据classpath下配置文件jdbc.properties中配置的参数来获取数据库连接并绑定到当前线程上?
?*? 可以获取JDBC的事务管理器?
?* @author qiujy?
?* @version 0.9Beta?
?*/?
public class DbUtils {??
??? private static Properties prop = new Properties();??
??? /** 数据源 */?
??? private static DataSource ds = null;???
??????
??? //用来把Connection绑定到当前线程上的变量??
??? private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();??
??? static{??
??????? try {??
??????????? prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));??
??????? } catch (IOException e) {??
??????????? e.printStackTrace();??
??????????? System.out.println("在classpath下没有找到jdbc.properties文件");??
??????? }??
??????????
??????? //使用C3P0连接池技术??
??????? try {??
??????????? Class.forName("com.mysql.jdbc.Driver");??
??????????????
??????????? DataSource unpooled = DataSources.unpooledDataSource(??
??????????????????? prop.getProperty("url"),??
??????????????????? prop.getProperty("user"),??
??????????????????? prop.getProperty("password"));??
??????????? ds = DataSources.pooledDataSource(unpooled);??
??????????????
??????? } catch (ClassNotFoundException e) {??
??????????? e.printStackTrace();??
??????? } catch (SQLException e) {??
??????????? e.printStackTrace();??
??????? }??
??? }??
??????
??? private DbUtils(){}??
??????
??? /**?
???? * 根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上?
???? * @return 成功,返回Connection对象,否则返回null?
???? */?
??? public static synchronized Connection getConnection(){??
??????? Connection conn = tl.get(); //先从当前线程上取出连接实例??
??????????
??????? if(null == conn){ //如果当前线程上没有Connection的实例???
??????????? try {??
??????????????? conn = ds.getConnection(); // 从连接池中取出一个连接实例???
??????????????? tl.set(conn);? //把它绑定到当前线程上??
??????????? } catch (SQLException e) {??
??????????????? e.printStackTrace();??
??????????? }??
??????? }??
??????? return conn;??
??? }??
??? /**?
???? * 获取事务管理器?
???? * @return 事务管理实例?
???? */?
??? public static synchronized TransactionManager getTranManager(){??
??????? return new TransactionManager(getConnection());??
??? }??
??????
??? /**?
???? * 关闭数据库连接,并卸装线程绑定?
???? * @param conn 要关闭数据库连接实例?
???? * @throws DaoException??
???? */?
??? protected static void close(Connection conn) throws DaoException{??
??????? if(conn != null){??
??????????? try {??
??????????????? conn.close();??
??????????? } catch (SQLException e) {??
??????????????? throw new DaoException("关闭连接时出现异常",e);??
??????????? } finally {??
??????????????????? tl.remove(); //卸装线程绑定??
??????????? }??
??????? }??
??? }??
??????
}?
package com.tjitcast.common;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.DataSources;
import com.tjitcast.dao.DaoException;
/**
?*? 数据库工具类
?*? 可以根据classpath下配置文件jdbc.properties中配置的参数来获取数据库连接并绑定到当前线程上
?*? 可以获取JDBC的事务管理器
?* @author qiujy
?* @version 0.9Beta
?*/
public class DbUtils {
?private static Properties prop = new Properties();
?/** 数据源 */
?private static DataSource ds = null;
?
?//用来把Connection绑定到当前线程上的变量
?private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
?static{
??try {
???prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"));
??} catch (IOException e) {
???e.printStackTrace();
???System.out.println("在classpath下没有找到jdbc.properties文件");
??}
??
??//使用C3P0连接池技术
??try {
???Class.forName("com.mysql.jdbc.Driver");
???
???DataSource unpooled = DataSources.unpooledDataSource(
?????prop.getProperty("url"),
?????prop.getProperty("user"),
?????prop.getProperty("password"));
???ds = DataSources.pooledDataSource(unpooled);
???
??} catch (ClassNotFoundException e) {
???e.printStackTrace();
??} catch (SQLException e) {
???e.printStackTrace();
??}
?}
?
?private DbUtils(){}
?
?/**
? * 根据数据库的默认连接参数获取数据库的Connection对象,并绑定到当前线程上
? * @return 成功,返回Connection对象,否则返回null
? */
?public static synchronized Connection getConnection(){
??Connection conn = tl.get(); //先从当前线程上取出连接实例
??
??if(null == conn){ //如果当前线程上没有Connection的实例
???try {
????conn = ds.getConnection(); // 从连接池中取出一个连接实例
????tl.set(conn);? //把它绑定到当前线程上
???} catch (SQLException e) {
????e.printStackTrace();
???}
??}
??return conn;
?}
?/**
? * 获取事务管理器
? * @return 事务管理实例
? */
?public static synchronized TransactionManager getTranManager(){
??return new TransactionManager(getConnection());
?}
?
?/**
? * 关闭数据库连接,并卸装线程绑定
? * @param conn 要关闭数据库连接实例
? * @throws DaoException
? */
?protected static void close(Connection conn) throws DaoException{
??if(conn != null){
???try {
????conn.close();
???} catch (SQLException e) {
????throw new DaoException("关闭连接时出现异常",e);
???} finally {
?????????? tl.remove(); //卸装线程绑定
???}
??}
?}
?
}
?

如下事务管理器类:

package com.tjitcast.common;??
import java.sql.Connection;??
import java.sql.SQLException;??
import com.tjitcast.dao.DaoException;??
/**?
?* 事务管理器?
?* @author qiujy?
?* @version 0.9Beta?
?*/?
public class TransactionManager {??
??? private Connection conn;??
??????
??? protected TransactionManager(Connection conn) {??
??????? this.conn = conn;??
??? }??
??????
??? /** 开启事务 */?
??? public void beginTransaction() throws DaoException{??
??????? try {??
??????????? conn.setAutoCommit(false);? //把事务提交方式改为手工提交??
??????? } catch (SQLException e) {??
??????????? throw new DaoException("开户事务时出现异常",e);??
??????? }??
??? }??
??????
??? /** 提交事务并关闭连接 */?
??? public void commitAndClose() throws DaoException{??
??????? try {??
??????????? conn.commit(); //提交事务??
??????? } catch (SQLException e) {??
??????????? throw new DaoException("提交事务时出现异常",e);??
??????? }finally{??
??????????? DbUtils.close(conn);??
??????? }??
??? }??
??????
??? /** 回滚并关闭连接 */?
??? public void rollbackAndClose()throws DaoException{??
??????? try {??
??????????? conn.rollback();??
??????? } catch (SQLException e) {??
??????????? throw new DaoException("回滚事务时出现异常",e);??
??????? }finally{??
??????????? DbUtils.close(conn);??
??????? }??
??? }??
}?
package com.tjitcast.common;
import java.sql.Connection;
import java.sql.SQLException;
import com.tjitcast.dao.DaoException;
/**
?* 事务管理器
?* @author qiujy
?* @version 0.9Beta
?*/
public class TransactionManager {
?private Connection conn;
?
?protected TransactionManager(Connection conn) {
??this.conn = conn;
?}
?
?/** 开启事务 */
?public void beginTransaction() throws DaoException{
??try {
???conn.setAutoCommit(false);? //把事务提交方式改为手工提交
??} catch (SQLException e) {
???throw new DaoException("开户事务时出现异常",e);
??}
?}
?
?/** 提交事务并关闭连接 */
?public void commitAndClose() throws DaoException{
??try {
???conn.commit(); //提交事务
??} catch (SQLException e) {
???throw new DaoException("提交事务时出现异常",e);
??}finally{
???DbUtils.close(conn);
??}
?}
?
?/** 回滚并关闭连接 */
?public void rollbackAndClose()throws DaoException{
??try {
???conn.rollback();
??} catch (SQLException e) {
???throw new DaoException("回滚事务时出现异常",e);
??}finally{
???DbUtils.close(conn);
??}
?}
}
?

如下业务层类:

view plaincopy to clipboardprint?
package com.tjitcast.service;??
import java.util.List;??
import com.tjitcast.common.DbUtils;??
import com.tjitcast.common.TransactionManager;??
import com.tjitcast.dao.DaoException;??
import com.tjitcast.dao.DaoFactory;??
import com.tjitcast.dao.DeptDao;??
import com.tjitcast.dao.EmployeeDao;??
import com.tjitcast.entity.Dept;??
import com.tjitcast.entity.Employee;??
import com.tjitcast.entity.PageModel;??
/**?
?* 业务层门面? --> 添加事务控制?
?* @author qiujy?
?*/?
public class ServiceFacade {??
??? private DeptDao deptDao = DaoFactory.getInstance("deptDao", DeptDao.class);??
??? private EmployeeDao empDao? = DaoFactory.getInstance("empDao", EmployeeDao.class);??
??????
??? /**?
???? * 新增部门?
???? * @param dept?
???? */?
??? public void insertDept(Dept dept){??
??????? TransactionManager tx = DbUtils.getTranManager();??
??????? try{??
??????????? tx.beginTransaction();??
??????????????
??????????? deptDao.insert(dept);??
??????????????
??????????? tx.commitAndClose();??
??????? }catch (DaoException e) {??
??????????? tx.rollbackAndClose();??
??????? }??
??? }??
??????
??? /**?
???? * 新增员工?
???? * @param emp 员工?
???? */?
??? public void insertEmp(Employee emp){??
??????? TransactionManager tx = DbUtils.getTranManager();??
??????? try{??
??????????? tx.beginTransaction();??
??????????????
??????????? empDao.insert(emp);??
??????????????
??????????? tx.commitAndClose();??
??????? }catch (DaoException e) {??
??????????? tx.rollbackAndClose();??
??????? }??
??? }??
??????
??? /**?
???? * 获取所有部门的列表?
???? * @return 部门列表?
???? */?
??? public List<Dept> getDeptList(){??
??????? List<Dept> list = null;??
??????????
??????? TransactionManager tx = DbUtils.getTranManager();??
??????? try{??
??????????? tx.beginTransaction();??
??????????????
??????????? list = deptDao.getDeptList();??
??????????????
??????????? tx.commitAndClose();??
??????? }catch (DaoException e) {??
??????????? e.printStackTrace();??
??????????? tx.rollbackAndClose();??
??????? }??
??????????
??????? return list;??
??? }??
??????
??? /**?
???? * 获取指定部门下的员工分页列表?
???? * @param deptId?
???? * @param pageNo?
???? * @param pageSize?
???? * @return 符合条件的PageModel?
???? */?
??? public PageModel<Employee> getEmpListByDeptId(int deptId, int pageNo, int pageSize){??
??????? PageModel<Employee> pm = null;???
??????? TransactionManager tx = DbUtils.getTranManager();??
??????? try{??
??????????? tx.beginTransaction();??
??????????????
??????????? pm = empDao.getEmpListByDeptId(deptId, pageNo, pageSize);??
??????????????
??????????? tx.commitAndClose();??
??????? }catch (DaoException e) {??
??????????? tx.rollbackAndClose();??
??????? }??
??????? return pm;??
??? }??
??????
??? /**?
???? * 删除指定ID的部门?
???? * @param id 部门ID?
???? */?
??? public void deleteDept(int id){??
??????????
??????? TransactionManager tx = DbUtils.getTranManager();??
??????? try{??
??????????? tx.beginTransaction();??
??????????????
??????????? empDao.deleteByDeptId(id); //先删除指定ID部门下的所有员工??
??????????? deptDao.delete(id);? //再删除该部门??
??????????????
??????????? tx.commitAndClose();??
??????? }catch (DaoException e) {??
??????????? tx.rollbackAndClose();??
??????? }??
??? }??
}?
package com.tjitcast.service;
import java.util.List;
import com.tjitcast.common.DbUtils;
import com.tjitcast.common.TransactionManager;
import com.tjitcast.dao.DaoException;
import com.tjitcast.dao.DaoFactory;
import com.tjitcast.dao.DeptDao;
import com.tjitcast.dao.EmployeeDao;
import com.tjitcast.entity.Dept;
import com.tjitcast.entity.Employee;
import com.tjitcast.entity.PageModel;
/**
?* 业务层门面? --> 添加事务控制
?* @author qiujy
?*/
public class ServiceFacade {
?private DeptDao deptDao = DaoFactory.getInstance("deptDao", DeptDao.class);
?private EmployeeDao empDao? = DaoFactory.getInstance("empDao", EmployeeDao.class);
?
?/**
? * 新增部门
? * @param dept
? */
?public void insertDept(Dept dept){
??TransactionManager tx = DbUtils.getTranManager();
??try{
???tx.beginTransaction();
???
???deptDao.insert(dept);
???
???tx.commitAndClose();
??}catch (DaoException e) {
???tx.rollbackAndClose();
??}
?}
?
?/**
? * 新增员工
? * @param emp 员工
? */
?public void insertEmp(Employee emp){
??TransactionManager tx = DbUtils.getTranManager();
??try{
???tx.beginTransaction();
???
???empDao.insert(emp);
???
???tx.commitAndClose();
??}catch (DaoException e) {
???tx.rollbackAndClose();
??}
?}
?
?/**
? * 获取所有部门的列表
? * @return 部门列表
? */
?public List<Dept> getDeptList(){
??List<Dept> list = null;
??
??TransactionManager tx = DbUtils.getTranManager();
??try{
???tx.beginTransaction();
???
???list = deptDao.getDeptList();
???
???tx.commitAndClose();
??}catch (DaoException e) {
???e.printStackTrace();
???tx.rollbackAndClose();
??}
??
??return list;
?}
?
?/**
? * 获取指定部门下的员工分页列表
? * @param deptId
? * @param pageNo
? * @param pageSize
? * @return 符合条件的PageModel
? */
?public PageModel<Employee> getEmpListByDeptId(int deptId, int pageNo, int pageSize){
??PageModel<Employee> pm = null;
??TransactionManager tx = DbUtils.getTranManager();
??try{
???tx.beginTransaction();
???
???pm = empDao.getEmpListByDeptId(deptId, pageNo, pageSize);
???
???tx.commitAndClose();
??}catch (DaoException e) {
???tx.rollbackAndClose();
??}
??return pm;
?}
?
?/**
? * 删除指定ID的部门
? * @param id 部门ID
? */
?public void deleteDept(int id){
??
??TransactionManager tx = DbUtils.getTranManager();
??try{
???tx.beginTransaction();
???
???empDao.deleteByDeptId(id); //先删除指定ID部门下的所有员工
???deptDao.delete(id);? //再删除该部门
???
???tx.commitAndClose();
??}catch (DaoException e) {
???tx.rollbackAndClose();
??}
?}
}

?


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/qjyong/archive/2010/04/08/5464835.aspx

读书人网 >其他数据库

热点推荐