zoukankan      html  css  js  c++  java
  • java操作数据库的事务支持

    一、需求背景:

    我们生活经常遇到一个情况:在购买商品的时候,已经支付的了,那么商品应该处于已购买订单里。而不是付款之后,已购买商品没有。

    还有转账的时候,转出方和转入方都需要扣减相应的金额,而不是一方减少或者增加。

    因为上面的例子都是对数据操作,所以需要我们操作数据库的事务。

    如何确定一个事务范围?

    事务是由一系列数据库操作组成,他和业务场景有关。当操作完成的时候,如果操作过程没有出现失败,则这个事务产生的数据库操作一并提交(commit)。反之,如果出现失败,则一并回滚(rollback)。

    所有事务和业务层(service)确定。

    事务的特点:

    ACID

       1、原子性:事务里面的操作单元不可切割,要么全部成功,要么全部失败
            2、一致性:事务执行前后,业务状态和其他业务状态保持一致.
            3、隔离性:一个事务执行的时候最好不要受到其他事务的影响
            4、持久性:一旦事务提交或者回滚.这个状态都要持久化到数据库中

     不考虑隔离性会出现的读问题:
            脏读:在一个事务中读取到另一个事务没有提交的数据
            不可重复读:在一个事务中,两次查询的结果不一致(针对的update操作)
            虚读(幻读):在一个事务中,两次查询的结果不一致(针对的insert操作)
        通过设置数据库的隔离级别来避免上面的问题(理解)
            read uncommitted      读未提交    上面的三个问题都会出现
            read committed      读已提交    可以避免脏读的发生
            repeatable read        可重复读    可以避免脏读和不可重复读的发生
            serializable        串行化        可以避免所有的问题

    两种方式:

    1)数据库上控制事务:

    默认情况下,mysql数据库中的事务是自动提交。

    1 mysql> show variables like "%autocommit%";
    2 +---------------+-------+
    3 | Variable_name | Value |
    4 +---------------+-------+
    5 | autocommit    | ON    |
    6 +---------------+-------+
    7 1 row in set (0.00 sec

    我们可以在数据库上进行事务的开启和关闭。但是这种方式显然不好。

    2)java代码实现。

    查看connection的方法:

    1:void commit() 提交事务。Makes all changes made since the previous commit/rollback permanent and releases any database locks currently held by this Connection object.

    2:void  rollback()回滚事务.Undoes all changes made in the current transaction and releases any database locks currently held by this Connection object

    3:void setAutocommit(boolean ) false 表示开启事务,true表示事务关闭。Sets this connection's auto-commit mode to the given state.

    注意:事务要作用同一个数据库连接。

    java代码实现有两种方式:

      1、自上到下传递参数:

        通过service传递到dao层的connection的变量,保证整个过程使用的是同一个数据连接。

      2、当前线程绑定事务:

        通过绑定当前线程一个变量(connection),在dao中获取这个connection,保证整个事务的过程中,使用的是同一个连接。

      其中threadLocal方法:ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID)

        该字段在类中要以private  static 来进行修饰。

        1) T  get() 返回当前线程绑定的值。

        2)void  remove() 从当前线程解绑。

        3)void  set(T  value)  绑定当前线程。

    内部实现原理就是map方法对currentThread  进行put 和set  remove。

    jdbc工具类:

     1 package jd.com.tool_jdbc;
     2 
     3 import org.apache.commons.dbcp2.BasicDataSource;
     4 import org.apache.commons.dbcp2.BasicDataSourceFactory;
     5 
     6 
     7 import java.io.InputStream;
     8 import java.sql.Connection;
     9 import java.sql.PreparedStatement;
    10 import java.sql.SQLException;
    11 import java.util.Properties;
    12 
    13 
    14 
    15 public   class ds_tool {
    16     private  static BasicDataSource ds=null;
    17     private static Connection con=null;
    18     private static ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
    19     public   static  BasicDataSource getDs(){
    20         try {
    21             InputStream inp= ds_tool.class.getClassLoader().getResourceAsStream("database.properties");
    22             Properties  pro=new Properties();
    23             pro.load(inp);
    24             ds=new BasicDataSourceFactory().createDataSource(pro);
    25             ds.setInitialSize(4);
    26             ds.setMaxTotal(8);
    27             ds.setMaxIdle(2);
    28             return  ds;
    29         }catch (Exception ex){
    30             throw  new RuntimeException("初始化数据库失败"+ex);
    31         }
    32 
    33     }
    34     //给当前线程绑定变量。
    35     public  static  BasicDataSource setDs(){
    36         try {
    37             if(con==null){
    38                 ds=getDs();
    39                 con=ds.getConnection();
    40                 threadLocal.set(con);
    41             }else{
    42                 threadLocal.set(con);
    43             }
    44             return  ds;
    45         }catch (Exception ex){
    46             ex.printStackTrace();
    47             throw  new RuntimeException("初始化错误"+ex);
    48         }
    49 
    50     }
    51     //开启事务
    52     public static void  openEven(){
    53         try {
    54             setDs();
    55             con=threadLocal.get();
    56             con.setAutoCommit(false);
    57         }catch (Exception ex){
    58             ex.printStackTrace();
    59         }
    60 
    61     }
    62     //提交事务
    63     public static void  commEven(){
    64         try {
    65             con=threadLocal.get();
    66             con.commit();
    67         }catch (Exception ex){
    68             ex.printStackTrace();
    69         }
    70 
    71     }
    72     //回滚事务
    73     public static  void  rollbackEvent(){
    74         try {
    75             con=threadLocal.get();
    76             System.out.println(con);
    77             con.rollback();
    78         }catch (Exception ex){
    79             ex.printStackTrace();
    80         }
    81 
    82     }
    83     public  static  void closedDs(BasicDataSource ds, PreparedStatement pre){
    84         try { 
    85             ds.close();
    86             pre.close();
    87             threadLocal.remove();
    88         }catch (Exception ex){
    89             ex.printStackTrace();
    90         }
    91 
    92     }
    93 
    94 }

    然后在业务层上进行事务的控制。

    3)1、queryRunner()初始化的时候,不带参数,是默认是开启事务。

      在执行的时候,update(connection con  sql  object ...parm)传入connection。需要关闭资源 connection。

      2、如果quryRunner(Datasource ds)的时候,内部代码会帮我们实现资源回收。不需要connection.close()

  • 相关阅读:
    python实用技巧总结(二)
    python实用技巧总结(一)
    windows下tensorflow/objectdetection API(SSD)环境搭建(基于tensorflow1.14和python3.6)
    理解Python函数和方法
    理解迭代器和可迭代对象
    Anaconda安装报错
    Windows下命令行Git无法显示中文问题解决方案
    web服务器/HTTP协议基础
    Unity3D第三人称摄像机
    Ubuntu 18.04 安装 mysql
  • 原文地址:https://www.cnblogs.com/evilliu/p/8665010.html
Copyright © 2011-2022 走看看