在spring中,我们可以通过@Transactional注解,同时配合org.springframework.orm.hibernate3.HibernateTemplate类使用为方法添加事务,无论该方法中涉及多少数据库连接,都在该事务的控制之内。比如举个简单的例子,我有个为博客(blog)添加评论(comment)的方法,此时我需要完成两步:1、向数据库中插入一条评论记录 2、修改该评论所属的blog记录中的评论数字段(total_comment)。 这两步必须在同一个事务中进行,否则,一旦某个操作异常都会造成数据的不一致。简单的代码如下:
/**
* 增加评论
* @return
*/
public boolean addComment(Comment comment){
//第一步,保存评论
save(comment);
//第二步,blog的评论数total_comment字段加1
Blog blog = blogService.getBlogById(comment.getBlogId);
blog.setTotal_comment(blog.getTotal_comment() + 1);
update_total(blog );
}
由于save()和update_total()俩个方法都是通过各自的数据库连接(connection)与数据库打交道,所以如果要想让二者在同一个事务的控制下,必须有个机制,保证二者使用的是同一个连接(connection)。在spring中是通过将connection放入本地线程ThreadLocal中,当addComment()方法执行时,最初会创建个连接(connection)放入ThreadLocal,方法内所有涉及到数据库操作的方法都从这个ThreadLocal获得连接,这样就保证大家使用的是同一个connection。同时,由于连接被放入到ThreadLocal中,避免了多个线程执行时,connection的混乱。
如果使用的是hibernate框架完成底层操作,使用org.springframework.orm.hibernate3.HibernateTemplate类配合@Transactional注解就可完成事务管理。如果使用纯jdbc的话,实现起来也很简单,个人觉得没有必要在使用第三方的东西。
下面的代码是我实现的一个,已经使用在项目中。 分享下。
package com.nec.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
public class GenericDao {
private static ThreadLocal tansaction_connecton_map = new ThreadLocal();
@Autowired
private DataSourceFactory dataSourceFactory;
/**
* 开启事务
* @return
*/
public void startTransaction()throws SQLException,NamingException{
Connection con = createConnection();
con.setAutoCommit(false);
tansaction_connecton_map.set(con);
}
/**
* 提交事务
* @return
*/
public void commit()throws SQLException{
Object obj = tansaction_connecton_map.get();
if(obj != null){
Connection con = (Connection)obj;
con.commit();
}
}
/**
* 回滚事务
* @return
*/
public void rollback()throws SQLException{
Object obj = tansaction_connecton_map.get();
if(obj != null){
Connection con = (Connection)obj;
con.rollback();
}
}
/**
* 销毁事务,释放连接
* @return
* @throws SQLException
* @throws NamingException
*/
public void distroyTransaction(){
try {
Object obj = tansaction_connecton_map.get();
if(obj != null){
Connection con = (Connection)obj;
con.setAutoCommit(true);
con.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
tansaction_connecton_map.set(null);
}
}
private Connection createConnection() throws SQLException, NamingException{
Connection con = this.dataSourceFactory.createDataSource().getConnection();
return con;
}
//获得数据库连接
public Connection getConnection() throws SQLException, NamingException{
Connection con = null;
Object obj = tansaction_connecton_map.get();
if(obj != null){
con = (Connection)obj;
}else{
con = createConnection();
}
return con;
}
//增加记录
public int insert(String sql, Object[] params)throws SQLException, Exception{
int key = -1;
Connection con = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
con = getConnection();
stmt = con.prepareStatement(sql);
setParams(stmt, params);
int nums = stmt.executeUpdate(); //返回受影响的行数
if(nums != 0){
rs = stmt.getGeneratedKeys();
if(rs != null && rs.next()){
key = rs.getInt(1);
}
}
}finally{
closeResultSet(rs);
closeStatement(stmt);
closeConnection(con);
}
return key;
}
//删除记录
public int delete(String sql, Object[] params)throws SQLException, Exception{
int nums = -1;
Connection con = null;
PreparedStatement stmt = null;
try {
con = getConnection();
stmt = con.prepareStatement(sql);
setParams(stmt, params);
nums = stmt.executeUpdate(); //返回受影响的行数
}finally{
closeStatement(stmt);
closeConnection(con);
}
return nums;
}
//修改
public int update(String sql, Object[] params)throws SQLException,NamingException{
int num = -1;
Connection con = null;
PreparedStatement stmt = null;
try {
con = getConnection();
stmt = con.prepareStatement(sql);
setParams(stmt, params);
num = stmt.executeUpdate(); //返回受影响的行数
}finally{
closeStatement(stmt);
closeConnection(con);
}
return num;
}
//求和统计
public long count(String sql, Object[] params){
long count = 0;
Connection con = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
con = this.getConnection();
stmt = con.prepareStatement(sql);
setParams(stmt, params);
rs = stmt.executeQuery();
while(rs != null && rs.next()){
count = rs.getLong(1);
}
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
closeResultSet(rs);
closeStatement(stmt);
closeConnection(con);
}
return count;
}
//关闭ResultSet
public void closeResultSet(ResultSet rs){
try {
if(rs != null){
rs.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//关闭Statement
public void closeStatement(Statement stmt){
try {
if(stmt != null){
stmt.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//归还连接到连接池
public void closeConnection(Connection con){
try {
Object obj = tansaction_connecton_map.get();
if(obj == null && con != null){ //不在事务控制中时直接关闭
con.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//设置参数
public void setParams(PreparedStatement stmt, Object[] params) throws SQLException{
for(int i = 0; i<params.length;){//int, short, long, float, double, boolean, byte, char, List
stmt.setObject(i+1, params[i]);
i++;
}
}
public List<Object> findFields(String sql,final Object[] params)throws SQLException,NamingException{
List<Object> list = null;
Connection con = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
con = this.getConnection();
stmt = con.prepareStatement(sql);
setParams(stmt, params);
rs = stmt.executeQuery();
ResultSetMetaData md = rs.getMetaData();
int columnCount = md.getColumnCount();
if(rs != null && rs.next()){
list = new ArrayList<Object>();
do{
Object[] item = new Object[columnCount];
list.add(item);
for (int i = 0; i < columnCount; i++) {
item[i] = rs.getObject(i+1);
}
}while(rs.next());
}
}finally{
closeResultSet(rs);
closeStatement(stmt);
closeConnection(con);
}
return list;
}
/**
* 专用于前台统计
* @param dataSourceFactory
*/
public MyResultSet countNumService(String sql,final Object[] params){
MyResultSet myResult = null;
Connection con = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
con = this.getConnection();
stmt = con.prepareStatement(sql);
setParams(stmt, params);
rs = stmt.executeQuery();
myResult = new MyResultSet();
myResult.setCon(con);
myResult.setStmt(stmt);
myResult.setRs(rs);
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return myResult;
}
public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
this.dataSourceFactory = dataSourceFactory;
}
}
其中 DataSourceFactory 是个获得数据源的工厂,大家可以使用任何方式获得。
这样,在把最初的addComment()方法改造下,就成了:
public boolean addComment(Comment comment){
try {
this.blogDao.startTransaction();//开启事务
//第一步,保存评论
save(comment);
//第二步,blog的评论数total_comment字段加1
Blog blog = blogService.getBlogById(comment.getBlogId);
blog.setTotal_comment(blog.getTotal_comment() + 1);
update_total(blog );
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
try {
this.blogDao.rollback();//回滚事务
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}finally{
this.blogDao.distroyTransaction();//销毁事务
}
}
其中 blogDao继承了GenericDao。
完毕。