JDBC入门教程 - Java数据库连接完整指南
大约 20 分钟
JDBC入门教程 - Java数据库连接完整指南
目录
- JDBC简介
- 环境搭建
- 第一个JDBC程序
- JDBC核心API
- 数据库连接
- CRUD操作详解
- PreparedStatement预编译语句
- 事务管理
- 结果集处理
- 数据库元数据
- 连接池
- 批量操作
- 存储过程调用
- 实际应用案例
- 常见问题与最佳实践
- 总结与进阶
1. JDBC简介
JDBC(Java Database Connectivity)是Java语言中用来规范客户端程序如何访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。
- 数据库连接:建立Java应用程序与数据库之间的连接
- SQL执行:执行SQL语句(查询、更新、删除等)
- 结果处理:处理SQL查询返回的结果集
- 事务管理:管理数据库事务
- 元数据访问:获取数据库和表的结构信息
Java应用程序
↓
JDBC API(java.sql包)
↓
JDBC驱动管理器(DriverManager)
↓
JDBC驱动(数据库厂商提供)
↓
数据库类型1:JDBC-ODBC桥接驱动
- 通过ODBC连接数据库
- 已过时,不推荐使用
类型2:本地API驱动
- 使用数据库厂商的本地库
- 性能较好,但需要安装本地库
类型3:网络协议驱动
- 纯Java实现
- 通过网络协议连接数据库
类型4:本地协议驱动(推荐)
纯Java实现
直接与数据库通信
性能最好,最常用
java.sql:核心JDBC APIjavax.sql:扩展JDBC API(连接池、数据源等)
2. 环境搭建
- JDK 8或更高版本
- 数据库(MySQL、PostgreSQL、Oracle等)
- IDE(IntelliJ IDEA、Eclipse等)
MySQL驱动
方式1:Maven依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>方式2:手动下载
- 访问:https://dev.mysql.com/downloads/connector/j/
- 下载MySQL Connector/J
- 将jar包添加到项目classpath
PostgreSQL驱动
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>Oracle驱动
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>23.2.0.0</version>
</dependency>创建MySQL数据库
-- 创建数据库
CREATE DATABASE IF NOT EXISTS jdbc_demo DEFAULT CHARACTER SET utf8mb4;
USE jdbc_demo;
-- 创建用户表
CREATE TABLE IF NOT EXISTS user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL COMMENT '用户名',
password VARCHAR(100) NOT NULL COMMENT '密码',
email VARCHAR(100) COMMENT '邮箱',
age INT COMMENT '年龄',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 插入测试数据
INSERT INTO user (username, password, email, age) VALUES
('zhangsan', '123456', 'zhangsan@example.com', 25),
('lisi', '123456', 'lisi@example.com', 30),
('wangwu', '123456', 'wangwu@example.com', 28);jdbc-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── entity/
│ │ │ │ └── User.java
│ │ │ ├── dao/
│ │ │ │ └── UserDao.java
│ │ │ ├── util/
│ │ │ │ └── DBUtil.java
│ │ │ └── Main.java
│ │ └── resources/
│ │ └── db.properties
└── pom.xml3. 第一个JDBC程序
package com.example.entity;
import java.time.LocalDateTime;
/**
* 用户实体类
*/
public class User {
private Integer id;
private String username;
private String password;
private String email;
private Integer age;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 构造方法
public User() {
}
public User(String username, String password, String email, Integer age) {
this.username = username;
this.password = password;
this.email = email;
this.age = age;
}
// Getter和Setter方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", age=" + age +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
}
}package com.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class FirstJDBCDemo {
public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/jdbc_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8";
String username = "root";
String password = "root";
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
// 1. 加载数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立数据库连接
connection = DriverManager.getConnection(url, username, password);
System.out.println("数据库连接成功!");
// 3. 创建Statement对象
statement = connection.createStatement();
// 4. 执行SQL查询
String sql = "SELECT id, username, email, age FROM user";
resultSet = statement.executeQuery(sql);
// 5. 处理结果集
System.out.println("查询结果:");
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String email = resultSet.getString("email");
int age = resultSet.getInt("age");
System.out.println("ID: " + id + ", 用户名: " + name +
", 邮箱: " + email + ", 年龄: " + age);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6. 关闭资源
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
System.out.println("资源已关闭");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}- 加载驱动:
Class.forName("com.mysql.cj.jdbc.Driver") - 建立连接:
DriverManager.getConnection() - 创建Statement:
connection.createStatement() - 执行SQL:
statement.executeQuery() - 处理结果:遍历
ResultSet - 关闭资源:释放数据库资源
4. JDBC核心API
DriverManager用于管理JDBC驱动,建立数据库连接。
主要方法:
getConnection(String url):建立连接getConnection(String url, String user, String password):建立连接(带认证)getConnection(String url, Properties info):建立连接(使用Properties)
Connection表示数据库连接。
主要方法:
createStatement():创建Statement对象prepareStatement(String sql):创建PreparedStatement对象prepareCall(String sql):创建CallableStatement对象setAutoCommit(boolean autoCommit):设置自动提交commit():提交事务rollback():回滚事务close():关闭连接
Statement用于执行静态SQL语句。
主要方法:
executeQuery(String sql):执行查询,返回ResultSetexecuteUpdate(String sql):执行更新,返回受影响行数execute(String sql):执行SQL,返回booleanaddBatch(String sql):添加批处理SQLexecuteBatch():执行批处理close():关闭Statement
PreparedStatement是Statement的子接口,用于执行预编译SQL。
主要方法:
setInt(int parameterIndex, int x):设置int参数setString(int parameterIndex, String x):设置String参数setDate(int parameterIndex, Date x):设置Date参数executeQuery():执行查询executeUpdate():执行更新clearParameters():清空参数
ResultSet表示查询结果集。
主要方法:
next():移动到下一行getInt(String columnLabel):获取int值getString(String columnLabel):获取String值getDate(String columnLabel):获取Date值getObject(String columnLabel):获取Object值close():关闭ResultSet
5. 数据库连接
MySQL连接URL
jdbc:mysql://[host][:port]/[database][?参数列表]示例:
String url = "jdbc:mysql://localhost:3306/jdbc_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8";常用参数:
useSSL=false:禁用SSLserverTimezone=UTC:设置时区characterEncoding=utf8:设置字符编码useUnicode=true:使用Unicode编码allowPublicKeyRetrieval=true:允许公钥检索
PostgreSQL连接URL
jdbc:postgresql://[host][:port]/[database][?参数列表]示例:
String url = "jdbc:postgresql://localhost:5432/jdbc_demo";Oracle连接URL
jdbc:oracle:thin:@[host][:port]:[SID]
或
jdbc:oracle:thin:@//[host][:port]/[service_name]示例:
String url = "jdbc:oracle:thin:@localhost:1521:orcl";方式1:使用DriverManager
String url = "jdbc:mysql://localhost:3306/jdbc_demo";
String username = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, username, password);方式2:使用Properties
Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "root");
props.setProperty("useSSL", "false");
String url = "jdbc:mysql://localhost:3306/jdbc_demo";
Connection connection = DriverManager.getConnection(url, props);方式3:使用DataSource(推荐)
import com.mysql.cj.jdbc.MysqlDataSource;
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/jdbc_demo");
dataSource.setUser("root");
dataSource.setPassword("root");
Connection connection = dataSource.getConnection();package com.example.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* 数据库连接工具类
*/
public class DBUtil {
private static String url;
private static String username;
private static String password;
private static String driver;
// 静态代码块,加载配置
static {
try {
// 加载配置文件
InputStream is = DBUtil.class.getClassLoader()
.getResourceAsStream("db.properties");
Properties props = new Properties();
props.load(is);
// 读取配置
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
driver = props.getProperty("driver");
// 加载驱动
Class.forName(driver);
} catch (Exception e) {
throw new RuntimeException("加载数据库配置失败", e);
}
}
/**
* 获取数据库连接
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
/**
* 关闭连接
*/
public static void closeConnection(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}db.properties配置文件:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
username=root
password=root6. CRUD操作详解
查询所有记录
public List<User> findAll() {
List<User> users = new ArrayList<>();
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
statement = connection.createStatement();
String sql = "SELECT * FROM user";
resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
user.setEmail(resultSet.getString("email"));
user.setAge(resultSet.getInt("age"));
user.setCreateTime(resultSet.getTimestamp("create_time").toLocalDateTime());
user.setUpdateTime(resultSet.getTimestamp("update_time").toLocalDateTime());
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(resultSet, statement, connection);
}
return users;
}根据ID查询
public User findById(int id) {
User user = null;
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "SELECT * FROM user WHERE id = ?";
ps = connection.prepareStatement(sql);
ps.setInt(1, id);
resultSet = ps.executeQuery();
if (resultSet.next()) {
user = new User();
user.setId(resultSet.getInt("id"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
user.setEmail(resultSet.getString("email"));
user.setAge(resultSet.getInt("age"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(resultSet, ps, connection);
}
return user;
}条件查询
public List<User> findByCondition(String username, Integer minAge) {
List<User> users = new ArrayList<>();
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
if (username != null && !username.isEmpty()) {
sql.append(" AND username LIKE ?");
}
if (minAge != null) {
sql.append(" AND age >= ?");
}
ps = connection.prepareStatement(sql.toString());
int paramIndex = 1;
if (username != null && !username.isEmpty()) {
ps.setString(paramIndex++, "%" + username + "%");
}
if (minAge != null) {
ps.setInt(paramIndex++, minAge);
}
resultSet = ps.executeQuery();
while (resultSet.next()) {
User user = mapResultSetToUser(resultSet);
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(resultSet, ps, connection);
}
return users;
}简单插入
public int insert(User user) {
Connection connection = null;
PreparedStatement ps = null;
int result = 0;
try {
connection = DBUtil.getConnection();
String sql = "INSERT INTO user (username, password, email, age) VALUES (?, ?, ?, ?)";
ps = connection.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getEmail());
ps.setInt(4, user.getAge());
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, connection);
}
return result;
}获取自增主键
public int insertAndGetId(User user) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
int generatedId = 0;
try {
connection = DBUtil.getConnection();
String sql = "INSERT INTO user (username, password, email, age) VALUES (?, ?, ?, ?)";
ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getEmail());
ps.setInt(4, user.getAge());
ps.executeUpdate();
// 获取生成的主键
rs = ps.getGeneratedKeys();
if (rs.next()) {
generatedId = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, ps, connection);
}
return generatedId;
}public int update(User user) {
Connection connection = null;
PreparedStatement ps = null;
int result = 0;
try {
connection = DBUtil.getConnection();
String sql = "UPDATE user SET username=?, password=?, email=?, age=? WHERE id=?";
ps = connection.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getEmail());
ps.setInt(4, user.getAge());
ps.setInt(5, user.getId());
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, connection);
}
return result;
}public int deleteById(int id) {
Connection connection = null;
PreparedStatement ps = null;
int result = 0;
try {
connection = DBUtil.getConnection();
String sql = "DELETE FROM user WHERE id=?";
ps = connection.prepareStatement(sql);
ps.setInt(1, id);
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, connection);
}
return result;
}7. PreparedStatement预编译语句
优势:
- 防止SQL注入:参数化查询防止SQL注入攻击
- 性能更好:预编译SQL,执行效率更高
- 代码清晰:参数设置更直观
- 类型安全:编译时检查类型
不安全的Statement
// 危险!容易SQL注入
String username = "admin' OR '1'='1";
String sql = "SELECT * FROM user WHERE username='" + username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// 实际执行的SQL: SELECT * FROM user WHERE username='admin' OR '1'='1'
// 这会返回所有用户!安全的PreparedStatement
// 安全!防止SQL注入
String username = "admin' OR '1'='1";
String sql = "SELECT * FROM user WHERE username=?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, username);
ResultSet rs = ps.executeQuery();
// 只会查找用户名为 "admin' OR '1'='1" 的用户,不会执行恶意SQL基本类型参数
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO user (username, age, salary, active) VALUES (?, ?, ?, ?)"
);
ps.setString(1, "zhangsan"); // String
ps.setInt(2, 25); // int
ps.setDouble(3, 5000.50); // double
ps.setBoolean(4, true); // boolean
ps.setDate(5, new Date(System.currentTimeMillis())); // Date
ps.setTimestamp(6, new Timestamp(System.currentTimeMillis())); // TimestampNULL值处理
ps.setString(1, null); // 可以设置null
// 或者
ps.setNull(1, Types.VARCHAR); // 明确指定类型参数索引
// 参数索引从1开始
ps.setString(1, "first");
ps.setString(2, "second");
ps.setString(3, "third");public User findByUsernameAndPassword(String username, String password) {
User user = null;
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = DBUtil.getConnection();
String sql = "SELECT * FROM user WHERE username=? AND password=?";
ps = connection.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
rs = ps.executeQuery();
if (rs.next()) {
user = mapResultSetToUser(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, ps, connection);
}
return user;
}8. 事务管理
事务是一组SQL操作的集合,要么全部成功,要么全部失败。
ACID特性:
- 原子性(Atomicity):事务是不可分割的
- 一致性(Consistency):事务前后数据保持一致
- 隔离性(Isolation):事务之间相互隔离
- 持久性(Durability):事务提交后数据永久保存
Connection connection = null;
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
try {
connection = DBUtil.getConnection();
// 1. 关闭自动提交
connection.setAutoCommit(false);
// 2. 执行多个SQL操作
String sql1 = "UPDATE account SET balance=balance-? WHERE id=?";
ps1 = connection.prepareStatement(sql1);
ps1.setDouble(1, 100.0);
ps1.setInt(2, 1);
ps1.executeUpdate();
String sql2 = "UPDATE account SET balance=balance+? WHERE id=?";
ps2 = connection.prepareStatement(sql2);
ps2.setDouble(1, 100.0);
ps2.setInt(2, 2);
ps2.executeUpdate();
// 3. 提交事务
connection.commit();
System.out.println("转账成功");
} catch (SQLException e) {
// 4. 回滚事务
if (connection != null) {
try {
connection.rollback();
System.out.println("转账失败,已回滚");
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 5. 恢复自动提交
if (connection != null) {
try {
connection.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
}
DBUtil.close(null, ps2, null);
DBUtil.close(null, ps1, connection);
}public boolean transfer(int fromId, int toId, double amount) {
Connection connection = null;
PreparedStatement ps1 = null;
PreparedStatement ps2 = null;
try {
connection = DBUtil.getConnection();
connection.setAutoCommit(false);
// 扣款
String sql1 = "UPDATE account SET balance=balance-? WHERE id=? AND balance>=?";
ps1 = connection.prepareStatement(sql1);
ps1.setDouble(1, amount);
ps1.setInt(2, fromId);
ps1.setDouble(3, amount);
int rows1 = ps1.executeUpdate();
if (rows1 == 0) {
throw new SQLException("余额不足");
}
// 加款
String sql2 = "UPDATE account SET balance=balance+? WHERE id=?";
ps2 = connection.prepareStatement(sql2);
ps2.setDouble(1, amount);
ps2.setInt(2, toId);
ps2.executeUpdate();
connection.commit();
return true;
} catch (SQLException e) {
if (connection != null) {
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
return false;
} finally {
if (connection != null) {
try {
connection.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
}
DBUtil.close(null, ps2, null);
DBUtil.close(null, ps1, connection);
}
}// 设置事务隔离级别
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
// 隔离级别常量:
// TRANSACTION_NONE:不支持事务
// TRANSACTION_READ_UNCOMMITTED:读未提交(最低)
// TRANSACTION_READ_COMMITTED:读已提交
// TRANSACTION_REPEATABLE_READ:可重复读
// TRANSACTION_SERIALIZABLE:串行化(最高)9. 结果集处理
遍历结果集
ResultSet rs = statement.executeQuery("SELECT * FROM user");
// 方式1:使用列名
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
int age = rs.getInt("age");
}
// 方式2:使用列索引(从1开始)
while (rs.next()) {
int id = rs.getInt(1);
String username = rs.getString(2);
int age = rs.getInt(5);
}获取不同类型的数据
while (rs.next()) {
// 基本类型
int id = rs.getInt("id");
String name = rs.getString("username");
double salary = rs.getDouble("salary");
boolean active = rs.getBoolean("active");
// 日期时间
Date date = rs.getDate("create_time");
Time time = rs.getTime("create_time");
Timestamp timestamp = rs.getTimestamp("create_time");
// 对象
Object obj = rs.getObject("column_name");
// 流
InputStream is = rs.getBinaryStream("blob_column");
Reader reader = rs.getCharacterStream("text_column");
}ResultSet rs = statement.executeQuery("SELECT * FROM user");
ResultSetMetaData metaData = rs.getMetaData();
// 获取列数
int columnCount = metaData.getColumnCount();
// 获取列信息
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
String columnType = metaData.getColumnTypeName(i);
int columnSize = metaData.getColumnDisplaySize(i);
boolean nullable = metaData.isNullable(i) == ResultSetMetaData.columnNullable;
System.out.println("列名: " + columnName +
", 类型: " + columnType +
", 大小: " + columnSize +
", 可空: " + nullable);
}// 创建可滚动、可更新的Statement
Statement stmt = connection.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
// 移动到第一行
rs.first();
// 移动到最后一行
rs.last();
// 移动到指定行
rs.absolute(3);
// 相对移动
rs.relative(2); // 向后移动2行
rs.relative(-1); // 向前移动1行
// 检查位置
boolean isFirst = rs.isFirst();
boolean isLast = rs.isLast();
int currentRow = rs.getRow();// 创建可更新结果集
Statement stmt = connection.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
// 更新当前行
if (rs.next()) {
rs.updateString("username", "new_username");
rs.updateInt("age", 30);
rs.updateRow(); // 提交更新
}
// 插入新行
rs.moveToInsertRow();
rs.updateString("username", "new_user");
rs.updateString("password", "123456");
rs.updateInt("age", 25);
rs.insertRow(); // 插入行
// 删除当前行
rs.deleteRow();10. 数据库元数据
Connection connection = DBUtil.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
// 数据库信息
String dbName = metaData.getDatabaseProductName();
String dbVersion = metaData.getDatabaseProductVersion();
String driverName = metaData.getDriverName();
String driverVersion = metaData.getDriverVersion();
// 数据库功能支持
boolean supportsTransactions = metaData.supportsTransactions();
boolean supportsBatchUpdates = metaData.supportsBatchUpdates();
boolean supportsStoredProcedures = metaData.supportsStoredProcedures();
System.out.println("数据库: " + dbName + " " + dbVersion);
System.out.println("驱动: " + driverName + " " + driverVersion);DatabaseMetaData metaData = connection.getMetaData();
// 获取所有表
ResultSet tables = metaData.getTables(null, null, "%", new String[]{"TABLE"});
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
String tableType = tables.getString("TABLE_TYPE");
System.out.println("表名: " + tableName + ", 类型: " + tableType);
}
// 获取表的列信息
ResultSet columns = metaData.getColumns(null, null, "user", null);
while (columns.next()) {
String columnName = columns.getString("COLUMN_NAME");
String dataType = columns.getString("TYPE_NAME");
int columnSize = columns.getInt("COLUMN_SIZE");
int nullable = columns.getInt("NULLABLE");
System.out.println("列名: " + columnName +
", 类型: " + dataType +
", 大小: " + columnSize +
", 可空: " + (nullable == 1 ? "是" : "否"));
}
// 获取主键信息
ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, "user");
while (primaryKeys.next()) {
String pkName = primaryKeys.getString("COLUMN_NAME");
System.out.println("主键: " + pkName);
}11. 连接池
问题:
- 频繁创建和关闭连接开销大
- 连接数量有限
- 连接管理复杂
解决方案:
- 预先创建连接
- 复用连接
- 统一管理连接
添加依赖
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>配置连接池
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class HikariCPUtil {
private static HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc_demo");
config.setUsername("root");
config.setPassword("root");
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池配置
config.setMinimumIdle(5); // 最小空闲连接数
config.setMaximumPoolSize(20); // 最大连接数
config.setConnectionTimeout(30000); // 连接超时时间(毫秒)
config.setIdleTimeout(600000); // 空闲连接超时时间
config.setMaxLifetime(1800000); // 连接最大生存时间
config.setLeakDetectionThreshold(60000); // 连接泄漏检测阈值
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void closeDataSource() {
if (dataSource != null) {
dataSource.close();
}
}
}添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.18</version>
</dependency>配置连接池
import com.alibaba.druid.pool.DruidDataSource;
public class DruidUtil {
private static DruidDataSource dataSource;
static {
dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/jdbc_demo");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池配置
dataSource.setInitialSize(5); // 初始连接数
dataSource.setMinIdle(5); // 最小空闲连接数
dataSource.setMaxActive(20); // 最大连接数
dataSource.setMaxWait(60000); // 获取连接最大等待时间
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void closeDataSource() {
if (dataSource != null) {
dataSource.close();
}
}
}12. 批量操作
Connection connection = DBUtil.getConnection();
Statement stmt = connection.createStatement();
// 添加批量SQL
stmt.addBatch("INSERT INTO user (username, password) VALUES ('user1', 'pass1')");
stmt.addBatch("INSERT INTO user (username, password) VALUES ('user2', 'pass2')");
stmt.addBatch("INSERT INTO user (username, password) VALUES ('user3', 'pass3')");
// 执行批量操作
int[] results = stmt.executeBatch();
// 清空批量操作
stmt.clearBatch();
stmt.close();
connection.close();Connection connection = DBUtil.getConnection();
String sql = "INSERT INTO user (username, password, email, age) VALUES (?, ?, ?, ?)";
PreparedStatement ps = connection.prepareStatement(sql);
// 添加批量操作
for (int i = 1; i <= 1000; i++) {
ps.setString(1, "user" + i);
ps.setString(2, "pass" + i);
ps.setString(3, "user" + i + "@example.com");
ps.setInt(4, 20 + i % 50);
ps.addBatch();
// 每100条执行一次
if (i % 100 == 0) {
ps.executeBatch();
ps.clearBatch();
}
}
// 执行剩余的批量操作
ps.executeBatch();
ps.clearBatch();
ps.close();
connection.close();public int[] batchInsert(List<User> users) {
Connection connection = null;
PreparedStatement ps = null;
int[] results = null;
try {
connection = DBUtil.getConnection();
connection.setAutoCommit(false);
String sql = "INSERT INTO user (username, password, email, age) VALUES (?, ?, ?, ?)";
ps = connection.prepareStatement(sql);
for (User user : users) {
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getEmail());
ps.setInt(4, user.getAge());
ps.addBatch();
}
results = ps.executeBatch();
connection.commit();
} catch (SQLException e) {
if (connection != null) {
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
}
DBUtil.close(null, ps, connection);
}
return results;
}13. 存储过程调用
DELIMITER //
CREATE PROCEDURE get_user_by_id(IN user_id INT)
BEGIN
SELECT * FROM user WHERE id = user_id;
END //
DELIMITER ;Connection connection = DBUtil.getConnection();
CallableStatement cs = connection.prepareCall("{call get_user_by_id(?)}");
cs.setInt(1, 1);
ResultSet rs = cs.executeQuery();
while (rs.next()) {
// 处理结果
}
rs.close();
cs.close();
connection.close();DELIMITER //
CREATE PROCEDURE get_user_count(OUT total_count INT)
BEGIN
SELECT COUNT(*) INTO total_count FROM user;
END //
DELIMITER ;Connection connection = DBUtil.getConnection();
CallableStatement cs = connection.prepareCall("{call get_user_count(?)}");
cs.registerOutParameter(1, Types.INTEGER);
cs.execute();
int count = cs.getInt(1);
System.out.println("用户总数: " + count);
cs.close();
connection.close();14. 实际应用案例
package com.example.dao;
import com.example.entity.User;
import com.example.util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class UserDao {
/**
* 查询所有用户
*/
public List<User> findAll() {
List<User> users = new ArrayList<>();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM user";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
users.add(mapResultSetToUser(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, ps, conn);
}
return users;
}
/**
* 根据ID查询用户
*/
public User findById(int id) {
User user = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM user WHERE id=?";
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
rs = ps.executeQuery();
if (rs.next()) {
user = mapResultSetToUser(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, ps, conn);
}
return user;
}
/**
* 插入用户
*/
public int insert(User user) {
Connection conn = null;
PreparedStatement ps = null;
int result = 0;
try {
conn = DBUtil.getConnection();
String sql = "INSERT INTO user (username, password, email, age) VALUES (?, ?, ?, ?)";
ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getEmail());
ps.setInt(4, user.getAge());
result = ps.executeUpdate();
// 获取生成的主键
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
user.setId(rs.getInt(1));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, conn);
}
return result;
}
/**
* 更新用户
*/
public int update(User user) {
Connection conn = null;
PreparedStatement ps = null;
int result = 0;
try {
conn = DBUtil.getConnection();
String sql = "UPDATE user SET username=?, password=?, email=?, age=? WHERE id=?";
ps = conn.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setString(3, user.getEmail());
ps.setInt(4, user.getAge());
ps.setInt(5, user.getId());
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, conn);
}
return result;
}
/**
* 删除用户
*/
public int deleteById(int id) {
Connection conn = null;
PreparedStatement ps = null;
int result = 0;
try {
conn = DBUtil.getConnection();
String sql = "DELETE FROM user WHERE id=?";
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(null, ps, conn);
}
return result;
}
/**
* 将ResultSet映射为User对象
*/
private User mapResultSetToUser(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setEmail(rs.getString("email"));
user.setAge(rs.getInt("age"));
Timestamp createTime = rs.getTimestamp("create_time");
if (createTime != null) {
user.setCreateTime(createTime.toLocalDateTime());
}
Timestamp updateTime = rs.getTimestamp("update_time");
if (updateTime != null) {
user.setUpdateTime(updateTime.toLocalDateTime());
}
return user;
}
}package com.example.util;
import java.sql.*;
public class DBUtil {
/**
* 关闭ResultSet
*/
public static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭Statement
*/
public static void close(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭Connection
*/
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭多个资源
*/
public static void close(ResultSet rs, Statement stmt, Connection conn) {
close(rs);
close(stmt);
close(conn);
}
/**
* 关闭多个资源(无ResultSet)
*/
public static void close(Statement stmt, Connection conn) {
close(stmt);
close(conn);
}
}15. 常见问题与最佳实践
问题1:ClassNotFoundException
原因: 未加载数据库驱动或驱动jar包未添加到classpath
解决:
// 确保驱动类存在
Class.forName("com.mysql.cj.jdbc.Driver");
// 或使用新版本(Java 6+自动加载)
// 不需要显式加载驱动问题2:SQLException: No suitable driver
原因: 连接URL格式错误或驱动未加载
解决:
// 检查URL格式
String url = "jdbc:mysql://localhost:3306/database_name";
// 确保驱动jar包在classpath中问题3:连接超时
原因: 数据库服务未启动或网络问题
解决:
- 检查数据库服务是否启动
- 检查网络连接
- 增加连接超时时间
问题4:字符编码问题
原因: 数据库字符集与Java字符集不一致
解决:
// URL中添加字符编码参数
String url = "jdbc:mysql://localhost:3306/db?characterEncoding=utf8&useUnicode=true";// ✅ 推荐:使用PreparedStatement
PreparedStatement ps = connection.prepareStatement("SELECT * FROM user WHERE id=?");
ps.setInt(1, userId);
// ❌ 不推荐:使用Statement拼接SQL
Statement stmt = connection.createStatement();
stmt.executeQuery("SELECT * FROM user WHERE id=" + userId);// ✅ 推荐:使用try-with-resources
try (Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
// 处理结果
}
// ❌ 不推荐:手动关闭(容易遗漏)
Connection conn = DBUtil.getConnection();
// ... 使用连接
conn.close(); // 可能忘记关闭// ✅ 推荐:使用连接池
Connection conn = HikariCPUtil.getConnection();
// ❌ 不推荐:每次都创建新连接
Connection conn = DriverManager.getConnection(url, user, pass);// ✅ 推荐:正确的事务管理
connection.setAutoCommit(false);
try {
// 执行多个操作
connection.commit();
} catch (Exception e) {
connection.rollback();
} finally {
connection.setAutoCommit(true);
}// ✅ 推荐:详细的异常处理
try {
// 数据库操作
} catch (SQLException e) {
logger.error("数据库操作失败", e);
// 根据异常类型处理
if (e.getSQLState().startsWith("23")) {
// 处理约束违反
}
}// ✅ 推荐:统一的资源管理
public class DBUtil {
public static void close(AutoCloseable... resources) {
for (AutoCloseable resource : resources) {
if (resource != null) {
try {
resource.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
// 使用
DBUtil.close(rs, ps, conn);16. 总结与进阶
通过本教程,你已经掌握了:
- ✅ JDBC基本概念和架构
- ✅ 数据库连接的建立和管理
- ✅ CRUD操作的实现
- ✅ PreparedStatement的使用
- ✅ 事务管理
- ✅ 结果集处理
- ✅ 连接池的使用
- ✅ 批量操作和存储过程
ORM框架
- MyBatis:半自动ORM框架
- Hibernate:全自动ORM框架
- JPA:Java持久化API
数据库设计
- 数据库范式
- 索引优化
- 查询优化
连接池深入
- 连接池原理
- 性能调优
- 监控和管理
分布式事务
- JTA事务
- 两阶段提交
- 分布式事务管理
- 官方文档:Oracle JDBC文档
- 数据库文档:MySQL、PostgreSQL官方文档
- 连接池文档:HikariCP、Druid文档
- 书籍推荐:
- 《Java数据库编程宝典》
- 《高性能MySQL》
- 多练习:完成各种CRUD操作
- 性能优化:学习SQL优化和连接池调优
- 框架学习:学习MyBatis、Hibernate等ORM框架
- 项目实践:在实际项目中应用JDBC知识
结语
JDBC是Java数据库编程的基础,掌握JDBC对于Java开发者来说非常重要。通过本教程的学习,相信你已经掌握了JDBC的核心知识。
记住:
- 多实践:理论结合实践,多写代码
- 注意安全:使用PreparedStatement防止SQL注入
- 资源管理:及时关闭数据库资源
- 性能优化:合理使用连接池和批量操作
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。