javaweb 事务控制
在service层中采用TransactionManager来对事务进行控制这样的好处是:
在service层屏蔽掉许多try-catch块,例如代码conn.setAutoCommit(false);、conn.commit();、conn.rollback();、conn.close();都会抛出SqlException的,这样代码在service层看起来就很冗余了,因此使用TransactionContext对Connection对象进行封装,在这个过程中将异常捕获并且抛出RuntimeException,这样做符合Spring的理念,因为这些异常是我们没办法处理的.
如下是封装前与封装后的对比:
public long getSample(long parentId) throws ServiceException {
Connection conn = DBConnectionFactory.getInstance.getConnection();
try {
conn.setAutoCommit(false);
NewsCatalogDao dao = new NewsCatalogDaoImpl(conn);
long cnt = dao.getNewsCatalogCount(parentId);
conn.commit();
return cnt;
} catch (SQLException e) {
throw new ConnectionException(e.getMessage());
} catch (DaoException e) {
try {
conn.rollback();
throw new ServiceException(ErrorCode.LIST_NEWSCATALOG_ERROR, e);
} catch (SQLException e1) {
e1.printStackTrace();
throw new ConnectionException(e.getMessage());
}
} finally {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
throw new ConnectionException(e.getMessage());
}
}
}
}
public long getSample(long parentId) throws ServiceException {
TransactionContext tc = transManager.beginTransaction();
try {
NewsCatalogDao dao = new NewsCatalogDaoImpl(tc.getConnection());
long cnt = dao.getNewsCatalogCount(parentId);
transManager.commitTransaction(tc);
return cnt;
} catch (DaoException e) {
e.printStackTrace();
transManager.rollbackTransaction(tc);
throw new ServiceException(ErrorCode.LIST_NEWSCATALOG_ERROR, e);
}
}
一个28行一个13行,显而易见第二种更好,否则第一种的样子去写代码估计得吐血,每个方法都要去处理一大堆SqlException异常。
总体思路就是将Connection对象封装成TransactionContext对象,该对象在遇到conn.setAutoCommit();时将SqlException捕获并抛出成ConnectionException(注:该异常对象是自己定义的并继承RuntimeException),而遇到conn.commit();与conn.rollback()的时候同理也是先捕获SqlException然后抛出ConnectionException,区别在于commit与rollback的try-catch块中要加入finally块,来将Connection对象close掉,通过封装手段我们不再直接与Connection对象打交道而是TransactionContext对象打交道,因TransactionContext已经处理掉Connection对象的异常,所以在Service层就不需要再去处理SqlException了。
因为在Service类里每个方法都需要进行Dao处理也就是说每个方法都要一个连接对象,也就是TransactionContext对象,这样代码情况就会如下:
public long getNewsCatalogCount(long parentId) throws ServiceException {
TransactionContext tc = new TransactionContext(DBConnectionFactory.getInstance.getConnection());
try {
NewsCatalogDao dao = new NewsCatalogDaoImpl(tc.getConnection());
long total = dao.getNewsCatalogCount(parentId);
transManager.commitTransaction(tc);
return total;
} catch(DaoException e) {
e.printStackTrace();
transManager.rollbackTransaction(tc);
throw new ServiceException(ErrorCode.LIST_NEWSCATALOG_ERROR, e);
}
}
在service实例类里每个方法都要去new TransactionContext();这样代码看起来不太好,没有语意,因此就让service实例类去继承AbstractBaseService,在这个类里主要就一个属性TransactionManger transManager = new TransactionManger();这样当service类被实例化的时候就可以引用该属性了.TransactionManager是对TransactionContext的进一步封装,将TransactionContext对象的实例化行为封装到一个方法里,通过方法来控制TransactionContext对象的生成的好处是可以取一个有意义的方法名来增强代码的可读性
public long getNewsCatalogCount(long parentId) throws ServiceException {
TransactionContext tc = transManager.beginTransaction();
try {
NewsCatalogDao dao = new NewsCatalogDaoImpl(tc.getConnection());
long total = dao.getNewsCatalogCount(parentId);
transManager.commitTransaction(tc);
return total;
} catch(DaoException e) {
e.printStackTrace();
transManager.rollbackTransaction(tc);
throw new ServiceException(ErrorCode.LIST_NEWSCATALOG_ERROR, e);
}
}
TransactionManager类代码如下
public class TransactionManager {
/**
* 将TransactionContext对象生成的过程封装起来,从而可以在一个service对象中的不同方法中各自生成TC对象
*/
public TransactionContext beginTransaction() {
return new TransactionContext(DBConnectionFactory.getInstance.getConnection());
}
public void commitTransaction(TransactionContext context) {
context.commitTransaction();
}
public void rollbackTransaction(TransactionContext context) {
context.rollbackTransaction();
}
}
TransactionContext代码如下
package com.shengsiyuan.imis.util;
import java.sql.Connection;
import java.sql.SQLException;
import com.shengsiyuan.imis.exception.ConnectionException;
import com.shengsiyuan.imis.exception.ErrorCode;
/**
*
* <p>Title: TransactionContext</p>
* <p>Description: 将连接对象封装到TransactionContext对象中,并一同将数据库对象处理过程中出现的异常解决掉</p>
* <p>Company: 自由职业</p>
* @author lsw
* @date 2018年2月10日
*/
public class TransactionContext {
private Connection conn;
public TransactionContext(Connection conn) {
this.conn = conn;
if (null == conn) {
throw new ConnectionException(MessageHelper.getExceptionInfo(ErrorCode.DB_CONNECTION_ERROR));
}
try {
conn.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
throw new ConnectionException(MessageHelper.getExceptionInfo(ErrorCode.DB_CONNECTION_ERROR));
}
}
public void rollbackTransaction() {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
throw new ConnectionException(MessageHelper.getExceptionInfo(ErrorCode.DB_ROLLBACK_ERROR));
} finally {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new ConnectionException(MessageHelper.getExceptionInfo(ErrorCode.DB_CONNECTION_ERROR));
}
}
}
public void commitTransaction() {
try {
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
throw new ConnectionException(MessageHelper.getExceptionInfo(ErrorCode.DB_COMMIT_ERROR));
} finally {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new ConnectionException(MessageHelper.getExceptionInfo(ErrorCode.DB_CONNECTION_ERROR));
}
}
}
}
public Connection getConnection() {
return conn;
}
}