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。

  • 相关阅读:
    CF1051F The Shortest Statement 题解
    CF819B Mister B and PR Shifts 题解
    HDU3686 Traffic Real Time Query System 题解
    HDU 5969 最大的位或 题解
    P3295 萌萌哒 题解
    BZOJ1854 连续攻击游戏 题解
    使用Python编写的对拍程序
    CF796C Bank Hacking 题解
    BZOJ2200 道路与航线 题解
    USACO07NOV Cow Relays G 题解
  • 原文地址:https://www.cnblogs.com/liyihua/p/12324613.html
Copyright © 2011-2022 走看看