1. JDBC 编程六部

  1. 注册驱动
  2. 获取数据库连接
  3. 获取数据库操作对象
  4. 执行 SQL 语句
  5. 处理查询结果集
  6. 释放资源

注意:连接数据库时的URL:

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static void main(String[] args) {
Statement stmt = null;
Connection conn = null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");

//2.获取数据库连接
String url = "jdbc:mysql://localhost:3306/Study";
String user = "root";
String password = "Pu1949..";
conn = DriverManager.getConnection(url, user, password);
//输出连接对象的内存地址
//System.out.println(conn);
//com.mysql.cj.jdbc.ConnectionImpl@6f1de4c7

//3.获取数据库操作对象
stmt = conn.createStatement();
//通过一个连接对象可以创建多个 Statement 对象

//4.执行 SQL 语句
String sql = "INSERT INTO book(id, bname, price, authorId, pubDate)" +
"VALUES(4, '概率论与数理统计', 20.00, 4, NOW());";
int result = stmt.executeUpdate(sql);

//5.输出执行结果
System.out.println(result);

} catch (SQLException e) {
e.printStackTrace();
} finally {
//6.释放资源
//先释放 Statement,再释放 Connection
//分别进行 try-catch 处理
if (stmt != null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

2. 功能类详解

2.1 DriverManager

DriverManager 驱动管理对象

2.1.1 注册驱动

方法一:注册给定地驱动程序

1
2
Driver driver = new com.mysql.jdbc.Driver();
DriverManager.registerDriver(driver);

方法二:直接使用com.mysql.cj.jdbc.Driver类中的静态代码块

1
Class.forName("com.mysql.cj.jdbc.Driver");

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mysql.cj.jdbc;

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

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}

static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}

注意:

  • 一般情况下不需要调用DriverManager的静态方法registerDriver(),因为只要静态类被使用,就会自动调用其中的静态代码块完成注册驱动
  • mysql5 之后可以省略注册驱动的步骤

2.1.2 获取数据库连接

获取数据库连接对象:

1
static Connection getConnection(String url, String username, String password);

2.2 Connection

Connection 数据库连接对象

2.2.1 获取执行者对象

  1. 获取普通执行者对象:
1
Statement createStatement();
  1. 获取预编译执行者对象
1
PreparedStatement prepareStatement(String sql);

2.2.2 管理事务

手动管理事务:

1
setAutoCommit(boolean autoCommit);

jdbc 默认是自动提交事务,通过将参数设置为 false 可以开启手动提交事务

提交事务:

1
commit();

回滚事务:

1
rollback();

2.2.3 释放资源

立即将数据库连接对象释放:

1
void close();

2.3 Statement

Statement 执行 sql 语句

2.3.1 执行 DML 语句(增、删、改 操作)

1
int executeUpdate(String sql);

返回值:int 类型,指影响的行数

参数:insert、update、delete 语句

2.3.2 执行 DQL 语句(查询操作)

1
ResultSet executeQuery(String sql);

返回值:ResultSet 类型,封装查询结果

参数:select 语句

2.3.3 释放资源

1
void close();

2.4 ResultSet

ResultSet 结果集对象

2.4.1 判断结果集中是否还有数据

1
boolean next();

返回值:有数据则返回 true,并将索引向下移动一行。没有数据则返回 false

2.4.2 获取结果集中的数据

1
XXX getXxx("数据库列名");

XXX:java 数据类型

如:

1
2
String getString("name");
int getInt("age");

2.4.3 释放资源

1
void close();

2.5 PrepareStatement

PrepareStatement 预编译执行者对象

原理:

  • 在执行 sql 语句之前,将 sql 语句进行提前编译。明确 sql 语句的格式后,就不会改变了,剩下的内容都只会被认为是参数
  • sql 语句中的参数使用 ?做占位符

2.5.1 为 ?占位符赋值的方法

1
setXxx(参数1, 参数2);

Xxx:数据类型

参数1:?的位置编号(编号从1开始)

参数2:?的实际参数

1
2
3
String sql = "select * from book where id=?";
statement = connection.prepareStatement(sql);
statement.setInt(1, 1);

2.5.2 执行 sql 语句

  1. 执行 insert、update、delete 语句
1
int executeUpdate();
  1. 执行 select 语句
1
ResultSet executeQuery();

2.5.3 释放资源

1
void close();

3. 数据库连接池

3.1 自定义数据库连接池

3.1.1 实现步骤

  1. 定义一个类,实现 DataSource 接口
  2. 定义一个容器,用于保存多个 Connection 连接对象
  3. 定义静态代码块,通过 JDBC 工具类获取 10 个连接对象并保存到容器中
  4. 重写 getConnection 方法,从容器中获取一个连接并返回
  5. 定义 getSize 方法,用于获取容器的大小并返回

3.1.2 归还连接

归还数据库连接的方式:

  1. 继承方式

思想:

  • 通过打印连接对象,发现 DriverManager 获取的连接实现类是 JDBC4Connection
  • 那么就可以自己定义一个类,继承 JDBC4Connection这个类,重写close() 方法,完成连接对象的归还

问题:

  • 我们虽然定义了一个子类完成了连接归还的操作,但是DriverManager获取的还是 JDBC4Connection这个对象,并不是我们自己定义的子类对象。多态允许把一个子类对象指向父类引用,但是这里强转相当于把一个父类对象指向子类引用,会报语法错误。所以此方法行不通
  1. 装饰设计模式

思想:

  • 可以自己定义一个类,实现 Connection 接口。这样就具备了和 JDBC4Connection相同的行为了
  • 重写close() 方法,完成连接的归还,其余的功能还调用 mysql 驱动包实现类原有的方法即可

问题:

  • 实现 Connection 接口后,有大量的方法需要在自定义类中进行重写,非常麻烦
  1. 适配器设计模式

思想:

  • 我们可以自己定义一个适配器类,实现 Connection 接口,将除 close 外的所有方法进行实现
  • 自定义连接类只需要继承这个适配器类,重写需要改进的 close 方法即可

问题:

  • 虽然自定义连接类比较简洁,但是适配器类和上个方法一样麻烦
  1. 动态代理方式

思想:

  • 通过 Proxy 来完成对 Connection 实现类对象的代理
  • 代理过程中判断如果执行的是 close 方法,就将连接归还到连接池中,如果是其他的方法就继续调用原来的功能即可

3.2 第三方连接池

3.2.1 c3p0 连接池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws Exception {
//1.创建c3p0连接池对象
DataSource dataSource = new ComboPooledDataSource();

//2.通过连接池对象获取数据库连接
Connection connection = dataSource.getConnection();

//3.执行操作
String sql = "select * from book";
PreparedStatement stmt = connection.prepareStatement(sql);
ResultSet set = stmt.executeQuery();
while (set.next()) {
System.out.printf("id:%d name:%s price:%.2f\n", set.getInt("id"), set.getString("bookName"), set.getDouble("price"));
}
set.close();
stmt.close();
connection.close();
}

3.2.2 Druid 连接池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) throws Exception {
//1.通过Properties集合加载配置文件
InputStream in = testDruid.class.getClassLoader().getResourceAsStream("druid.properties");
Properties druid = new Properties();
druid.load(in);

//2.通过Druid连接池工厂类获取数据库连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(druid);

//3.通过连接池对象获取数据库连接进行使用
Connection connection = dataSource.getConnection();
String sql = "select * from book";
PreparedStatement stmt = connection.prepareStatement(sql);
ResultSet set = stmt.executeQuery();
while (set.next()) {
System.out.printf("id:%d name:%s price:%.2f\n",
set.getInt("id"),
set.getString("bookName"),
set.getDouble("price"));
}
set.close();
stmt.close();
connection.close();
}