tsx81429 发表于 2024-8-1 14:45:33

数据库的事件(超详细)

数据库的事件

一、界说

在 Java 中,事件管理是确保一组利用要么全部乐成要么全部失败的一种机制。事件通常用于数据库利用,以确保数据的一致性和完整性。Java 提供了多种事件管理方式,包括编程式事件管理和声明式事件管理。
理解:MySQL:每一条语句都属于独立事件,默认主动管理提交的。
假如需要把多条语句当成一个整体,那么就需要把多条语句放在一个事件里面
二、事件的特性(ACID)

原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和长期性( Durability )
原子性:事件是数据库的逻辑工作单元,事件中包含的各利用要么都完成,要么都不完成
一致性:事件实行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含乐成事件提交的结果时,就说数据库处于一致性状态。假如数据库系统 运行中发生故障,有些事件尚未完成就被迫中断,这些未完成事件对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不精确的状态,或者说是 不一致的状态。
**隔离性:**一个事件的实行不能别的事件干扰。即一个事件内部的利用及利用的数据对别的并发事件是隔离的,并发实行的各个事件之间不能相互干扰。
长期性:指一个事件一旦提交,它对数据库中的数据的改变就应该是永世性的。接下来的别的利用或故障不应该对实在行结果有任何影响。
三、编程式事件管理

在编程式事件管理中,开辟者显式地控制事件的开始、提交和回滚。这通常通过 JDBC(Java Database Connectivity)来实现。
开启事件:start transaction 提交事件:commit; 回滚事件:rollback
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class TransactionExample {
    public static void main(String[] args) {
      Connection conn = null;
      try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
            conn.setAutoCommit(false);// 关闭自动提交

            // 执行 SQL 语句(需自己给数据库建表)
            conn.createStatement().executeUpdate("INSERT INTO bank (name, money) VALUES ('Alice', 1000)");
            conn.createStatement().executeUpdate("INSERT INTO bank (name, money) VALUES ('Bob', 1500)");

            conn.commit();// 提交事务
      } catch (SQLException e) {
            if (conn != null) {
                try {
                  conn.rollback();// 回滚事务
                } catch (SQLException ex) {
                  ex.printStackTrace();
                }
            }
            e.printStackTrace();
      } finally {
            if (conn != null) {
                try {
                  conn.close();
                } catch (SQLException e) {
                  e.printStackTrace();
                }
            }
      }
    }
}

四、事件的隔离级别

可以通过 @Transactional 注解的 isolation 属性设置事件的隔离级别,如 READ_COMMITTED、REPEATABLE_READ 等。
1、事件里可能出现的征象:
脏读:一个线程中的事件读到了另外一个线程中未提交的数据。
**不可重复读:**一个线程中的事件读到了另外一个线程中已经提交的update的数据。
虚读:一个线程中的事件读到了另外一个线程中已经提交的insert的数据。
2、4种隔离级别:
   READ UNCOMMITTED(读未提交): 脏读、不可重复读、虚读有可能发生。
READ COMMITTED(读已提交): 制止脏读的发生,不可重复读、虚读有可能发生。
REPEATABLE READ(可重复读): 制止脏读、不可重复读的发生,虚读有可能发生。
SERIALIZABLE(串行化): 制止脏读、不可重复读、虚读的发生。
3、MySQL和ORACLE默认的隔离级别
MySQL:默认REPEATABLE READ
ORACLE:默认READ COMMITTED
4、SQL命令:查察当前的隔离级别
   ​ SELECT @@SESSION.transaction_isolation;
​ 设置当前的事件隔离级别
   ​ set transaction isolation level READ UNCOMMITTED
图片理解:
https://i-blog.csdnimg.cn/direct/8735f688886d4f37959143f5a732824d.png#pic_center
五、通过JDBC利用事件

1、需求:
需求:模拟银行用户转账
需求一:实现取钱利用。
需求二:实现存钱利用。
需求三:实现转账利用,要考虑多对用户,每对用户之间两两存取钱。
需求三的思想:每对用户实现两两存取钱,则这一对就共享同一个connection,但与其他对之间的connection是不一样的,相当于多线程之间的局部变量是不一样的,也就需要用到共享局部变量的思想,ThreadLocal来办理需求。
2、代码实现:
package com.qf.jdbc01;

import com.utils.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Test02 {
    public static void main(String[] args) {
      //传参
      try {
            saveMoney(1,200);
      } catch (SQLException e) {
            throw new RuntimeException(e);
      }
      try {
            withdrawMoney(2,300);
      } catch (SQLException e) {
            throw new RuntimeException(e);
      }
    }
    //场景一:取钱
   public static void withdrawMoney(int id,float money) throws SQLException {
      Connection connection = null;
      PreparedStatement statement = null;
       try {
         connection = DBUtil.getConnection();
         System.out.println(connection);
         String sql = "update bank set money = money-? where id = ?";
         statement = connection.prepareStatement(sql);
         statement.setFloat(1,money);
         statement.setInt(2,id);
         statement.executeUpdate();
       } finally {
         DBUtil.close(connection,statement,null);
       }
   }
   //场景二、存钱
    public static void saveMoney(int id,float money) throws SQLException {
      Connection connection = null;
      PreparedStatement statement = null;
      try {
            connection = DBUtil.getConnection();
            System.out.println(connection);
            String sql = "update bank set money=money+? where id=?";
            statement = connection.prepareStatement(sql);
            statement.setFloat(1,money);
            statement.setInt(2,id);
            statement.executeUpdate();

      } finally {
            DBUtil.close(connection,statement,null);
      }

    }
    //场景三:转账
//      try {
//
//            DBUtil.startTransaction();
//
//            withdrawMoney(1,200);
//
//            //System.out.println(10/0);
//
//            saveMoney(2,200);
//
//            DBUtil.commit();
//
//      } catch (Exception e) {
//            try {
//                DBUtil.rollback();
//            } catch (SQLException ex) {
//                throw new RuntimeException(ex);
//            }
//}
//   }

}


DButil代码实现:
package com.utils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
* 数据库工具类
*/
public class DBUtil {

    private static String url;
    private static String username;
    private static String password;

    private static final ThreadLocal<Object> local;//使用ThreadLocal处理线程安全的问题

    static{
      Properties properties = new Properties();
      try {
            properties.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
      } catch (IOException e) {
            throw new RuntimeException(e);
      }
      String driverName = properties.getProperty("driverName");
      url = properties.getProperty("url");
      username = properties.getProperty("username");
      password = properties.getProperty("password");

      try {
            Class.forName(driverName);
      } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
      }
      local = new ThreadLocal<>();
    }

    /**
   * 获取连接对象
   */
    public static Connection getConnection() throws SQLException {
      Connection connection = DriverManager.getConnection(url,username,password);
      if (connection == null){
            connection = DriverManager.getConnection(url,username,password);
            local.set(connection);//将Connection对象添加到local中
      }
      return connection;
    }

    /**
   * 关闭资源
   */
    public static void close(Connection connection, Statement statement, ResultSet resultSet){
      if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
      }
      if(statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
      }
      if(connection != null){
            try {
                if (connection.getAutoCommit()){
                  connection.close();
                  if (connection == null) {
                        connection.close();
                        local.set(null);
                  }
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
      }
    }
    /**
   * 开启事务
   */
    public static void startTransaction() throws SQLException {
      Connection connection = getConnection();
      connection.setAutoCommit(false);
    }
    /**
   * 提交事务
   */
    public static void commit() throws SQLException {
      Connection connection = (Connection) local.get();
      if(connection != null){
            connection.commit();
            local.set(null);
      }
    }
    /**
   * 事务回滚
   */
    public static void rollback() throws SQLException {
      Connection connection = (Connection) local.get();
      if (connection != null){
            connection.rollback();
            local.set(null);
      }
    }

    /**
   * 更新数据(添加、删除、修改)
   */
    public static int commonUpdate(String sql,Object... params) throws SQLException {
      Connection connection = null;
      PreparedStatement statement = null;
      try {
            connection = getConnection();
            statement = connection.prepareStatement(sql);
            paramHandler(statement,params);
            int num = statement.executeUpdate();
            return num;
      }finally {
            close(connection,statement,null);
      }
    }

    /**
   * 添加数据 - 主键回填(主键是int类型可以返回)
   */
    public static int commonInsert(String sql,Object... params) throws SQLException {
      Connection connection = null;
      PreparedStatement statement = null;
      ResultSet resultSet = null;
      try {
            connection = getConnection();
            statement = connection.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
            paramHandler(statement,params);
            statement.executeUpdate();

            resultSet = statement.getGeneratedKeys();
            int primaryKey = 0;
            if(resultSet.next()){
                primaryKey = resultSet.getInt(1);
            }
            return primaryKey;
      }finally {
            close(connection,statement,resultSet);
      }
    }

    /**
   * 查询多个数据
   */
    public static <T> List<T> commonQueryList(Class<T> clazz,String sql, Object... params) throws SQLException, InstantiationException, IllegalAccessException {

      Connection connection = null;
      PreparedStatement statement = null;
      ResultSet resultSet = null;

      try {
            connection = getConnection();
            statement = connection.prepareStatement(sql);
            paramHandler(statement,params);
            resultSet = statement.executeQuery();

            //获取表数据对象
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取字段个数
            int count = metaData.getColumnCount();

            List<T> list = new ArrayList<>();

            while(resultSet.next()){

                T t = clazz.newInstance();

                //获取字段名及数据
                for (int i = 1; i <= count; i++) {
                  String fieldName = metaData.getColumnName(i);
                  Object fieldVal = resultSet.getObject(fieldName);
                  setField(t,fieldName,fieldVal);
                }
                list.add(t);
            }
            return list;
      } finally {
            DBUtil.close(connection,statement,resultSet);
      }
    }

    /**
   * 查询单个数据
   */
    public static <T> T commonQueryObj(Class<T> clazz,String sql, Object... params) throws SQLException, InstantiationException, IllegalAccessException {

      Connection connection = null;
      PreparedStatement statement = null;
      ResultSet resultSet = null;

      try {
            connection = getConnection();
            statement = connection.prepareStatement(sql);
            paramHandler(statement,params);
            resultSet = statement.executeQuery();

            //获取表数据对象
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取字段个数
            int count = metaData.getColumnCount();

            if(resultSet.next()){

                T t = clazz.newInstance();

                //获取字段名及数据
                for (int i = 1; i <= count; i++) {
                  String fieldName = metaData.getColumnName(i);
                  Object fieldVal = resultSet.getObject(fieldName);
                  setField(t,fieldName,fieldVal);
                }
                return t;
            }
      } finally {
            DBUtil.close(connection,statement,resultSet);
      }
      return null;
    }

    /**
   * 处理statement对象参数数据的处理器
   */
    private static void paramHandler(PreparedStatement statement,Object... params) throws SQLException {
      for (int i = 0; i < params.length; i++) {
            statement.setObject(i+1,params);
      }
    }

    /**
   * 获取当前类及其父类的属性对象
   * @param clazz class对象
   * @param name 属性名
   * @return 属性对象
   */
    private static Field getField(Class<?> clazz,String name){

      for(Class<?> c = clazz;c != null;c = c.getSuperclass()){
            try {
                Field field = c.getDeclaredField(name);
                return field;
            } catch (NoSuchFieldException e) {
            } catch (SecurityException e) {
            }
      }
      return null;
    }

    /**
   * 设置对象中的属性
   * @param obj 对象
   * @param name 属性名
   * @param value 属性值
   */
    private static void setField(Object obj,String name,Object value){

      Field field = getField(obj.getClass(), name);
      if(field != null){
            field.setAccessible(true);
            try {
                field.set(obj, value);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
      }
    }
}


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 数据库的事件(超详细)