Ubtils中事务处理

来源:互联网 发布:什么是软件开发方法 编辑:程序博客网 时间:2024/04/27 19:58

dbutils中处理事务,其连接池需要手动获取和手动关闭,因此需要使用QurryRunner类中的默认构造函数。使用带Connection参数的query和update,或者batch方法来操作sql语句。使用带参数的QurryRunner的构造函数,没执行一条sql语句,都将自动获取连接和产生和关闭preparestament 和resultset。

下面是事物处理的一个小示例:

//向account数据表中插入一条记录,同时修改此条记录,最后删除这条记录,这些sql语句都是在一个事务中处理的,同时在此事务中,提供了两个回滚点。要是此事物出现异常,将回滚到事务起始状态。conn.setAutoCommit(false),设置执行一条语句后不会自动提交,即不让事务自动关闭。此语句相当于开启一个事务。而conn.commit()则是关闭一个事务。

@Testpublic void tranactionTest() {Connection conn = null;try {conn = JdbcC3p0Pools.getConnection2();conn.setAutoCommit(false);QueryRunner qr = new QueryRunner();String sql = "insert into account(name, money) values(?,?)";Object[] param= {"jack", 1000};qr.update(conn, sql, param);Savepoint sp = conn.setSavepoint();sql = "update account set money=money-100 where name=?";Object param2 = "jack";qr.update(conn, sql, param2);Savepoint sp2 = conn.setSavepoint();sql = "delete from account where name=?";qr.update(conn, sql, param2);conn.rollback(sp2);conn.commit();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();try {conn.rollback();} catch (SQLException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}finally{if(conn!=null){try {conn.close();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}
//上面执行结果将是插入一条jack记录,同时将其money修改为900

| 55 | jack    |   900 |

上面的处理显得很粗糙,根据MVC三层架构设计,下面模拟了一个转账事务。

//code

public class AccountDaoImpl {Connection conn = null;public AccountDaoImpl(Connection conn){this.conn = conn;}public void update(Account account) throws SQLException{QueryRunner qr = new QueryRunner();String sql = "update account set name=?, money=? where id=?";Object[] param = {account.getName(), account.getMoney(), account.getId()};qr.update(conn, sql, param);}public Account find(int id) throws SQLException{QueryRunner qr = new QueryRunner();String sql = "select * from account where id=?";Object[] param = {id};return (Account) qr.query(conn, sql, new BeanHandler(Account.class), param);}}

package cn.itcast.service.impl;import java.sql.Connection;import java.sql.SQLException;import cn.itcast.dao.impl.AccountDaoImpl;import cn.itcast.domain.Account;import cn.itcast.service.AccountService;import cn.itcast.utils.JdbcC3p0Pools;public class AccountServiceImpl implements AccountService {private Connection conn = null;/* (non-Javadoc) * @see cn.itcast.service.impl.AccountService#tranfer(int, int, float) */public void tranfer(int sourceId, int toDestId, float money) {try {conn = JdbcC3p0Pools.getConnection2();conn.setAutoCommit(false);AccountDaoImpl dao = new AccountDaoImpl(conn);Account A = dao.find(sourceId);Account B = dao.find(toDestId);A.setMoney(A.getMoney() - money);B.setMoney(B.getMoney() + money);dao.update(A);dao.update(B);conn.commit();} catch (Exception e) {conn.rollback();throw new RuntimeException(e);} finally {JdbcC3p0Pools.close(conn);}}}

package cn.itcast.demo;import cn.itcast.service.AccountService;import cn.itcast.service.impl.AccountServiceImpl;public class AccountTransferDemo {public static void  main(String[] args){int sourceId = 54;int destId = 55;float money = 80;AccountService service = new AccountServiceImpl();service.tranfer(sourceId, destId, money);}}

//转账前| 54 | kebi    |   820 || 55 | jack    |   900 |转账后| 54 | kebi    |   740 || 55 | jack    |   980 |
按照MVC三层架构来设计,降低了各层间的耦合性,但是上面这中实现方法,在实际应用中依然欠妥。在实际应用中,这段转账代码会在并发操作中的多个线程中执行,因此此connection会被多个线程拥有,为了防止对共享变量访问的冲突,可以使用同步操作,使在某一时刻只有一个线程访问connection,待此访问完后,再允许下一个线程对其访问,这就是以时间换空间的做法。另外还有一种做法就是以空间换时间的做法,这种方法可以使用ThreadLocal,这个其实是一个容器。可以多线程共享的变量,此处为connection为每个线程申请一个副本,将这个副本绑定到每个线程的ThreadLocal容器对象上,这样可以将每个线程对此变量的操作进行隔离,避免产生冲突。
在使用ThreadLocal来优化上面的转账代码前,这里先对ThreadLocal的接口方法进行说明:
ThreadLocal类接口很简单,只有4个方法:
void set(Object value)
设置当前线程的线程局部变量的值;
public Object get()
该方法返回当前线程所对应的线程局部变量;
public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度,建议调用此操作,例如:在WEB应用中,当多个客户端都同时访问时,每个客户端都会绑定一个这么一个局部变量,很容易导致服务器内存崩溃;
protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的默认实现直接返回一个null.
ThreadLocal支持泛型操作ThreadLocal<T>,还需要说明一点的是,ThreadLocal的set方法,会将局部变量存储到一个hashMap中去,key值为此线程标识号,value为此局部变量。
//使用ThreadLocal来优化转账操作。

//工具类:

package cn.itcast.utils;import java.sql.Connection;import java.sql.SQLException;import javax.sql.DataSource;import com.mchange.v2.c3p0.ComboPooledDataSource;public class JdbcDbutils {private static ThreadLocal<Connection> localThread = new ThreadLocal();private static DataSource ds = null;private static Connection conn = null;static{ds = new ComboPooledDataSource("mysql");}public static void startTransaction(){conn =  localThread.get();try {if(conn ==null){conn = ds.getConnection();localThread.set(conn);}conn.setAutoCommit(false);}catch (SQLException e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}public static void commit(){conn =  localThread.get();if(conn !=null){try {conn.commit();} catch (SQLException e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}localThread.remove();}}public static void release(){if(conn!=null){try {conn.close();} catch (SQLException e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}public static void rollback(){if(conn!=null){try {conn.rollback();} catch (SQLException e) {// TODO Auto-generated catch blockthrow new RuntimeException(e);}}}public static Connection getConnection(){return localThread.get();}}

//dao类

package cn.itcast.dao.impl;import java.sql.Connection;import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import cn.itcast.dao.AccountDao;import cn.itcast.domain.Account;import cn.itcast.utils.JdbcDbutils;public class AccountDaoImpl implements AccountDao {private Connection conn = null;public AccountDaoImpl(Connection conn){this.conn = conn;}public AccountDaoImpl(){}public void update(Account account) throws SQLException{QueryRunner qr = new QueryRunner();String sql = "update account set name=?, money=? where id=?";Object[] params={account.getName(), account.getMoney(), account.getId()};//qr.update(conn, sql, params);qr.update(JdbcDbutils.getConnection(), sql, params);}public Account find(int id) throws SQLException{QueryRunner qr = new QueryRunner();String sql = "select * from account where id=?";Object[] params={id};return (Account) qr.query(JdbcDbutils.getConnection(), sql, new BeanHandler(Account.class), params);}}

//service层

public void transfer(int sourceId, int toDestId, float money) {try {JdbcDbutils.startTransaction();AccountDao dao = new AccountDaoImpl();Account A;A = dao.find(sourceId);Account B = dao.find(toDestId);A.setMoney(A.getMoney()-money);dao.update(A);B.setMoney(B.getMoney()+money);dao.update(B);JdbcDbutils.commit();} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();JdbcDbutils.rollback();}finally{JdbcDbutils.release();}}
//测试程序

package cn.itcast.demo;import cn.itcast.service.AccountService;import cn.itcast.service.impl.AccountServiceImpl;public class TransferAccountDemo {public static void main(String[] args){int idA = 13;int idB = 12;float money = 80;AccountService service = new AccountServiceImpl();service.threadLocalTransfer(idA, idB, money);}}

0 0
原创粉丝点击