zoukankan      html  css  js  c++  java
  • 实战项目中 :一个业务对多个数据库操作的同步的处理办法(要么都成功,要么都失败)Threadlocal 数据库事务

    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中去。

    1. private static ThreadLocal<Connection>threadLocal=new ThreadLocal<>();
    2. private DBUtil2(){
    3. }
    4. /**
    5. * 获取数据库连接
    6. * @return
    7. */
    8. public static Connection getConnection(){
    9. Connection conn=threadLocal.get();
    10. if(conn==null){
    11. String driver = "com.mysql.jdbc.Driver";
    12. String url = "jdbc:mysql://127.0.0.1:3306/sxtoa";// ip port dbname
    13. String user = "root";
    14. String password = "root";
    15. try {
    16. //加载驱动
    17. Class.forName(driver);
    18. //建立和数据库的连接
    19. conn = DriverManager.getConnection(url,user,password);
    20. } catch (ClassNotFoundException e) {
    21. e.printStackTrace();
    22. } catch (SQLException e) {
    23. e.printStackTrace();
    24. }
    25. threadLocal.set(conn);
    26. }
    27. return conn;
    28. }

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

    1. public void add(Expense expense) {
    2. //统一的获得连接,全局只让有一个连接,只要有一个失败,就全部回滚
    3. Connection conn= DBUtil2.getConnection();
    4. //这里加try catch就是为了能够让异常丢出去,然后来判断是否添加成功
    5. try {
    6. conn.setAutoCommit(false);
    7. //先添加报销数据,这里要如何返回一个expId呢?select @@identity返回最近添加的第几列
    8. int expId = expenseDao.add(expense);
    9. //提取报销项目
    10. List<ExpenseItem> ItemList = expense.getExpenseItemList();
    11. for (int i = 0; i < ItemList.size(); i++) {
    12. ExpenseItem item = ItemList.get(i);
    13. item.setExpId(expId);
    14. //添加报销项目数据
    15. expenseItem.add(item);
    16. }
    17. conn.commit();
    18. }catch (Exception e){
    19. e.printStackTrace();
    20. try {
    21. //事务回滚
    22. conn.rollback();
    23. } catch (SQLException e1) {
    24. e1.printStackTrace();
    25. throw new MybatisException(e1.getMessage());
    26. }
    27. throw new MybatisException(e.getMessage());
    28. }
    29. }
  • 相关阅读:
    MySQL 误操作后数据恢复(update,delete忘加where条件)
    数据预处理流程
    pandas读取大量数据的分块处理
    go语言 nil一些注意的地方
    C语言编程优化运行速度
    go语言常用内置函数
    RSA加密算法
    一致性哈希算法原理
    数据服务的分布式模型
    leetcode链表题
  • 原文地址:https://www.cnblogs.com/sunny3158/p/14629631.html
Copyright © 2011-2022 走看看