首先这个是我自己在学习中总结出来的,也就是自己的理解,仅限交流学习,不足之处请各位指正。
在最开始学习JDBCUtils是,将数据库事务的处理直接放在到dao层实现,这样看起来很简单,但是在MVC分层思想下,但是dao层只负责与数据库的数据交互(CRUD),Service层中要想处理事务,还必须获取Connection连接,这就暴露了Connection连接。
解决的办法可以参照我画的这个图:首先将Connection隐藏,把它隐藏在JDBCUtils中,并将Transaction放到JDBCutils处理,因此在JDBCutils中就会有2个Connection,一个是未开启事务时的Connection ,一个是事务专用的TConnection,这样既满足dao中不处理事务,又满足Service层中隐藏Connection。
最后发现:虽然解决了是事务的 处理,但是每次执行都必须重新getConnection()、releaseConnection()方法,因此直接重写QueryRunner类中的Update()、Query() 方法,将对连接的操作,放到TxQueryRunner中执行,dao中直接实例化TxQueryRunner即可。
jdbcUtils.java
package JDBC_tools;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class jdbcUtils {
public static ComboPooledDataSource ds=new ComboPooledDataSource( );
//表示他是事务的专用连接
private static Connection conn =null;
/**
* 使用连接池返回一个连接对象
* 使用的是文件的默认配置,使用时必须给出c3p0-config.xml
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
if(conn!=null) return conn;
return ds.getConnection();
}
/**
* 返回连接池对象
* @return
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 开启事务
* 必须保证dao commit rollback中的连接是使用同一个连接
*/
public static void beginTransaction(){
try {
//获取连接
conn=getConnection();
//设置自动提交为false
conn.setAutoCommit( false );
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 提交事务
*/
public static void commitTransaction(){
try {
if(conn==null){
throw new SQLException("还没开启事务!");
}
conn.commit();
conn.close();
conn=null;
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 回滚事务
*/
public static void rollbacktranaction(){
try {
if(conn==null){
throw new SQLException("还没开启事务!");
}
conn.rollback();
conn.close();
conn=null;
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 释放连接
*/
public static void releaseConnection(Connection connection) throws SQLException {
//判断是否为事务专用连接
//如果是事务专用连接就不做处理(等待用完后commit处理)
//如果不是事务专用连接,那么就要关闭
//如果conn为空,那么它一定不是事务连接,直接关闭了!
if(conn==null) connection.close();
//将要关闭的连接与事务专用连接对比,如果不一样就直接关闭
if(conn!=connection){
connection.close();
}
}
}
Dao.java
package JDBC_tools;
import org.apache.commons.dbutils.QueryRunner;
import java.sql.Connection;
import java.sql.SQLException;
public class dao {
private static void update() throws SQLException {
QueryRunner qr=new TxQueryRunner();
String sql="update person set money=9999 where id=1";
//这里的连接必须是我自己定义的连接,不能使用数据库连接池的连接,
//必须保证多次调用的Connection是同一个连接
qr.update( sql );
}
public static void main(String[] args) throws SQLException {
update( );
}
}
TxQueryRunner.java
package JDBC_tools;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import java.sql.*;
public class TxQueryRunner 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) throws SQLException {
Connection conn=jdbcUtils.getConnection();
int result= super.update(conn,sql);
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, Object... params) throws SQLException {
Connection conn=jdbcUtils.getConnection();
int result= super.update(conn,sql,params);
jdbcUtils.releaseConnection( conn );
return result;
}
}