一、JDBC概述
-
JDBC:Java DataBase Connectivity Java连接数据库技术。
-
JDBC四个接口一个类
- DriverManager:驱动管理器类,管理JDBC驱动,不同数据库驱动管理器不同
- Driver:驱动接口
- Connection:连接接口 传输数据
- Statement:由Connection创建,执行SQL接口
- ResultSet:保存Statement执行后产生的查询结果集接口
// java.sql.DriverManager类,获取并加载驱动 Class.forName("驱动连接"); MySQL5: "com.mysql.jdbc.Driver" MySQL8: "com.mysql.cj.jdbc.Driver" Oracle11g: "oracle.jdbc.driver.OracleDriver" // java.sql.Driver驱动接口
// java.sql.Connection 获取连接,实现与数据库之间的连接 Connection conn = DriverManager.getConnection(数据库连接,用户名,密码); MySQL: "jdbc:mysql://localhost:3306/数据库名?useSSL=false&characterEncoding=utf-8&serverTimeZone=Asia/Shanghai" Oracle: "jdbc:oracle:thin:@localhost:1521:数据库实例名" // java.sql.Statement,java.sql.PreparedStatement 发送并执行SQL接口 // 1. 创建执行sql的对象 Statement statement = conn.createStatement(); // 2. 创建预编译对象 PreparedStatemnt preparedStatement = conn.prepareStatement(sql); // 3. 返回自增的键 仅在增加语句使用 PreparedStatemnt preparedStatement = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
// java.sql.ResultSet 获取查询的结果集 // 1. 执行sql返回结果集或操作的行数 ResultSet resultSet = statement.executeQuery(sql); int num = statement.executeUpdate(sql); // 2. 预编译对象已经执行过sql 返回结果集或操作的行数 ResultSet resultSet = preparedStatement.executeQuery(); int num = preparedStatement.executeUpdate(); // 3. 获取自增键的结果集 Result resultSet = preparedStatement.getGeneratedKeys();
-
JDBC连接步骤
- 加载驱动
- 创建连接
- 编写sql
- 获取sql执行对象
- 执行sql并返回
- 关闭资源
二、SQL注入
1. 概念:通过特殊参数传入程序,非法侵入系统
2. 解决方法:使用PreparedStatement创建对象
- PreparedStatement批处理速度快,效率高,安全性高
- PreparedStatement不使用字符串拼接,安全性高
- 预编译SQL命令,无需多次创建对象,批处理速度快,效率高
- Statement使用SQL拼接,执行一次编译一次,效率低,安全性低
三、封装Connection对象
# jdbc.properties
# DRIVER
driverClassName=com.mysql.cj.jdbc.Driver
# URL
url=jdbc:mysql://localhost:3308/goods?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
# USER
username=root
# PASSWORD
password=mysql
# 最大活跃数量
maxActive=50
# 最大空闲数量
maxIdle=20
# 最大等待时长
maxWait=60000
package com.goods.util;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @program: jdbc_study
* @description: MySQL工具类
* @author: MaEnguang
* @create: 2021-06-01 10:10
**/
public class DBUtil {
// 工具类 禁止外部实例化
private DBUtil() {
}
// 声明一个配置文件类
private static Properties properties = new Properties();
// 创建数据源对象
private static DataSource dataSource;
static {
try (
// 获取文件流
InputStream resourceAsStream = DBUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
) {// 文件类加载文件流
properties.load(resourceAsStream);
// 创建数据源管理
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 处理多线程
private static ThreadLocal<Connection> local = new ThreadLocal<>();
/**
* 获取连接
* @return
* @throws ClassNotFoundException
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
// 仅获取本地一条线程的连接
Connection connection = local.get();
if (connection == null || connection.isClosed()) {
// 通过数据源管理连接
connection = dataSource.getConnection();
// 将数据源放到本地线程中 保证线程安全
local.set(connection);
}
return connection;
}
/**
* 关闭连接
*/
public static void closeConnection() {
try {
// 仅获取本地一条线程的连接
Connection connection = local.get();
if (connection != null && !connection.isClosed()) {
// 释放数据源中占用的连接
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
// 线程需要移除
local.remove();
}
}
}
四、事务
-
事务的特点
- 原子性:一个事务,要么都提交,要么都回滚
- 一致性:事务必须是数据库从一个一致性状态变到另一个一致性状态
- 隔离性:事务之间互不影响
- 持续性:事务的结果可以被永久保留
-
事务的开始与结束
- 显示开始:执行DML
- 显式结束:执行commit,rollback
- 隐式提交:执行了DDL,DCL,exit
- 隐式回滚:系统异常,死机
-
事务中易出现的并发问题
- 脏读:读取到未提交的数据
- 不可重复读:同时读取数据时能读取到此时被修改的数据
- 幻读:同时读取数据时,读取到此时新增或删除的数据
-
事务的隔离级别
- 读未提交:可以读取到未提交的数据,脏读
- 读已提交:不能读取到未提交的数据,避免脏读
- 可重复读:同时访问数据时,禁止修改,避免不可重复读
- 串行化:同时访问数据时,禁止添加删除数据,避免幻读
-
JDBC中的事务
// 业务层增删改操作需要提交事务 // 关闭自动提交 connection.setAutoCommit(false); // 业务处理... // 业务处理后提交事务 connection.Commit();