DAO中的事务
其实在DAO中处理事务真的是“小菜一碟”
try{
con.commit();
}catch(Exception e){
con.rollback();
}
但是dao层中只能是对账户金额的修改而不是业务的处理
service层中也可以使用刚才的格式,使用con会暴露出service直接使用数据库的问题
我们希望这样来处理事务:
public class XXXService(){
private XXXDao dao=new XXXDao();
public void serviceMethod(){
try{
JdbcUtils.beginTransaction();
dao.daoMethod1(...);
dao.daoMethod2(...);
JdbcUtils.commitTransaction();
}catch(Exception e){
JdbcUtils.rollbackTransaction();
}
}
}
修改后:
package cn.itcast.cn; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class JdbcUtils { /* * 配置文件的恶魔人配置!要求你必须给出c3p0-config。xnl! */ private static ComboPooledDataSource dataSource=new ComboPooledDataSource("oracle-config"); /** * 它是事务专用连接 */ private static Connection con=null; /** * 使用连接池返回一个连接对象 * @return * @throws SQLException */ public static Connection getConnection() throws SQLException{ //当con!=null,表示已经调用过beginTransaction方法了 if(con!=null) return con; return dataSource.getConnection(); } /** * 返回连接池对象 * @return */ public static DataSource getDataSource(){ return dataSource; } /** * 1、开启一个Connection,设置它的setAutoCommit(false) * 2、还要保证dao中使用的连接是我们刚刚创建的 * ------------------------ * 1、创建一个Connection,设置为手动提交 * 2、把这个Connection给dao用 * 3、还要让commitTransaction或rollbackTransaction可以获取到 * @throws SQLException */ public static void beignTransaction() throws SQLException{ if(con!=null) throw new SQLException("已经开始了事务,就不要继续开启事务了!"); con=getConnection(); con.setAutoCommit(false); } /** * 提交事务 * 获取之前开启的Connection,兵提交 * @throws SQLException */ public static void commitTransaction() throws SQLException{ if(con==null) throw new SQLException("还没有开启事务,不能提交!"); con.commit(); con.close(); con=null;//因为前面的close()不会销毁连接而是放回连接池 } /** * 回滚事务 * 获取之前开启的Connection,兵回滚 * @throws SQLException */ public static void rollbackTransaction() throws SQLException{ if(con==null) throw new SQLException("还没有开启事务,不能提交!"); con.rollback(); con.close(); con=null;//因为前面的close()不会销毁连接而是放回连接池 } }
package cn.itcast.cn; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; public class AccountDao { public static void update(String name,double money) throws SQLException{ QueryRunner qr=new QueryRunner(); String sql="UPDATE account SET balance=balance+? WHERE aname=?"; Object[] params={money,name}; //我们需要自己来提供连接,保证多次调用使用的是同一个连接 Connection con=JdbcUtils.getConnection(); qr.update(con, sql, params); } }
package cn.itcast.cn; import java.sql.SQLException; import org.junit.Test; @SuppressWarnings("static-access") public class Demo1 { private AccountDao dao=new AccountDao(); @Test public void serviceMethod(){ try{ JdbcUtils.beignTransaction(); dao.update("zs", -1000); dao.update("lisi", +1000); JdbcUtils.commitTransaction(); }catch(Exception e){ try { JdbcUtils.rollbackTransaction(); } catch (SQLException e1) { e1.printStackTrace(); } } } }
针对前面的针对多线程并发问题和代码复杂度问题作出的再次优化:
package cn.itcast.cn; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class JdbcUtils { /* * 配置文件的恶魔人配置!要求你必须给出c3p0-config。xnl! */ private static ComboPooledDataSource dataSource=new ComboPooledDataSource(); /** * 它是事务专用连接 */ private static ThreadLocal<Connection> t1=new ThreadLocal<Connection>(); /** * 使用连接池返回一个连接对象 * @return * @throws SQLException */ public static Connection getConnection() throws SQLException{ //当con!=null,表示已经调用过beginTransaction方法了 Connection con=t1.get(); if(con!=null) return con; return dataSource.getConnection(); } /** * 返回连接池对象 * @return */ public static DataSource getDataSource(){ return dataSource; } /** * 1、开启一个Connection,设置它的setAutoCommit(false) * 2、还要保证dao中使用的连接是我们刚刚创建的 * ------------------------ * 1、创建一个Connection,设置为手动提交 * 2、把这个Connection给dao用 * 3、还要让commitTransaction或rollbackTransaction可以获取到 * @throws SQLException */ public static void beignTransaction() throws SQLException{ Connection con=t1.get(); if(con!=null) throw new SQLException("已经开始了事务,就不要继续开启事务了!"); con=getConnection(); con.setAutoCommit(false); t1.set(con);//把连接保存起来 } /** * 提交事务 * 获取之前开启的Connection,兵提交 * @throws SQLException */ public static void commitTransaction() throws SQLException{ Connection con=t1.get(); if(con==null) throw new SQLException("还没有开启事务,不能提交!"); con.commit(); con.close(); // con=null;//因为前面的close()不会销毁连接而是放回连接池 t1.remove();//从t1中移除连接 } /** * 回滚事务 * 获取之前开启的Connection,兵回滚 * @throws SQLException */ public static void rollbackTransaction() throws SQLException{ Connection con=t1.get(); if(con==null) throw new SQLException("还没有开启事务,不能提交!"); con.rollback(); con.close(); // con=null;//因为前面的close()不会销毁连接而是放回连接池 t1.remove(); } public static void releaseConnection(Connection connection) throws SQLException{ /* *判斷它是不是中事務專用,如果是就不關閉 *如果不是就要關閉 */ //如果con==null,說明沒有事務,那麼connection一定不是事務專用的 Connection con=t1.get(); if(con==null) connection.close(); if(con!=connection) connection.close(); } }
package cn.itcast.cn; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.ResultSetHandler; /** * 这个类中的方法自己来处理连接的问题 * 无需外界传递 * 怎么处理的呢? * 通过JdbcUtils.getConnection()得到连接!有可能是事务连接也有可能是普通连接 * JdbcUtils.releaseConnection()完成连接的释放 * @author Administrator * */ public class TxQueryRunner extends QueryRunner{ @Override public int[] batch(String sql, Object[][] params) throws SQLException { /** * 得到连接 * 执行父类方法 * 释放连接 * 返回值 */ Connection con=JdbcUtils.getConnection(); int[] result=super.batch(con, sql, params); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, Object param, ResultSetHandler<T> rsh) throws SQLException { Connection con=JdbcUtils.getConnection(); T result=super.query(con, sql, param,rsh); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, Object[] params, ResultSetHandler<T> rsh) throws SQLException { Connection con=JdbcUtils.getConnection(); T result=super.query(con,sql, params, rsh); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException { Connection con=JdbcUtils.getConnection(); T result=super.query(con,sql, rsh,params); JdbcUtils.releaseConnection(con); return result; } @Override public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException { Connection con=JdbcUtils.getConnection(); T result=super.query(con,sql, rsh); JdbcUtils.releaseConnection(con); return result; } @Override public int update(String sql) throws SQLException { Connection con=JdbcUtils.getConnection(); int result=super.update(con,sql); JdbcUtils.releaseConnection(con); return result; } @Override public int update(String sql, Object param) throws SQLException { Connection con=JdbcUtils.getConnection(); int result=super.update(con,sql,param); JdbcUtils.releaseConnection(con); return result; } @Override public int update(String sql, Object... params) throws SQLException { Connection con=JdbcUtils.getConnection(); int result=super.update(con,sql,params); JdbcUtils.releaseConnection(con); return result; } }
package cn.itcast.cn; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; public class AccountDao { public static void update(String name,double money) throws SQLException{ // QueryRunner qr=new QueryRunner(); QueryRunner qr=new TxQueryRunner(); String sql="UPDATE account SET balance=balance+? WHERE aname=?"; Object[] params={money,name}; //我们需要自己来提供连接,保证多次调用使用的是同一个连接 // Connection con=JdbcUtils.getConnection(); // qr.update(con, sql, params); // JdbcUtils.releaseConnection(con); qr.update(sql,params); } } package cn.itcast.cn; import java.sql.SQLException; import org.junit.Test; @SuppressWarnings("static-access") public class Demo1 { private AccountDao dao=new AccountDao(); @Test public void serviceMethod() throws Exception{ try{ JdbcUtils.beignTransaction(); dao.update("zs", -1000); if(true) throw new RuntimeException("不好依稀"); dao.update("lisi", +1000); JdbcUtils.commitTransaction(); }catch(Exception e){ try { JdbcUtils.rollbackTransaction(); } catch (SQLException e1) { e1.printStackTrace(); } throw e; } } }