JDBC 操作数据库
- JDBC 使用
- SQL 注入攻击的应对
- 数据库查询方法
- 数据库写入方法
- 连接池的使用
- DBUtils
- Apache Commons DBUtils
JDBC 使用
全称:Java DataBase Connectivity
,Java 数据库连接,是 Java 语言操作数据库的一种规范,是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。
1. 加载并注册数据库 JDBC 驱动
下载数据库 jar 包,添加到项目文件夹lib
中,在开发工具中选择File
-> Project Structure
-> Modules
-> Dependencies
添加 jar 包。 代码中使用 Class.forName() 方法加载并注册数据库驱动,如:
Class.forName("com.mysql.cj.jdbc.Driver");
2. 创建数据库连接
String dbURL = "jdbc:mysql://localhost:3306/imooc_java?useSSL=false&useUnicode=true&characterEncoding=UTF-8&severTimezone=Aisa/Shanghai&allowPublicKeyRetrieval=true";
String dbUsername = "root";
String dbPassword = "123456";
Connection conn = DriverManager.getConnection(dbURL,dbUsername, dbPassword);
2
3
4
3. 创建 Statement 对象
用来创建 SQL 语句
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from employee where dname='研发部'");
2
4. 遍历查询结果
while (rs.next()) {
Integer eno = rs.getInt(1);
String ename = rs.getString("ename");
Float salary = rs.getFloat("salary");
String dname = rs.getString("dname");
System.out.println(eno + ", " + ename + ", " + salary + ", " + dname);
}
2
3
4
5
6
7
5. 关闭连接,释放资源
conn.close();
JDBC 驱动程序
需要从各大数据库厂商的官网下载对应数据库的驱动程序。
SQL 注入攻击的应对
解决方法:使用 PreparedStatement 对象代替 Statement
String sql = "select * from employee where ename=? and salary=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, ename);
pstmt.setFloat(2, salary);
ResultSet rs = pstmt.executeQuery();
2
3
4
5
数据库写入方法
INSERT
所有写操作都是使用executeUpdate
方法,返回值是int
类型,表示影响的行数。
Connection conn = DBUtils.getConnection();
String sql = "insert into employee(eno, ename, salary, dname) values(?, ?, ?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, eno);
pstmt.setString(2, ename);
pstmt.setFloat(3, salary);
pstmt.setString(4, dname);
int cnt = pstmt.executeUpdate();
System.out.println("cnt: " + cnt);
2
3
4
5
6
7
8
9
UPDATE
Connection conn = DBUtils.getConnection();
String sql = "update employee set salary=? where eno=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setFloat(1, salary);
pstmt.setInt(2, eno);
int cnt = pstmt.executeUpdate();
System.out.println("cnt: " + cnt);
2
3
4
5
6
7
DBUtils
封装 DBUtils 工具方法。
创建新的数据库连接,和关闭数据库连接。
Apache Commons DBUtils
Apache Commons DBUtils 是 Apache 组织提供的一个开源 JDBC 工具类库,它是对 JDBC 的简单封装,学习成本极低,并且使用 DBUtils 可以极大简化 JDBC 编码的工作量,同时也不会影响程序的性能。
事务机制
事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。不能只执行其中的一部分操作。
使用场景,比如银行转账,需要保证转账的两个操作要么都执行,要么都不执行。 批量操作,比如批量插入,需要保证所有数据都插入成功,要么都不插入。如果有一条失败,那就全部回滚。
JDBC 有两种事物模式:
- 自动提交模式
- 手动提交模式
自动提交模式
默认模式,每执行一次写操作,就会自动提交一次事务。
此模式无法保证多数据一致性。
手动提交模式
动手提交模式下,需要手动调用commit
方法提交事务。或者调用rollback
方法回滚事务。
手动提交模式下,可以保证多数据一致性。
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DBUtils.getConnection();
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
String sql = "insert into employee(eno, ename, salary, dname) values(?,?,?,?)";
for (int i = 100; i < 200; i++) {
if (i == 105) {
throw new RuntimeException("插入失败");
}
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, i);
pstmt.setString(2, "员工" + i);
pstmt.setFloat(3, 4000);
pstmt.setString(4, "市场部");
pstmt.executeUpdate();
}
// 提交事务
conn.commit();
} catch (Exception e) {
e.printStackTrace();
try {
if (conn != null && !conn.isClosed()) {
// 回滚事务
conn.rollback();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
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
数据库查询方法
- 分页:limit
- 时间日期处理
- 数据批处理
分页:limit
将数据转换成实体类对象保存。
时间日期处理
JDBC 获取日期使用可以使用 java.sql.Date,它继承自 java.util.Date,可以相互兼容。
写入的时候,需要将输入的字符串转换成 java.sql.Date 类型,分为两步:
- 将日期字符串转换成 java.util.Date 类型
String strHiredate = in.next();
java.util.Date udHiredata = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
udHiredata = sdf.parse(strHiredate);
} catch (Exception e) {
System.out.println("您输入的日期格式有误,格式为:yyyy-MM-dd");
}
2
3
4
5
6
7
8
- 将 java.util.Date 类型转换成 java.sql.Date 类型
long time = udHiredata.getTime();
java.sql.Date sdHiredate = new java.sql.Date(time);
2
数据批处理
- addBatch:将参数添加到批处理任务
- executeBatch:执行批处理任务
String sql = "insert into employee(eno, ename, salary, dname) values(?,?,?,?,?)";
pstmt = conn.prepareStatement(sql);
for (int i = 200; i < 300; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, "员工" + i);
pstmt.setFloat(3, 4000);
pstmt.setString(4, "市场部");
// 将参数添加到批处理任务
pstmt.addBatch();
}
// 执行批处理任务
pstmt.executeBatch();
2
3
4
5
6
7
8
9
10
11
12
连接池的使用
在实际开发中,不会直接使用 JDBC 连接数据库,而是使用连接池。在项目启动时,会创建一定数量的数据库连接,放入连接池中。当需要连接数据库时,直接从连接池中获取连接,使用完毕后,再将连接放回连接池中。
- 阿里巴巴的 Druid 连接池
- C3P0 连接池
Druid 连接池
Druid 是阿里巴巴开发的一个开源的数据库连接池,它是目前最好的数据库连接池。
- 加载属性文件,下载 Druid 的 jar 包,导入项目中,创建
druid-filter.properties
配置文件。
Properties properties = new Properties();
String propertyFile = DruidSample.class.getResource("/druid-filter.properties").getPath();
try {
// 空格会被转义成%20,需要URLDecoder解码,还原成空格
propertyFile = URLDecoder.decode(propertyFile, "UTF-8");
properties.load(new FileInputStream(propertyFile));
} catch (Exception e) {
e.printStackTrace();
}
2
3
4
5
6
7
8
9
- 获取 DataSource 数据源对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
- 创建数据库连接对象
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
2
常见错误
连接池最大上线为 20,当创建到第 21 个时,程序会卡死。
小技巧,在线上环境可以将最小和最大都设置为同一个值,程序一运行,就分配好所有资源,创建好所有连接,避免后面再重新创建连接。这样对程序管理和性能都有帮助。
initialSize=20
maxActive=20
2