javaweb开发过程中小工具系列之带事务的QueryRunner

        DBUtils简化了对数据库的操作,再加上我们上一节讲的JdbcUtils,两者配合起来用,也是非常方便,但是操作在操作事务时,也会产生一些冗余的代码。代码示例如下:

Jdbcutils

package cn.ccnu.jdbc;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtils {
	//通过c3p0连接池得到DataSource
	private static DataSource ds = new ComboPooledDataSource();
	//定义一个Connection来判断是否有事务
	private static Connection con = null;
	//返回DataSource
	public static DataSource getDataSource(){
		return ds;
	}
	
	//通过DataSource得到Connection
	public static Connection getConnection() throws SQLException{
		//如果开启了事务,则con不为空,应该直接返回con
		if(con != null){
			return con;
		}
		return ds.getConnection();
	}
	
	// 开启事务
	public static void beginTransaction() throws SQLException {
		//判断con是否为空,如果不为空,则说明事务已经开启
		if(con != null){
			throw new SQLException("事务已经开启了,不能重复开启事务");
		}
		//如果不为空,则开启事务
		con = getConnection();
		//设置事务提交为手动
		con.setAutoCommit(false);
	}

	// 提交事务
	public static void commitTransaction() throws SQLException {
		//判断con是否为空,如果为空,则说明没有开启事务
		if(con == null){
			throw new SQLException("没有开启事务,不能提交事务");
		}
		//如果con不为空,提交事务
		con.commit();
		//事务提交后,关闭连接
		con.close();
		//表示事务已经结束
		con = null;
	}

	// 回滚事务
	public static void rollbackTransaction() throws SQLException {
		//判断con是否为空,如果为空,则说明没有开启事务,也就不能回滚事务
		if(con == null){
			throw new SQLException("没有开启事务,不能回滚事务");
		}
		//事务回滚
		con.rollback();
		//事务回滚后,关闭连接
		con.close();
		//把con置为null,表示事务已经结束
		con = null;
	}

	// 关闭事务
	public static void releaseConnection(Connection connection) throws SQLException {
		//如果参数连接与当前事务连接不相等,则说明参数连接不是事务连接,可以关闭,否则由事务关闭
		if(connection != null && con != connection){
			//如果连接没有被关闭,关闭之
			if(!connection.isClosed()){
				connection.close();
			}
		}
	}
}
测试代码:

package cn.ccnu.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;

public class JdbcUtilsTest {
	@Test
	public void test1() throws SQLException {
		String sql = "update user set salary = ? where id = ?";
		QueryRunner qr = new QueryRunner();
		Connection conn = JdbcUtils.getConnection();
		qr.update(conn, sql, "800", 3);
		JdbcUtils.releaseConnection(conn);
	}

	// 对事务的测试之没有回滚
	@Test
	public void test2() throws SQLException {
		JdbcUtils.beginTransaction();
		Connection conn = JdbcUtils.getConnection();
		QueryRunner qr = new QueryRunner();
		String sql1 = "update user set salary = salary + ? where id = 1;";
		String sql2 = "update user set salary = salary - ? where id = 2;";
		qr.update(conn, sql1, 200);
		qr.update(conn, sql2, 200);
		JdbcUtils.commitTransaction();
		JdbcUtils.releaseConnection(conn);
	}

	// 对事务的测试之有会回滚
	@Test
	public void test3() {
		try {
			//开启事务
			JdbcUtils.beginTransaction();
			//得到连接
			Connection conn = JdbcUtils.getConnection();
			//得到QueryRunner
			QueryRunner qr = new QueryRunner();
			
			String sql1 = "update user set salary = salary + ? where id = 1;";
			String sql2 = "update user set salary = salary - ? where id = 2;";
			//执行第一条sql
			qr.update(conn, sql1, 100);
			//认为制造异常
			int b = 1/0;
			qr.update(conn, sql2, 100);
			//提交事务
			JdbcUtils.commitTransaction();
			//关闭连接,在处理事务时,这句可以不写,因为在提交或回滚时会关闭连接
			JdbcUtils.releaseConnection(conn);
		} catch (Exception e) {
			try {
				JdbcUtils.rollbackTransaction();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}
	}
}
        从上面的代码示例可以看出,功能可以被实现,但是产生了代码冗余,比如说,每次都要得到连接,再把连接作为参数传给QueryRunner,最后关闭连接。每次事务操作时,都要进行这几步,那我们,是不是可以把这几步抽取出来???我们的解决办法是,重写一个类TSQueryRunner来继承QueryRunner,然后覆盖其中带有Connection的方法,这样我们就不用再传Connection进去了。但问题就在于开启事务,提交事务,回滚事务时,所有的连接要是同一个连接,我们如何保证呢?我们所有的方法是,在JdbcUtils中在开启事务时得到的的Connection存放在ThreadLocal中,这样提交事务,回滚事务时所有的连接就是同一个连接了。

TSQueryRunner

package cn.ccnu.queryrunner;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

public class TSQueryRunner extends QueryRunner {

	@Override
	public int[] batch(String sql, Object[][] params)
			throws SQLException {
		Connection conn = JdbcUtils.getConnection();
		int[] result = super.batch(conn, sql, params);
		JdbcUtils.releaseConnection(conn);
		return result;
	}

	@Override
	public <T> T query(String sql, ResultSetHandler<T> rsh,
			Object... params) throws SQLException {
		Connection conn = JdbcUtils.getConnection();
		T result =  super.query(conn, sql, rsh, params);
		JdbcUtils.releaseConnection(conn);
		return result;
	}

	@Override
	public <T> T query(String sql, ResultSetHandler<T> rsh)
			throws SQLException {
		Connection conn = JdbcUtils.getConnection();
		T result =  super.query(conn, sql, rsh);
		JdbcUtils.releaseConnection(conn);
		return result;
	}

	@Override
	public int update(String sql, Object... params)
			throws SQLException {
		Connection conn = JdbcUtils.getConnection();
		int result = super.update(conn, sql, params);
		JdbcUtils.releaseConnection(conn);
		return result;
	}

	@Override
	public int update(String sql, Object param)
			throws SQLException {
		Connection conn = JdbcUtils.getConnection();
		int result =  super.update(conn, sql, param);
		JdbcUtils.releaseConnection(conn);
		return result;
	}

	@Override
	public int update(String sql) throws SQLException {
		Connection conn = JdbcUtils.getConnection();
		int result =  super.update(conn, sql);
		JdbcUtils.releaseConnection(conn);
		return result;
	}
	
}
JdbcUtils

package cn.ccnu.queryrunner;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtils {
	//通过c3p0连接池得到DataSource
	private static DataSource ds = new ComboPooledDataSource();
	//定义一个Connection来判断是否有事务,将con放在ThreadLocal中
	//那么QueryRunner在使用事务时,就可以不用传con,而交由util控制
	private static ThreadLocal<Connection> conn = new ThreadLocal<Connection>();
	//返回DataSource
	public static DataSource getDataSource(){
		return ds;
	}
	
	//通过DataSource得到Connection
	public static Connection getConnection() throws SQLException{
		//得到ThreadLocal中的connection
		Connection con = conn.get();
		//如果开启了事务,则con不为空,应该直接返回con
		if(con != null){
			return con;
		}
		return ds.getConnection();
	}
	
	// 开启事务
	public static void beginTransaction() throws SQLException {
		//得到ThreadLocal中的connection
		Connection con = conn.get();
		//判断con是否为空,如果不为空,则说明事务已经开启
		if(con != null){
			throw new SQLException("事务已经开启了,不能重复开启事务");
		}
		//如果不为空,则开启事务
		con = getConnection();
		//设置事务提交为手动
		con.setAutoCommit(false);
		//把当前开启的事务放入ThreadLocal中
		conn.set(con);
	}

	// 提交事务
	public static void commitTransaction() throws SQLException {
		//得到ThreadLocal中的connection
		Connection con = conn.get();
		//判断con是否为空,如果为空,则说明没有开启事务
		if(con == null){
			throw new SQLException("没有开启事务,不能提交事务");
		}
		//如果con不为空,提交事务
		con.commit();
		//事务提交后,关闭连接
		con.close();
		//将连接移出ThreadLocal
		conn.remove();
	}

	// 回滚事务
	public static void rollbackTransaction() throws SQLException {
		//得到ThreadLocal中的connection
		Connection con = conn.get();
		//判断con是否为空,如果为空,则说明没有开启事务,也就不能回滚事务
		if(con == null){
			throw new SQLException("没有开启事务,不能回滚事务");
		}
		//事务回滚
		con.rollback();
		//事务回滚后,关闭连接
		con.close();
		//将连接移出ThreadLocal
		conn.remove();
	}

	// 关闭事务
	public static void releaseConnection(Connection connection) throws SQLException {
		//得到ThreadLocal中的connection
		Connection con = conn.get();
		//如果参数连接与当前事务连接不相等,则说明参数连接不是事务连接,可以关闭,否则交由事务关闭
		if(connection != null && con != connection){
			//如果连接没有被关闭,关闭之
			if(!connection.isClosed()){
				connection.close();
			}
		}
	}
}
测试代码

package cn.ccnu.queryrunner;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;

public class JdbcUtilsTest {
	// 没有事务的测试
	@Test
	public void test1() throws SQLException {
		String sql = "update user set salary = ? where id = ?";
		QueryRunner qr = new TSQueryRunner();
		qr.update(sql, 800, 3);
	}

	// 对事务的测试之没有回滚
	@Test
	public void test2() throws SQLException {
		JdbcUtils.beginTransaction();
		QueryRunner qr = new TSQueryRunner();
		String sql1 = "update user set salary = salary + ? where id = 1;";
		String sql2 = "update user set salary = salary - ? where id = 2;";
		qr.update(sql1, 200);
		qr.update(sql2, 200);
		JdbcUtils.commitTransaction();
	}

	// 对事务的测试之有会回滚
	@Test
	public void test3() {
		try {
			//开启事务
			JdbcUtils.beginTransaction();
			//得到QueryRunner
			QueryRunner qr = new TSQueryRunner();
			String sql1 = "update user set salary = salary + ? where id = 1;";
			String sql2 = "update user set salary = salary - ? where id = 2;";
			//执行第一条sql
			qr.update(sql1, 100);
			//人为制造异常
			int b = 1/0;
			qr.update(sql2, 100);
			//提交事务
			JdbcUtils.commitTransaction();
		} catch (Exception e) {
			try {
				JdbcUtils.rollbackTransaction();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值