您的位置:首页 > 其它

JDBC事务的控制问题

2010-12-31 14:35 393 查看
对目前的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();

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: