sunny123456

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  1796 随笔 :: 22 文章 :: 24 评论 :: 226万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

https://blog.csdn.net/qq_41581154/article/details/100185914

事务:一个数据库的操作就是一个事务。

简单的事务:对一个表进行增删改查。

默认数据库的事务在Dao层,自动开始、自动提交、自动回滚(insert、update、delete)。

当业务复杂的情况下:

添加报销单(需要添加一个报销单信息记录----报销单信息表,多个报销项目的记录----报销单项目表,当报销单信息表添加成功,多个报销单记录也必须添加到另一张表中。如果这其中有一条记录出现了问题,所有 的数据都不应该添加到数据库中)

银行转账(转账表中一个用户的转账记录,进账表中一个用户的进账记录,当一个用户转账成功的同时,另一个用户也必须同时进账,如果两方中的某一方进账失败或者转账失败,则整个的记录都失败作废)

所以我们需要将事务设置为手动提交,就银行转账的情况来说,转账的时候开始启动,确定转账成功之后提交所有数据写入数据库,如果这其中一方出现了问题,那么就回滚,恢复于原始状态(并不是指数据库,而是指数据库缓存)

下面我们用代码来实现这个情况:(两个数据表,必须要用一个Connection,所以在数据库增删改查的时候不能关闭connection,最后在service中统一关闭)

  1:首先我们需要在操作两张表之前提前获取一个Connection,并且引入新的知识点:ThreadLocal,需要新建一个ThreadLocal对象,ThreadLocal里存入一个Connecction,如果是第一次执行,ThreadLocal里没有Connection,这个时候我们需要新建一个Connection,将这个Connection存进ThreadLocal中去。

private static ThreadLocal<Connection>threadLocal=new ThreadLocal<>();
    private DBUtil2(){
 
    }
 
    /**
     * 获取数据库连接
     * @return
     */
    public static  Connection getConnection(){
        Connection conn=threadLocal.get();
        if(conn==null){
            String driver = "com.mysql.jdbc.Driver";
            String url = "jdbc:mysql://127.0.0.1:3306/sxtoa";// ip  port  dbname
            String user = "root";
            String password = "root";
            try {
                //加载驱动
                Class.forName(driver);
                //建立和数据库的连接
                conn = DriverManager.getConnection(url,user,password);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            threadLocal.set(conn);
        }
        return conn;
    }

然后在业务层执行两张表的操作之前,提前获取到这同一个连接,并且设置自动提交的属性为false,如果两张表的数据操作成功了就把这个数据提交了,如果失败了,就会抛出异常,那咱们就在catch里进行事务回滚,一切回到从来没有添加过的状态,数据库缓存恢复成原来的状态即可,如果成功了,两张表的数据就都添加成功

 public void add(Expense expense) {
        //统一的获得连接,全局只让有一个连接,只要有一个失败,就全部回滚
        Connection conn= DBUtil2.getConnection();
        //这里加try catch就是为了能够让异常丢出去,然后来判断是否添加成功
       try {
           conn.setAutoCommit(false);
           //先添加报销数据,这里要如何返回一个expId呢?select @@identity返回最近添加的第几列
           int expId = expenseDao.add(expense);
           //提取报销项目
           List<ExpenseItem> ItemList = expense.getExpenseItemList();
           for (int i = 0; i < ItemList.size(); i++) {
               ExpenseItem item = ItemList.get(i);
               item.setExpId(expId);
                //添加报销项目数据
               expenseItem.add(item);
           }
           conn.commit();
       }catch (Exception e){
           e.printStackTrace();
           try {
                //事务回滚
               conn.rollback();
           } catch (SQLException e1) {
               e1.printStackTrace();
               throw new MybatisException(e1.getMessage());
           }
           throw new MybatisException(e.getMessage());
       }
    }

 

posted on   sunny123456  阅读(345)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示