javaweb银行转账案例(事务处理)JDBC常见知识点(三)

【1.1】如何使用JDBC处理事务?
(1)在数据库操作中,一项事务是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元
(2)只有当事务中的所有操作都正常完成,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被撤销
例如 银行的转账业务 
【1.2】通过 Connection 接口中提供了三个方法来针对 JDBC 处理事物。
其中有三个方法:
setAutoCommit(boolean autoCommit)// 设置自动提交 我们需要将这个方法设置为 false 不自动提交
    
    
  1. //开启事务
  2. public static void startTransaction(){
  3. try {
  4. //获取连接
  5. Connection conn = getConnection();
  6. //开启事务
  7. conn.setAutoCommit(false);
  8. } catch (Exception e) {
  9. // TODO Auto-generated catch block
  10. e.printStackTrace();
  11. }
  12. }
commit() // 提交   当我们的事务完成的时候,提交
    
    
  1. //提交事务
  2. public static void commit(){
  3. try {
  4. //从集合tc中得到一个连接
  5. Connection conn = tc.get();
  6. if(conn != null){
  7. conn.commit();
  8. }
  9. } catch (Exception e) {
  10. // TODO Auto-generated catch block
  11. e.printStackTrace();
  12. }
  13. }
rollback()  // 回滚  当我们的事务出现异常的时候,回滚
    
    
  1. //回滚事务
  2. public static void rollback(){
  3. try {
  4. //从集合tc中得到一个连接
  5. Connection conn = tc.get();
  6. if(conn != null){
  7. conn.rollback();
  8. }
  9. } catch (SQLException e) {
  10. // TODO Auto-generated catch block
  11. e.printStackTrace();
  12. }
  13. }

【1.3】银行转账的业务逻辑
用户 zs 要向 ls 的账户上转200 元钱,该转账业务需要分成两步:
第一步:zs 的账户上需要减少 200 元钱
第二步:ls 的账户上需要增加 200 元钱
这两步操作需要都成功,事务才成功。也就是说,要么都执行成功,要么都没有执行成功。

那么就会出现两种结果,一种是commit() 执行成功,要么就需要执行 rollback () ;

[1.4]介绍ThreadLocaL类

(1)为什么使用ThreadLocaL 类?
Web应用程序, 多用户并发访问 。一个客户端访问时,服务器启动一个线程,比如转账,在这个线程中独立完成这个事务操作,另外一个客户端也来访问时,服务器也启动另外一个线程,在另外一个线程中独立完成转账事 务操作。
要想多线程中,每个事务执行成功,必须保证每个线程有独立的Connection对象。
(2)ThreadLocaL类介绍:
ThreadLocaL 类:在一个线程中记录本地变量。我们可以将一个Connection对象放到这个ThreadLocaL集合中,只要是这个线程中的对象都可以共享这个Connection对象,线程结束后,删除这个Connection对象。这样 保证:一个事务一个连接。
TreadLoacl本质上是一个Map,键是当前线程对象,值可以是我们的当前连接对象。
     
     
  1. //创建一个ThreadLoacl对象,用当前线程作为key
  2. private static ThreadLocal<Connection> tc = new ThreadLocal<Connection>();

代码演示:在使用数据源(DataSource )时,如果是业务处理,使用C3P0 时,不需要拿 数据源对象。

Java Web应用开发的三层架构:
1、表示层(Web层):jsp和servlet
2、业务逻辑层(Service层):XXXService,事务操作是在service层完成的。
3、数据访问层(dao层):XXXDao
按照编写正常代码演示:
bean-->Utils ---> Dao--->service--->Servlet
先看下数据库表: test数据库中 account 表 三个字段  id 主键自动递增,name 姓名 , money 账户的金额 double 类型

 Account.java
       
       
  1. package cn.edu.aynu.rjxy.bean;
  2. public class Account {
  3. private int id;
  4. private String name;
  5. private double money;
  6. public int getId() {
  7. return id;
  8. }
  9. public void setId(int id) {
  10. this.id = id;
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public double getMoney() {
  19. return money;
  20. }
  21. public void setMoney(double money) {
  22. this.money = money;
  23. }
  24. }
JDBCUtils.java
        
        
  1. package cn.edu.aynu.rjxy.utils;
  2. import java.sql.Connection;
  3. import java.sql.SQLException;
  4. import javax.sql.DataSource;
  5. import com.mchange.v2.c3p0.ComboPooledDataSource;
  6. public class JDBCUtils {
  7. //创建一个ThreadLoacl对象,用当前线程作为key
  8. private static ThreadLocal<Connection> tc = new ThreadLocal<Connection>();
  9. //从c3p0-config.xml文件中读取默认配置创建数据库连接池对象
  10. private static DataSource ds = new ComboPooledDataSource();
  11. //得到数据库连接池对象
  12. public static DataSource getDataSource(){
  13. return ds;
  14. }
  15. //从连接池中获取连接
  16. public static Connection getConnection() throws Exception{
  17. //从集合tc中获取一个Connection
  18. Connection conn = tc.get();
  19. if(conn == null){
  20. conn = ds.getConnection();
  21. //将conn存放到集合tc中
  22. tc.set(conn);
  23. System.out.println("首次创建连接:"+conn);
  24. }
  25. return conn;
  26. }
  27. //开启事务
  28. public static void startTransaction(){
  29. try {
  30. //获取连接
  31. Connection conn = getConnection();
  32. //开启事务
  33. conn.setAutoCommit(false);
  34. } catch (Exception e) {
  35. // TODO Auto-generated catch block
  36. e.printStackTrace();
  37. }
  38. }
  39. //提交事务
  40. public static void commit(){
  41. try {
  42. //从集合tc中得到一个连接
  43. Connection conn = tc.get();
  44. if(conn != null){
  45. conn.commit();
  46. }
  47. } catch (Exception e) {
  48. // TODO Auto-generated catch block
  49. e.printStackTrace();
  50. }
  51. }
  52. //回滚事务
  53. public static void rollback(){
  54. try {
  55. //从集合tc中得到一个连接
  56. Connection conn = tc.get();
  57. if(conn != null){
  58. conn.rollback();
  59. }
  60. } catch (SQLException e) {
  61. // TODO Auto-generated catch block
  62. e.printStackTrace();
  63. }
  64. }
  65. //关闭连接释放资源
  66. public static void close(){
  67. //从集合tc中得到一个连接
  68. Connection conn = tc.get();
  69. if(conn != null){
  70. try {
  71. conn.close();
  72. } catch (SQLException e) {
  73. // TODO Auto-generated catch block
  74. e.printStackTrace();
  75. }finally{
  76. //从集合tc中移除连接对象
  77. tc.remove();
  78. conn = null;
  79. }
  80. }
  81. }
  82. }
AccountDao,java
         
         
  1. package cn.edu.aynu.rjxy.dao;
  2. import java.sql.Connection;
  3. import org.apache.commons.dbutils.QueryRunner;
  4. import org.apache.commons.dbutils.handlers.BeanHandler;
  5. import cn.edu.aynu.rjxy.bean.Account;
  6. import cn.edu.aynu.rjxy.utils.JDBCUtils;
  7. public class AccountDao {
  8. //根据账户名查询账户信息
  9. public Account find(String name) throws Exception{
  10. QueryRunner run = new QueryRunner(); // 处理事务,不需要传 数据源对象
  11. Connection conn = JDBCUtils.getConnection();
  12. System.out.println("find()得到连接:"+conn);
  13. String sql = "select * from account where name=?";
  14. Account account = (Account)run.query(conn, sql, new BeanHandler(Account.class), new Object[]{name});
  15. return account;
  16. }
  17. //转账(可能转入也可能转出)
  18. public void update(Account account) throws Exception {
  19. QueryRunner run = new QueryRunner();
  20. Connection conn = JDBCUtils.getConnection();
  21. System.out.println("update()得到连接:"+conn);
  22. String sql = "update account set money=? where name=?";
  23. run.update(conn, sql, new Object[]{account.getMoney(),account.getName()});
  24. }
  25. }
AccountService.java  转账的业务逻辑
          
          
  1. package cn.edu.aynu.rjxy.service;
  2. import cn.edu.aynu.rjxy.bean.Account;
  3. import cn.edu.aynu.rjxy.dao.AccountDao;
  4. import cn.edu.aynu.rjxy.utils.JDBCUtils;
  5. public class AccountService {
  6. //转账事务
  7. public static void transfer(String fromName,String toName,double balance){
  8. try {
  9. //开启事务
  10. JDBCUtils.startTransaction();
  11. AccountDao dao = new AccountDao();
  12. //查询两个账户的金额
  13. Account accountFrom = dao.find(fromName);
  14. Account accountTo = dao.find(toName);
  15. //判断是否可以转账
  16. if(balance<accountFrom.getMoney()){
  17. //可以转账
  18. //设置转出账户的金额
  19. accountFrom.setMoney(accountFrom.getMoney()-balance);
  20. //设置转入账户的金额
  21. accountTo.setMoney(accountTo.getMoney()+balance);
  22. //执行数据库更改操作
  23. dao.update(accountFrom);
  24. dao.update(accountTo);
  25. //提交事务
  26. JDBCUtils.commit();
  27. System.out.println("事务提交成功");
  28. }else{
  29. System.out.println("转出账户金额不足");
  30. }
  31. } catch (Exception e) {
  32. //回滚事务
  33. JDBCUtils.rollback();
  34. System.out.println("事务提交失败");
  35. e.printStackTrace();
  36. }finally{
  37. //释放资源
  38. JDBCUtils.close();
  39. }
  40. }
  41. }
测试转账是否能够完成
           
           
  1. import cn.edu.aynu.rjxy.service.AccountService;
  2. public class TestTransfer {
  3. /**
  4. * @param args
  5. */
  6. public static void main(String[] args) {
  7. //实现从zs账户上转账200到ls账户上
  8. AccountService.transfer("zs", "ls", 200);
  9. }
  10. }
演示事务处理成功情况:
 
 演示错误情况,将 zs 用户名写成 zss
 
 可以看到,事务没有执行成功。

源码:https://github.com/gengzi/files/tree/master/%E9%93%B6%E8%A1%8C%E8%BD%AC%E8%B4%A6%EF%BC%88%E6%A1%88%E4%BE%8B%EF%BC%89
数据库:
https://github.com/gengzi/files/tree/master/%E9%93%B6%E8%A1%8C%E8%BD%AC%E8%B4%A6%EF%BC%88%E6%A1%88%E4%BE%8B%EF%BC%89
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值