zoukankan      html  css  js  c++  java
  • JDBC 控制事务(MySQL为例)

    事务

    一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

     

    对事务的操作

    1. 开启事务

    2. 提交事务

    3. 回滚事务

    使用Connection对象来管理事务

    java.sql.Connection接口是一个数据库连接对象。它与特定数据库的连接(会话)。 执行SQL语句并在连接的上下文中返回结果。

    • 开启事务
      setAutoCommit(boolean autoCommit)
      // 调用该方法设置参数为false,即开启事务
    • 提交事务
      commit()
      // 当所有sql都执行完提交事务
    • 回滚事务
      rollback()
      // 在catch中回滚事务

    Java代码举例

    有如下一个MySQL数据表,利用Java程序:把id = 1对应的余额减少500,id = 2对应的余额增加500

    CREATE TABLE account (
        id INT PRIMARY KEY AUTO_INCREMENT,   -- id
        NAME VARCHAR(10),                    -- 名字
        balance DOUBLE                       -- 余额
    );
    
    INSERT INTO account (NAME, balance) VALUES ('LeeHua', 1000), ('Tom', 1000);

    自定义一个注解,获取连接数据库的信息:

    package my.view.util;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE) // 注解能作用于类上
    @Retention(RetentionPolicy.RUNTIME) // 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
    public @interface PropertiesAnnotation {
    
        /* URL */
        public abstract String url();
    
        /* 用户 */
        public abstract String user();
    
        /* 密码 */
        public abstract String password();
    
        /* 驱动包 */
        public abstract String driver();
    }

    定义一个工具类,用来注册驱动和获取数据库连接对象:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    @PropertiesAnnotation(
            url = "jdbc:mysql:///Study", user = "账号", password = "密码", driver = "com.mysql.jdbc.Driver"
    )
    public class JdbcUtils02 {
    
        private static String url;
        private static String user;
        private static String password;
        private static String driver;
    
        /* 文件的读取,只需要读取一次即可拿到这些值。利用反射和注解、使用静态代码块 */
        static{
            // 读取资源文件,获取值。
    
            try {
                // 1. 解析注解
                // 1.1 获取JdbcUtils02类的字节码文件对象
                Class<JdbcUtils02> jdbcUtils02Class = JdbcUtils02.class;
    
                // 2. 获取上边的注解对象
                PropertiesAnnotation annotation = jdbcUtils02Class.getAnnotation(PropertiesAnnotation.class);
    
                // 3. 调用注解中定义的抽象方法,获取返回值,赋值给静态成员变量
                url = annotation.url();
                user = annotation.user();
                password = annotation.password();
                driver = annotation.driver();
    
                // 4. 注册驱动
                Class.forName(driver);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    
        /**
         * 获取连接
         * @return 连接对象
         */
        public static Connection getConnection() throws SQLException {
            return DriverManager.getConnection(url, user, password);
        }
    
    }

    有了以上条件,对数据表进行操作:

    1. 获取数据库连接
      Connection connection = JdbcUtils02.getConnection();
    2. 获取到数据库连接对象后,开启事务
      connection.setAutoCommit(false);
    3. 开启事务实际上是创建一个日志文件,将定义的SQL语句的执行结果暂时放到日记中,如果没有错,提交事务,如果出错,那么就回滚事务。接下来定义动态SQL语句
      String sql1 = "update account set balance = balance - ? where id = ?";
      String sql2
      = "update account set balance = balance + ? where id = ?";
    4. 定义好了SQL语句,接下来就要获取执行动态SQL语句的对象
      PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
      
      PreparedStatement preparedStatement2 = connection.prepareStatement(sql2);
    5. 给动态SQL语句传入参数
      // LeeHua 的账户余额减少500元
      preparedStatement1.setDouble(1,500);
      preparedStatement1.setInt(2,1);
      
      // Tom 的账户余额增加500元
      preparedStatement2.setDouble(1,500);
      preparedStatement2.setInt(2,2);
    6. 一切准备就绪,这时候就可以执行SQL语句了
      preparedStatement1.executeUpdate();
      preparedStatement2.executeUpdate();
    7. 执行SQL语句后,如果没有错误,那么就提交事务,这时候的表记录就会更改
      connection.commit();
    8. 执行SQL语句后,如果有错误,那么就回滚事务,这个时候,会把日记中的记录删除,不会提交到表中,表的记录不会更改
      connection.rollback();
    9. 无论执行SQL语句,是否存在错误,最后都需要释放资源,调用自定义releaseResources()方法,释放资源
      releaseResources(preparedStatement2);
      releaseResources(preparedStatement1);
      releaseResources(connection);
    10. releaseResources()方法的定义如下:
      /**
       * 释放资源
       * @param t 要被释放的资源
       * @param <T> 要被释放的资源对象的类型
       */
      public static <T> void releaseResources (T t){
          if(t != null){
              try {
                  // 利用反射,获取class对象
                  Class<?> aClass = t.getClass();
                  // 获取class对象中的方法对象
                  Method close = aClass.getMethod("close");
                  // 执行方法
                  close.invoke(t);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }

    对数据表进行操作的实现代码如下:

    package my.view.jdbc;
    
    import my.view.util.JdbcUtils02;
    
    import java.lang.reflect.Method;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    public class JdbcDemo09 {
    
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement1 = null;
            PreparedStatement preparedStatement2 = null;
    
            try {
                // 1. 获取连接
                connection = JdbcUtils02.getConnection();
    
                // 开启事务
                connection.setAutoCommit(false);
    
                // 2. 定义sql
                // 2.1 定义减少账户余额的SQL语句
                String sql1 = "update account set balance = balance - ? where id = ?";
                // 2.2 定义增加账户余额的SQL语句
                String sql2 = "update account set balance = balance + ? where id = ?";
    
                // 3.获取执行SQL语句的对象
                preparedStatement1 = connection.prepareStatement(sql1);
                preparedStatement2 = connection.prepareStatement(sql2);
    
                // 4. 设置参数
                // 4.1 LeeHua 的账户余额减少500元
                preparedStatement1.setDouble(1,500);
                preparedStatement1.setInt(2,1);
                // 4.2 Tom 的账户余额增加500元
                preparedStatement2.setDouble(1,500);
                preparedStatement2.setInt(2,2);
    
                // 5. 执行SQL语句
                preparedStatement1.executeUpdate();
                preparedStatement2.executeUpdate();
    
                // 6. 提交事务
                connection.commit();
            } catch (Exception e) {
                // 7. 事务回滚
                try {
                    if(connection != null) {
                        connection.rollback();
                    }
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            } finally {
                // 8. 释放资源
                releaseResources(preparedStatement2);
                releaseResources(preparedStatement1);
                releaseResources(connection);
            }
    
        }
    
        /**
         * 释放资源
         * @param t 要被释放的资源
         * @param <T> 要被释放的资源对象的类型
         */
        public static <T> void releaseResources (T t){
            if(t != null){
                try {
                    // 利用反射,获取class对象
                    Class<?> aClass = t.getClass();
                    // 获取class对象中的方法对象
                    Method close = aClass.getMethod("close");
                    // 执行方法
                    close.invoke(t);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    }

    运行程序,查看表中的记录,发现LeeHua的账号余额减少了500,Tom的账户余额增加了500。

  • 相关阅读:
    CVE-2019-16278:Nostromo Web服务器的远程命令执行
    内网渗透一(信息收集)
    Apache Flink 任意jar包上传漏洞
    Apache ---- Solrl漏洞复现
    linux内核过高导致vm打开出错修复脚本
    lvm拓展
    文件时间进度扫描控制,可回溯,空闲扫描,系统时间调整不影响
    Raid 管理
    curl 工具使用
    docker 入门
  • 原文地址:https://www.cnblogs.com/liyihua/p/12324613.html
Copyright © 2011-2022 走看看