Skip to content

三、全新JDBC扩展提升

1、自增长主键回显实现

  1. java程序获取插入数据时mysql维护自增长维护的主键id值,这就是主键回显
  2. 作用: 在多表关联插入数据时,一般主表的主键都是自动生成的,所以在插入数据之前无法知道这条数据的主键,但是从表需要在插入数据之前就绑定主表的主键,这时可以使用主键回显技术:
java
/**
     * 返回插入的主键!
     * 主键:数据库帮助维护的自增长的整数主键!
     *
     * @throws Exception
     */
@Test
public void returnPrimaryKey() throws Exception {

    //1.注册驱动
    Class.forName("com.mysql.cj.jdbc.Driver");
    //2.获取连接
    Connection connection = DriverManager.getConnection("jdbc:mysql:///xx_jdbc?user=root&password=mac_root");
    //3.编写SQL语句结构
    String sql = "insert into t_user (account,password,nickname) values (?,?,?);";
    //4.创建预编译的statement,传入SQL语句结构
    /**
         * TODO: 第二个参数填入 1 | Statement.RETURN_GENERATED_KEYS
         *       告诉statement携带回数据库生成的主键!
         */
    PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
    //5.占位符赋值
    statement.setObject(1, "tomcat");
    statement.setObject(2, "123456");
    statement.setObject(3, "汤姆猫");
    //6.执行SQL语句 【注意:不需要传入SQL语句】 DML
    int i = statement.executeUpdate();
    //7.结果集解析
    System.out.println("i = " + i);

    //一行一列的数据!里面就装主键值!
    ResultSet resultSet = statement.getGeneratedKeys();
    resultSet.next();
    int anInt = resultSet.getInt(1);
    System.out.println("pk = " + anInt);


    //8.释放资源
    statement.close();
    connection.close();
}

2、批量数据插入性能提升

  • 功能需求
    1. 批量数据插入优化
    2. 提升大量数据插入效率
  • 功能实现
Java
/**
     * 批量细节:
     * 1.url?rewriteBatchedStatements=true
     * 2.insert 语句必须使用 values
     * 3.语句后面不能添加分号;
     * 4.语句不能直接执行,每次需要装货  addBatch() 最后 executeBatch();
     * <p>
     * 批量插入优化!
     *
     * @throws Exception
     */
@Test
public void batchInsertYH() throws Exception {

    //1.注册驱动
    Class.forName("com.mysql.cj.jdbc.Driver");
    //2.获取连接
    Connection connection = DriverManager.getConnection("jdbc:mysql:///xx_jdbc?rewriteBatchedStatements=true",
                                                        "root", "mac_root");
    //3.编写SQL语句结构
    String sql = "insert into t_user (account,password,nickname) values (?,?,?)";
    //4.创建预编译的statement,传入SQL语句结构
    /**
         * TODO: 第二个参数填入 1 | Statement.RETURN_GENERATED_KEYS
         *       告诉statement携带回数据库生成的主键!
         */
    long start = System.currentTimeMillis();
    PreparedStatement statement = connection.prepareStatement(sql);
    for (int i = 0; i < 10000; i++) {

        //5.占位符赋值
        statement.setObject(1, "ergouzi" + i);
        statement.setObject(2, "lvdandan");
        statement.setObject(3, "驴蛋蛋" + i);
        //6.装车
        statement.addBatch();
    }

    //发车! 批量操作!
    statement.executeBatch();

    long end = System.currentTimeMillis();

    System.out.println("消耗时间:" + (end - start));

    //8.释放资源
    connection.close();
}

3、 jdbc中数据库事务实现

1、事务概念

java
// 事务概念
   数据库事务就是一种SQL语句执行的缓存机制,不会单条执行完毕就更新数据库数据,最终根据缓
   存内的多条语句执行结果统一判定!
   一个事务内所有语句都成功及事务成功,我们可以触发commit提交事务来结束事务,更新数据!
   一个事务内任意一条语句失败,及事务失败,我们可以触发rollback回滚结束事务,
   数据回到事务之前状态!
   
   举个例子: 
           临近高考,你好吃懒做,偶尔还瞎花钱,父母也只会说'你等着!',待到高考完毕!
           成绩600+,翻篇,庆祝!
           成绩200+,翻旧账,男女混合双打!
           
//优势
   允许我们在失败情况下,数据回归到业务之前的状态! 
   
//场景
   一个业务涉及多条修改数据库语句!
   例如: 经典的转账案例,转账业务(加钱和减钱)   
         批量删除(涉及多个删除)
         批量添加(涉及多个插入)     
         
// 事务特性
  1. 原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 

  2. 一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

  3. 隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

  4. 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

// 事务类型
  
  自动提交 : 每条语句自动存储一个事务中,执行成功自动提交,执行失败自动回滚! (MySQL)
  手动提交:  手动开启事务,添加语句,手动提交或者手动回滚即可!
  
// sql开启事务方式
   针对自动提交: 关闭自动提交即可,多条语句添加以后,最终手动提交或者回滚! (推荐)
     
      SET autocommit = off; //关闭当前连接自动事务提交方式
      # 只有当前连接有效
      # 编写SQL语句即可
      SQL
      SQL
      SQL
      #手动提交或者回滚 【结束当前的事务】
      COMMIT / ROLLBACK ;  
     
   手动开启事务: 开启事务代码,添加SQL语句,事务提交或者事务回滚! (不推荐)

// 呼应jdbc技术
 
  try{
    connection.setAutoCommit(false); //关闭自动提交了
    
    //注意,只要当前connection对象,进行数据库操作,都不会自动提交事务
    //数据库动作!
    //statement - 单一的数据库动作 c u r d 
    
    connection.commit();
  }catch(Execption e){
    connection.rollback();
  }

2、建表

mysql
-- 继续在 xx_jdbc 的库中创建银行表
CREATE TABLE t_bank(
   id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账号主键',
   account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',
   money  INT UNSIGNED COMMENT '金额,不能为负值') ;

INSERT INTO t_bank(account,money) VALUES
  ('ergouzi',1000),('lvdandan',1000);

3、BankDao

java
package com.xx;

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

/**
 * @Author: xueqimiao
 * @Date: 2023/1/4 09:51
 */
public class BankDao {

    /**
     * 加钱方法
     *
     * @param account
     * @param money
     * @param connection 业务传递的connection和减钱是同一个! 才可以在一个事务中!
     * @return 影响行数
     */
    public int addMoney(String account, int money, Connection connection) throws ClassNotFoundException, SQLException {


        String sql = "update t_bank set money = money + ? where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setObject(1, money);
        preparedStatement.setString(2, account);

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println("加钱执行完毕!");

        //关闭资源close
        preparedStatement.close();

        return rows;
    }

    /**
     * 减钱方法
     *
     * @param account
     * @param money
     * @param connection 业务传递的connection和加钱是同一个! 才可以在一个事务中!
     * @return 影响行数
     */
    public int subMoney(String account, int money, Connection connection) throws ClassNotFoundException, SQLException {

        String sql = "update t_bank set money = money - ? where account = ? ;";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        preparedStatement.setObject(1, money);
        preparedStatement.setString(2, account);

        //发送SQL语句
        int rows = preparedStatement.executeUpdate();

        //输出结果
        System.out.println("减钱执行完毕!");

        //关闭资源close
        preparedStatement.close();

        return rows;
    }
}

4、BankService

java
package com.xx;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @Author: xueqimiao
 * @Date: 2023/1/4 09:49
 */
public class BankService {

    /**
     * 转账业务方法
     *
     * @param addAccount 加钱账号
     * @param subAccount 减钱账号
     * @param money      金额
     */
    public void transfer(String addAccount, String subAccount, int money) throws ClassNotFoundException, SQLException {

        System.out.println("addAccount = " + addAccount + ", subAccount = " + subAccount + ", money = " + money);

        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        //获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///xx_jdbc", "root", "mac_root");

        int flag = 0;

        //利用try代码块,调用dao
        try {
            //开启事务(关闭事务自动提交)
            connection.setAutoCommit(false);

            BankDao bankDao = new BankDao();
            //调用加钱 和 减钱
            bankDao.addMoney(addAccount, money, connection);
            System.out.println("--------------");
            bankDao.subMoney(subAccount, money, connection);
            flag = 1;
            //不报错,提交事务
            connection.commit();
        } catch (Exception e) {

            //报错回滚事务
            connection.rollback();
            throw e;
        } finally {
            connection.close();
        }

        if (flag == 1) {
            System.out.println("转账成功!");
        } else {
            System.out.println("转账失败!");
        }
    }
}

5、BankTest

java
package com.xx;

import org.junit.Test;

/**
 * @Author: xueqimiao
 * @Date: 2023/1/4 09:48
 */
public class BankTest {

    @Test
    public void testBank() throws Exception {
        BankService bankService = new BankService();
        bankService.transfer("ergouzi", "lvdandan",
                500);
    }
}