15JavaEE核心技术-JDBC和数据访问
目录
15、JavaEE核心技术 - JDBC和数据访问
四、JDBC和数据访问
一、JDBC基础
1. 什么是JDBC?
JDBC(Java Database Connectivity)是Java用于与数据库交互的API,允许Java程序执行SQL语句,访问、处理数据库中的数据。
2. JDBC的作用
- 提供统一的接口,连接不同的数据库。
- 允许执行SQL语句,包括CRUD(增删改查)操作。
- 支持事务管理,确保数据一致性。
- 提供结果集处理,使得查询结果的处理更加灵活。
3. JDBC的主要组件
- JDBC驱动(JDBC Driver) :用于连接特定数据库的软件组件,翻译JDBC调用为数据库特有的协议。
- 连接(Connection) :代表应用程序与数据库之间的会话,用于执行SQL语句。
- 语句(Statement) :用于执行SQL语句,返回结果集。
- 结果集(ResultSet) :存储查询返回的数据,允许逐行遍历和数据访问。
- 事务管理(Transaction Management) :管理数据库事务,确保操作的原子性、一致性、隔离性和持久性(ACID)。
4. JDBC驱动类型
- 类型1:JDBC-ODBC桥接器
- 通过ODBC驱动连接数据库,性能较差,主要用于旧系统。
- 类型2:本地API进程间通信
- 在客户端和数据库之间使用进程间通信,性能较好,依赖数据库特定的客户端库。
- 类型3:网络-中间件
- 通过中间件服务器连接数据库,适合Web应用,增强了安全性和管理能力。
- 类型4:纯Java驱动
- 直接通过TCP/IP连接数据库,纯Java实现,性能和安全性最佳。
5. 使用JDBC的基本步骤
- 注册JDBC驱动
:使用
Class.forName()
加载驱动类。 - 建立连接
:通过
DriverManager.getConnection()
获取连接。 - 创建语句
:使用
Connection.createStatement()
创建Statement对象。 - 执行SQL语句
:调用
Statement.execute()
或executeQuery()
执行查询或更新。 - 处理结果集
:遍历
ResultSet
获取查询结果。 - 关闭资源 :确保关闭Statement、ResultSet和Connection,释放资源。
import java.sql.*;
public class JdbcExample {
public static void main(String[] args) {
// 数据库连接URL
String url = "jdbc:mysql://localhost:3306/testdb";
// 用户名
String username = "root";
// 密码
String password = "password";
// 注册JDBC驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("无法加载JDBC驱动:");
e.printStackTrace();
return;
}
// 执行SELECT查询
String query = "SELECT * FROM users";
// try-with-resources,资源会在以下情况下自动关闭:1、正常执行完 try 块。2、发生异常时,虚拟机会确保资源被关闭。
try (Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
System.out.println("连接数据库成功,执行查询:");
// 遍历结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
System.out.println("User ID: " + id + ", Name: " + name + ", Email: " + email);
}
} catch (SQLException e) {
System.out.println("数据库操作错误:");
e.printStackTrace();
}
}
}
二、连接池
1. 为什么需要连接池?
- 性能优化 :减少频繁创建和关闭连接的开销,提升响应速度。
- 资源管理 :高效管理数据库连接,避免资源耗尽。
- 扩展性 :支持更多的并发用户,提高系统吞吐量。
2. 常见的连接池实现
- HikariCP :性能优异,配置简单,广泛使用。
- Apache DBCP :稳定可靠,适合中小型应用。
- C3P0 :功能丰富,适合复杂应用,但性能相对较差。
3. 使用HikariCP实现连接池
步骤 :
添加依赖 :在项目中引入HikariCP库。
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency>
配置属性文件 :创建
hikari.properties
文件,配置连接参数。dataSourceClassName= com.mysql.cj.jdbc.Driver dataSource.url=jdbc:mysql://localhost:3306/testdb dataSource.user=root dataSource.password=secret
初始化HikariDataSource :在应用启动时初始化连接池。
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; public class DatabaseConfig { private static HikariDataSource dataSource; static { HikariConfig config = new HikariConfig("/hikari.properties"); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }
获取连接 :在需要访问数据库时,从连接池获取连接。
try (Connection conn = DatabaseConfig.getConnection()) { // 执行数据库操作 } catch (SQLException e) { e.printStackTrace(); }
关闭连接池 :在应用关闭时,关闭连接池。
dataSource.close();
完整代码:
import java.sql.*;
public class HikariCPExample {
public static void main(String[] args) {
// 数据库连接信息
String DB_URL = "jdbc:mysql://localhost:3306/testdb";
String USERNAME = "root";
String PASSWORD = "password";
// HikariCP 配置
HikariConfig config = new HikariConfig();
config.setJdbcUrl(DB_URL);
config.setUsername(USERNAME);
config.setPassword(PASSWORD);
// 可选配置:连接池参数
config.setMinimumIdle(5); // 最小空闲连接数
config.setMaximumPoolSize(10); // 最大连接池大小
config.setConnectionTimeout(30000); // 连接超时时间(毫秒)
config.setIdleTimeout(60000); // 空闲连接超时时间(毫秒)
// 创建 HikariDataSource
HikariDataSource dataSource = new HikariDataSource(config);
try {
// 获取连接
Connection conn = dataSource.getConnection();
// 执行查询
String query = "SELECT * FROM users";
try (PreparedStatement stmt = conn.prepareStatement(query)) {
try (ResultSet rs = stmt.executeQuery()) {
System.out.println("连接数据库成功,执行查询:");
// 遍历结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
System.out.println("User ID: " + id + ", Name: " + name + ", Email: " + email);
}
}
}
} catch (SQLException e) {
System.out.println("数据库操作错误:");
e.printStackTrace();
} finally {
// 关闭连接池
dataSource.close();
}
}
}
4. 连接池的常用配置参数
- connectionTimeout :获取连接的超时时间,避免长时间等待。
- maximumPoolSize :最大连接数,根据服务器能力和负载调整。
- minimumIdle :保持的最少空闲连接数,提高响应速度。
- idleTimeout :空闲连接的超时时间,避免资源浪费。
- poolName :连接池名称,方便监控和管理。
三、事务管理与锁机制
1. 事务管理
事务的特性(ACID) :
- 原子性(Atomicity) :事务中的操作要么全部执行,要么不执行。
- 一致性(Consistency) :事务前后,数据库保持一致的状态。
- 隔离性(Isolation) :多个事务间互不影响,避免数据不一致。
- 持久性(Durability) :事务提交后,结果永久保存。
JDBC中的事务管理 :
自动提交 :默认每个语句提交,可能导致性能问题。
conn.setAutoCommit(true); // 默认值
手动事务管理 :显式控制事务的提交和回滚。
conn.setAutoCommit(false); // 开始事务 try { // 执行多个SQL语句 conn.commit(); // 提交事务 } catch (SQLException e) { conn.rollback(); // 回滚事务 } finally { conn.setAutoCommit(true); // 恢复自动提交 }
保存点(Savepoint) :在事务中设置保存点,部分回滚。
Savepoint savePoint = conn.setSavepoint(); try { // SQL操作1 // SQL操作2 conn.releaseSavepoint(savePoint); } catch (SQLException e) { conn.rollback(savePoint); }
2. 锁机制
锁的类型 :
- 乐观锁 :假设多数情况下数据不会冲突,通过版本号或时间戳检测冲突。
- 悲观锁 :假设数据会发生冲突,事先加锁,阻止其他事务访问。
JDBC中的锁机制 :
行级锁 :锁定特定行,允许并行访问。
SELECT * FROM users WHERE id = 1 FOR UPDATE;
表级锁 :锁定整个表,限制并行访问。
LOCK TABLES users WRITE;
事务隔离级别 :通过设置隔离级别,控制数据读取的一致性。
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 隔离级别: // TRANSACTION_READ_UNCOMMITTED:最低隔离 // TRANSACTION_READ_COMMITTED:提交读 // TRANSACTION_REPEATABLE_READ:可重复读 // TRANSACTION_SERIALIZABLE:最高隔离
4. 常见的并发问题
- 脏读(Dirty Read) :事务读取了另一个未提交事务的修改。
- 不可重复读(Non-repeatable Read) :同一事务中,相同查询返回不同结果。
- 幻读(Phantom Read) :事务在特定条件下返回更多行,破坏一致性。
四、CRUD操作实践
1. 创建操作(Create)
示例:添加用户
public void createUser(String name, String email) {
final String SQL = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection conn = DatabaseConfig.getConnection();
PreparedStatement pstmt = conn.prepareStatement(SQL)) {
pstmt.setString(1, name);
pstmt.setString(2, email);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
// 处理异常,可能回滚或记录日志
}
}
注意事项 :
- 预编译语句(PreparedStatement) :防止SQL注入,提高安全性。
- 资源管理 :使用try-with-resources自动关闭连接和语句。
2. 读取操作(Read)
示例:查询用户列表
public List<User> getAllUsers() {
final String SQL = "SELECT * FROM users";
List<User> users = new ArrayList<>();
try (Connection conn = DatabaseConfig.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(SQL)) {
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
// 处理异常
}
return users;
}