ThreadLocal与DUBtils实现手动事务

400 阅读4分钟
  • 在学习之前要想一个问题:我学这个用来干嘛?不然学了没有用!
  • 大家应该都知道,对于数据库来说,一个连接对应于一个事务,一个事务可以包含多个DML操作(增删改查)
  • 举一个转账的例子,张三给李四转账1000元,就相当于张三减1000李四加1000,想要转账成功肯定是这二个操作同时成功,不可能是张三减了1000,李四账户上没有收到1000吧!
  • 用三层结构来解释就是service的操作都是来自于dao层的操作,而dao层就是二个操作:张三减1000,李四加1000,而这二个操作会产生二个连接对象connection,上面有说过一个连接相当于一个事务,而在二个连接就不是同一个事务了就不能实现同时张三减1000,李四加1000的操作,所以这个时候我们要应用ThreadLocal
  • 简单介绍一下ThreadLocal,它可以为每个线程复制一个副本。用上面的例子来说:当代码访问数据源的时候会产生一个connection连接,当张三减1000的时候,并没有提交事务。所以当李四加1000的时候代码也会去访问数据源,可数据源的连接已经被张三加1000给用了,所以这个时候ThreadLocal就会复制一个副本连接出来给李四来加1000,就实现了一个连接对应多个事务

接下来废话不多说,直接看代码

数据库的账号表:

cardid是账户卡号; name是账户户主;balance是账号余额

实体类:

 public class Account {
    private int cardid;
    private String name;
    private int balance;

    public int getCardid() {
        return cardid;
    }
    public void setCardid(int cardid) {
        this.cardid = cardid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getBalance() {
        return balance;
    }
    public void setBalance(int balance) {
        this.balance = balance;
    }
}

工具类:c3p0连接池

public static DataSource getDataSoure(){
                //传入的参数是配置文件中的name的值
                ComboPooledDataSource c3p0 = new ComboPooledDataSource("haiyang");
                return c3p0;
            }

c3p0连接池xml配置

  <c3p0-config>
    <!--默认配置-->
    <default-config>
        <property name="user">simple</property>
        <property name="password">123456</property>
        <property name="driverClass">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
        <property name="jdbcUrl">jdbc:sqlserver://localhost:1433;databaseName=Student</property>
    </default-config>
    <!--配置连接池-->
    <named-config name="haiyang">
        <property name="user">simple</property>
        <property name="password">123456</property>
        <property name="driverClass">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
        <property name="jdbcUrl">jdbc:sqlserver://localhost:1433;databaseName=User</property>
    </named-config>
</c3p0-config>

工具类:辅助事务

public class JDBCUtil {
      private static  ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
      //获取链接
      public static Connection getConnection() throws SQLException {
                  Connection conn = threadLocal.get();//第一次去数据源中获取链接
                  if (conn == null){//如果没有链接
                        conn = c3p0.getDataSoure().getConnection();//从连接池中获取数据源从而获取链接
                        threadLocal.set(conn);//把链接放进数据源中
                  }
                  return conn;
      }
      //开启事务
      public static  void beginThreadLocal() throws SQLException {
            Connection conn = getConnection();
                  conn.setAutoCommit(false);//设为手动提交,开启事务
      }
      //事务成功
      public static  void CommitThreadLocal() throws SQLException {
            Connection conn = getConnection();
            if (conn != null)//所有的对象.方法全部都要排空
                  conn.commit();
      }
      //事务失败,回滚事务
      public static  void rollbackThreadLocal() throws SQLException {
            Connection conn = getConnection();
            if (conn != null)
                  conn.rollback();
      }
      //关闭
      public static  void  close() throws SQLException{
            Connection conn = getConnection();
            if (conn != null)
                  conn.close();
            threadLocal.remove();
      }
}

Dao层接口

public interface AccountDao {
    //根据卡号查询账户
    Account QueryByid(int cardid) throws Exception;
    //修改账户
    void updateAccount(Account account) throws Exception;
}

Dao层实现类

 public class AccountDaoimpl implements AccountDao {
    @Override
    public Account QueryByid(int cardid) throws Exception{
        Connection conn = JDBCUtil.getConnection();
        QueryRunner runner = new QueryRunner();//手动提交事务,无参
        Account account = runner.query(conn, "select * from account where cardid = ?", new BeanHandler<Account>(Account.class), cardid);
        return account;
    }

    @Override
    public void updateAccount(Account account) throws Exception{
        Connection conn = JDBCUtil.getConnection();
        QueryRunner runner = new QueryRunner();
        runner.update(conn,"update account set balance = ? where cardid = ?",new Object[]{account.getBalance(),account.getCardid()});
    }
}

service接口

//业务:转账
public interface AccountService {
    //第一个参数:付款人,第二个参数:收款人,第三个参数:转账金额
    void transfer(int formcarid,int tocarid,int money);
}

service实现类

 public class AccountServiceimpl implements AccountService {
    @Override
    public void transfer(int formcarid, int tocarid, int money) {
        AccountDaoimpl accountDao = new AccountDaoimpl();
        try {
            //开启事务
            JDBCUtil.beginThreadLocal();
            //DML操作
            //先根据卡号查询账户
            Account formname = accountDao.QueryByid(formcarid);//付款方
            Account toname =  accountDao.QueryByid(tocarid);//收款方

            if(formname.getBalance() > money){//判断付款方是否有足够的余额
                //转账操作
                //付款方
                int formbalance = formname.getBalance() - money;
                formname.setBalance(formbalance);
                formname.setCardid(formcarid);
                accountDao.updateAccount(formname);
                //收款方
                int tobalance = toname.getBalance() + money;
                toname.setBalance(tobalance);
                toname.setCardid(tocarid);
                accountDao.updateAccount(toname);
                //事务成功
                System.out.println("转账成功!");
                JDBCUtil.CommitThreadLocal();
            }else {
                System.out.println("余额不足,转账失败!");
            }
        } catch (Exception e) {
            try {
                JDBCUtil.rollbackThreadLocal();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
            System.out.println("转账失败!");
        }finally {
            try {
                JDBCUtil.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试类

public class test {
    public static void main(String[] args) {
        AccountServiceimpl accountService = new AccountServiceimpl();
        accountService.transfer(1111,2222,2000);
    }
}

一开始我设置张三8000元,李四8000元

运行结果

再次查询数据库

张三成功给李四转账2000元

如果有错误,欢迎指正