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:设置隔离级别
1.2 MySQL 事务特性(acid)
- 原子性(Atomicity):事务是一个不可分割点工作单位,事务中的操作要么全发生,要么都不发生
- 隔离性(isolation):当多个用户访问数据库时,数据库为每个用户之间设置隔离,不会相互干扰
- 持久性(durability):一个事务一旦提交,对数据库的改变是永久性的,即使数据库发生故障也不会有任何影响
- 一致性(consistency):事务使数据库从一个一致性状态 切换到另外一个一致性状态
1.3 MySQL 存储引擎 和 表类型
- 基本介绍
- MySQL的表类型由存储引擎(Storage Engines)决定,主要包括MylISAM.innoDB、Memory等。
- MySQL 数据表主要支持六种类型,分别是:
CSV、Memory、ARCHIVE.MRG MYISAM、MYISAM、InnoBDB。 - 这六种又分为两类,一类是”事务安全型”(transaction-safe),比如: InnoDB;其余都属于第二类,称为”非事务安全型”(non-transaction-safe)[mysiam和memory].
- 主要的存储引擎/表类型特点
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 基本介绍
- JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题。
- Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。
- JDBC的基本原理图 [重要 ! ]
2.2 JDBC 快速入门
2.2.1 JDBC程序编写步骤
- 注册驱动 - 加载 Driver 类
- 获取连接 - 得到 Connection
- 执行增删改查 - 发送 SQL 给 MySQL 执行
- 释放资源 - 关闭连接
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();
}
- 连接数据库的几种方式
@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
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 基本介绍
- 当需要成批插入或者更新记录时。可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率。
- 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,省了一步)
- 预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
- 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
- 当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中
2.6.2 传统问题分析
- 传统的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将Connection 加载到内存中,再验证IP地址,用户名和密码(0.05s~1s时间)。需要数据库连接的时候,就向数据库要求一个,频繁的进行数据库连接操作将占用很多的系统资源,容易造成服务器崩溃。
- 每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。
- 传统获取连接的方式,不能控制创建的连接数量,如连接过多,会导致内存泄漏,MySQL崩溃。
- 解决传统开发中的数据库连接问题,可以采用数据库连接池技术(connection pool) 。
2.6.3 连接池示意图
2.6.4 连接池种类
- JDBC的数据库连接池使用
javax.sql.DataSource来表示,DataSource只是一个接口,该接口通常由第三方提供实现 - C3PO数据库连接池,速度相对较慢,稳定性不错(hibernate, spring).
- DBCP数据库连接池,速度相对c3p0较快,但不稳定
- Proxool数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
- BoneCP 数据库连接池,速度快
Druid(德鲁伊) 重点!!!!是阿里提供的数据库连接池,集DBCP、C3Po、Proxool优点于一身的数据库连接池- 德鲁伊连接池 应用案例
// 德鲁伊连接池 应用案例
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);
}
- 德鲁伊工具类
// 德鲁伊工具类
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 的优势
- commons-dbutils是 Apache组织提供的一个开源 JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化jdbc编码的工作量[真的]。
- QueryRunner类:该类封装了SQL的执行,是线程安全的。可以实现增、删、改、查、批处理
- 使用QueryRunner类实现查询
- 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