Java 学习(2022-07-15)

135 阅读9分钟

Java 学习(2022-07-15)

1. MySQL 数据库

1.1 MySQL 事务隔离级别(A对事务的操作,影响了B对事务的操作)

定义了事务与事务之间的隔离级别,默认隔离级别为:可重复读

  • 脏读(dirty read):当一个事务读取另一个事务尚未提交的改变(update,insert,delete)时,产生脏读
  • 不可重复读(nonrepeatable read):同一查询在同一事务中多次进行,由于身他提交事务所做的修改或删除,每次返回不同的结果集,此时发生不可重复读。
  • 幻读(phantom read):同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻读。
- select @@tx_isolation :查看当前mysql事务隔离级别
-- select @@global.tx_isolation 查看系统当前隔离级别
- set session transaction isolation level uncommitted:设置隔离级别

image.png

image.png

1.2 MySQL 事务特性(acid)

  • 原子性(Atomicity):事务是一个不可分割点工作单位,事务中的操作要么全发生,要么都不发生
  • 隔离性(isolation):当多个用户访问数据库时,数据库为每个用户之间设置隔离,不会相互干扰
  • 持久性(durability):一个事务一旦提交,对数据库的改变是永久性的,即使数据库发生故障也不会有任何影响
  • 一致性(consistency):事务使数据库从一个一致性状态 切换到另外一个一致性状态

1.3 MySQL 存储引擎 和 表类型

  • 基本介绍
  1. MySQL的表类型由存储引擎(Storage Engines)决定,主要包括MylISAM.innoDB、Memory等。
  2. MySQL 数据表主要支持六种类型,分别是:CSV、Memory、ARCHIVE.MRG MYISAM、MYISAM、InnoBDB
  3. 这六种又分为两类,一类是”事务安全型”(transaction-safe),比如: InnoDB;其余都属于第二类,称为”非事务安全型”(non-transaction-safe)[mysiam和memory].
  • 主要的存储引擎/表类型特点 image.png

1.4 MySQL 视图

1.4.1 视图基本使用

  • create view 视图名 as select 语句
  • drop view 视图名1,视图名2
  • show create view 视图名
  • alter view 视图名 as select 语句

2. JDBC 和 数据库连接池

2.1 JDBC 概述

2.1.1 基本介绍

  1. JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题。
  2. Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。
  3. JDBC的基本原理图 [重要 ! ]

image.png

2.2 JDBC 快速入门

2.2.1 JDBC程序编写步骤

  1. 注册驱动 - 加载 Driver 类
  2. 获取连接 - 得到 Connection
  3. 执行增删改查 - 发送 SQL 给 MySQL 执行
  4. 释放资源 - 关闭连接
public static void main(String[] args) throws SQLException {
        // 前置工作,引入 mysql 的 jar 包
        // 1. 注册驱动
        Driver driver = new Driver(); // 创建 driver 对象
        // 2. 得到连接
        // 通过 jdbc 的方式连接 mysql
        // localhost 可以是主机,可以是 IP 地址
        // db01 代表数据库
        // 3306 代表 mysql 端口
        String url = "jdbc:mysql://10.67.181.199:3306/db01";
        // 将用户名和密码放入 Properties 对象
        // user 和 password 不能变,后面的值可以变
        Properties properties = new Properties();
        properties.setProperty("user", "application");
        properties.setProperty("password", "Uts_123456");

        // 获取连接
        Connection connect = driver.connect(url, properties);

        // 3. crud
        String sql = "update actor set name = 'zxy' where name = 'ldh'";

        // 帮我们发送 sql,执行 sql 语句,并返回结果对象
        Statement statement = connect.createStatement();
        // rows 代表受影响行数
        int rows = statement.executeUpdate(sql);
        System.out.println(rows >= 1 ? "成功" : "失败");
        // 4. 关闭连接
        statement.close();
        connect.close();
    }
  1. 连接数据库的几种方式
@Test
public void connect01() throws SQLException {
    Driver driver =  new Driver();
    String url = "jdbc:mysql://10.67.181.199:3306/db01";
    Properties properties = new Properties();
    properties.setProperty("user", "application");
    properties.setProperty("password", "Uts_123456");
    Connection connect = driver.connect(url, properties);
    System.out.println(connect);
}
// 方式 2
@Test
public void connect02() throws Exception {
    String url = "jdbc:mysql://10.67.181.199:3306/db01";
    // 使用反射加载 Driver 类
    Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
    Driver driver = (Driver)aClass.getConstructor().newInstance();
    Properties properties = new Properties();
    properties.setProperty("user", "application");
    properties.setProperty("password", "Uts_123456");
    Connection connect = driver.connect(url, properties);
    System.out.println(connect);
}
// 方式 3 使用 DriverManage 统一管理
@Test
public void connect03() throws Exception {
    // 使用反射加载 Driver 类
    Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
    Driver driver = (Driver)aClass.getConstructor().newInstance();

    String url = "jdbc:mysql://10.67.181.199:3306/db01";
    String user = "application";
    String password = "Uts_123456";

    // 注册驱动
    DriverManager.registerDriver(driver);
    Connection connect = DriverManager.getConnection(url, user, password);
    System.out.println(connect);
}
// 方式 4 增加配置文件 - 推荐使用
@Test
public void connect04() throws Exception {

    Properties properties = new Properties();
    properties.load(new FileInputStream("src\mysql.properties"));

    String user = properties.getProperty("user");
    String password = properties.getProperty("password");
    String driver = properties.getProperty("driver");
    String url = properties.getProperty("url");

    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println(connection);

    connection.close();
}



        Properties properties = new Properties();
        properties.load(new FileInputStream("src\mysql.properties"));

        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");

        // 1. 注册驱动
        // 2. 得到连接
        Connection connection = DriverManager.getConnection(url, user, password);

        // 4. 组织 sql
        String sql = "select * from actor where name = ? and pwd = ?";
        // 3. 得到 Statement
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        preparedStatement.setString(1, user); // 1 代表上面的 ?位置
        preparedStatement.setString(2, password);
        ResultSet resultSet = preparedStatement.executeQuery();
        // 5. 通过 while 取出
        while (resultSet.next()){ // 让光标向后移
            int anInt = resultSet.getInt(1);
            String string = resultSet.getString(2);
            String string1 = resultSet.getString(3);
            Date date = resultSet.getDate(4);
            System.out.println(anInt + "\t" + string + "\t" + string1 + "\t" + date);
//            1    李永奇    男  1970-11-11
//            2    ldh2   男  1970-11-11
//            3    xxx        男  1970-11-11
        }

        preparedStatement.close();
        resultSet.close();
        connection.close();

2.3 JDBC API

image.png

image.png

2.4 JDBC 事务

2.4.1 事务介绍

  • JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
  • JDBC程序中为了让多个SQL语句作为一个整体执行,需要使用事务
  • 调用Connection的setAutoCommit(false)可以取消自动提交事务
  • 在所有的SQL语句都成功执行后,调用Connection的commit();方法提交事务
  • 在其中某个操作失败或出现异常时,调用Connection的rollback();方法回滚事务
// 事务案例
public void noTransaction() throws SQLException {
    Connection connection = null;
    String sql = "update account set balance = balance - 100 where id = 1";
    PreparedStatement preparedStatement = null;


    try {
        connection.setAutoCommit(false);
        connection.JDBCUtils.getConnection();
        preparedStatement = connection.prepareStatement(sql);
        preparedStatement.executeUpdate();

        int i = 1 / 0 ; // 抛出异常
        preparesStatement = connection.prepareStatement(sql2);
        connection.commit(); // 提交事务
    } catch (SQLException e){
        // 回滚,默认回到开始的位置
        connection.rollback();
        e.printStackTrace();

    }finally {
        connection.close();
    }
}

2.5 批处理

2.5.1 基本介绍

  1. 当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
  2. JDBC的批量处理语句包括下面方法:
    • addBatch():添加需要批量处理的SQL语句或参数executeBatch0:执行批量处理语句;
    • clearBatch():清空批处理包的语句
    • JDBC连接MySQL时,如果要使用批处理功能,请再url中加参数?rewriteBatchedStatements=true
    • 批处理往往和PreparedStatement一起搭配使用,可以既减少编译次数,又减少运行次数,效率大大提高
public void batch() throws Exception {
    Connection connection = JDBCUtils.getConnection();
    String sql = "insert into admin2 values(null, ?, ?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    System.out.println("开始执行");
    for (int i = 0; i < 5000; i++){
        preparedStatement.setString(1, "jack" + i);
        preparedStatement.setString(2, "666");
        // 将 sql 加入到 批处理
        preparedStatement.addBatch();
        // 当有1000条数据,在批量执行
        if((i + 1) % 1000 == 0){
            preparedStatement.executeBatch(); // 批量执行
            // 清空 sql
            preparedStatement.clearBatch();
        }
    }
}

2.6 连接池

2.6.1 连接池基本介绍(例如电话转接,打电话给A以后,可以让A把电话给B,省了一步)

  1. 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
  2. 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
  3. 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中

2.6.2 传统问题分析

  1. 传统的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将Connection 加载到内存中,再验证IP地址,用户名和密码(0.05s~1s时间)。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。
  2. 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
  3. 传统获取连接的方式,不能控制创建的连接数量,如连接过多,会导致内存泄漏,MySQL崩溃。
  4. 解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool) 。

2.6.3 连接池示意图

image.png

2.6.4 连接池种类

  1. JDBC的数据库连接池使用javax.sql.DataSource 来表示,DataSource只是一个接口,该接口通常由第三方提供实现
  2. C3PO数据库连接池,速度相对较慢,稳定性不错(hibernate, spring).
  3. DBCP数据库连接池,速度相对c3p0较快,但不稳定
  4. Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
  5. BoneCP 数据库连接池,速度快
  6. Druid(德鲁伊) 重点!!!!是阿里提供的数据库连接池,集DBCP、C3Po、Proxool优点于一身的数据库连接池
  7. 德鲁伊连接池 应用案例
// 德鲁伊连接池 应用案例
 public static void main(String[] args)  throws Exception{
        // 1. 加入 Druid jar 包
        // 2. 加入配置文件 Druid.properties 放置 src 目录
        // 3. 创建 Properties 对象,读取配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\druid.properties"));
        // 4. 创建指定参数的数据库连接池, Druid 连接
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 500000; i++) {
            Connection connection = dataSource.getConnection();
//            System.out.println("连接成功" + connection);
            connection.close();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
  1. 德鲁伊工具类
// 德鲁伊工具类
public static void main(String[] args) throws SQLException {
    // 1. 得到连接
    Connection connection = null;
    // 2. 组织 sql
    PreparedStatement preparedStatement = null;
    String sql = "select * from goods where id = ?";
    ResultSet set = null;
    // 3. 创建 PreparedStatement 对象
    connection = JDBCUtilsDruid.getConnection();
    preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setInt(1, 2); // 给 ? 号赋值

    // 执行得到结果集
    set = preparedStatement.executeQuery();
    // 遍历结果集
    while (set.next()) {
        int id = set.getInt("id");
        String goods_name = set.getString("goods_name");
        System.out.println(id + "\t" + goods_name);
    }

    JDBCUtilsDruid.close(set, preparedStatement, connection);

}

2.7 Apache - DBUtils 工具类

2.7.1 DBUtils 的优势

  1. commons-dbutils是 Apache组织提供的一个开源 JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量[真的]。
  2. QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理
  3. 使用QueryRunner类实现查询
  4. ResultSetHandler接口:该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式

2.7.2 常用方法

  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler: 把结果集中的每一行数据都转成一个数组,再存放到List中。BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler(name):将结果集中的每行数据都封装到Map里,再把这些map再存到一个map里,其key为指定的
  • key。MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List