目录
-
环境准备
(图片来源网络,侵删)- 安装 PostgreSQL 数据库
- 创建测试数据库和用户
- 准备 Java 开发环境
- 添加 PostgreSQL JDBC 驱动依赖
-
核心代码示例
- 获取数据库连接
- 执行查询 (SELECT)
- 执行更新 (INSERT, UPDATE, DELETE)
- 使用
try-with-resources确保资源关闭
-
使用连接池 (推荐)
- 为什么需要连接池?
- 使用 HikariCP (目前性能最好的连接池)
- 连接池示例代码
-
最佳实践与注意事项
- 永远不要硬编码密码
- 永远不要手动关闭连接 (连接池会处理)
- 使用
PreparedStatement防止 SQL 注入 - 正确处理异常
环境准备
a. 安装 PostgreSQL 数据库
如果您还没有安装 PostgreSQL,请从 PostgreSQL 官方网站 下载并安装,安装过程中会要求您设置一个超级用户(通常是 postgres)的密码。

b. 创建测试数据库和用户
为了方便测试,我们创建一个新的数据库和一个专门用于应用连接的用户。
-
打开终端或命令提示符。
-
使用
psql连接到 PostgreSQL:psql -U postgres
(系统可能会要求您输入
postgres用户的密码)
(图片来源网络,侵删) -
在
psql中执行以下 SQL 命令:-- 创建一个名为 'java_test_db' 的数据库 CREATE DATABASE java_test_db; -- 创建一个新用户 'java_app_user',并设置密码 CREATE USER java_app_user WITH PASSWORD 'your_strong_password'; -- 将新用户对新数据库的所有权限授予 GRANT ALL PRIVILEGES ON DATABASE java_test_db TO java_app_user; -- (可选) 将 'public' schema 的所有权限也授予该用户,方便操作 GRANT ALL ON SCHEMA public TO java_app_user;
您的测试环境就准备好了。
c. 准备 Java 开发环境
您需要一个 Java 开发环境,可以使用:
- IDE:如 IntelliJ IDEA, Eclipse, VS Code。
- 构建工具:如 Maven 或 Gradle。强烈推荐使用构建工具,它们可以自动管理依赖。
d. 添加 PostgreSQL JDBC 驱动依赖
这是最关键的一步,您需要将 PostgreSQL 的 JDBC 驱动程序(JDBC Driver,也称为 JDBC Driver 或 JDBC4 Driver)添加到您的项目中。
使用 Maven (pom.xml)
在您的 pom.xml 文件的 <dependencies> 标签中添加以下内容:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<!--
使用最新的稳定版本是一个好习惯。
您可以在 Maven Central 搜索 "postgresql" 来找到最新版本。
截至 2025 年底,42.6.x 是一个非常流行的版本。
-->
<version>42.6.0</version>
</dependency>
使用 Gradle (build.gradle)
在您的 build.gradle 文件的 dependencies 代码块中添加:
dependencies {
// 同样,推荐使用最新版本
implementation 'org.postgresql:postgresql:42.6.0'
}
添加依赖后,您的构建工具会自动下载所需的 JAR 文件。
核心代码示例
下面是使用 JDBC 连接 PostgreSQL 并执行基本操作的完整代码示例。
a. 获取数据库连接
我们需要一个 Connection 对象来与数据库建立连接。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class PostgresJdbcExample {
// 数据库连接信息 - 建议从配置文件中读取,不要硬编码!
private static final String DB_URL = "jdbc:postgresql://localhost:5432/java_test_db";
private static final String USER = "java_app_user";
private static final String PASS = "your_strong_password";
public static void main(String[] args) {
// try-with-resources 语句会自动关闭 Connection 对象
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
if (conn != null) {
System.out.println("成功连接到 PostgreSQL 数据库!");
// 在这里执行数据库操作
// createAndPopulateTable(conn);
// selectData(conn);
} else {
System.out.println("连接失败!");
}
} catch (SQLException e) {
System.err.println("数据库连接错误: " + e.getMessage());
e.printStackTrace();
}
}
}
代码解释:
DB_URL: JDBC URL 的格式为jdbc:postgresql://<host>:<port>/<database_name>。USER: 连接数据库的用户名。DriverManager.getConnection(): 这是获取连接的标准方法。try-with-resources: 这是一个 Java 7+ 的特性,它会自动调用conn.close(),即使发生异常也能确保资源被释放,强烈推荐使用。
b. 执行查询
下面是一个查询 users 表的示例,我们需要在数据库中创建一个表:
-- 在 psql 中执行
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
编写 Java 代码来查询这个表:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class PostgresJdbcExample {
// ... (DB_URL, USER, PASS 常量)
public static void selectData(Connection conn) {
// SQL 查询语句
String sql = "SELECT id, name, email, created_at FROM users";
// try-with-resources 确保 Statement 和 ResultSet 被关闭
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
System.out.println("\n--- 用户列表 ---");
// 遍历结果集
while (rs.next()) {
// 通过列名获取数据,更具可读性和健壮性
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
java.sql.Timestamp createdAt = rs.getTimestamp("created_at");
System.out.printf("ID: %d, Name: %s, Email: %s, Created At: %s%n",
id, name, email, createdAt);
}
} catch (SQLException e) {
System.err.println("查询数据时出错: " + e.getMessage());
}
}
// ... main 方法 ...
}
c. 执行更新
使用 PreparedStatement 来执行插入、更新和删除操作,这是防止 SQL 注入的最佳实践。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class PostgresJdbcExample {
// ... (DB_URL, USER, PASS 常量)
public static void insertUser(Connection conn, String name, String email) {
// 使用 ? 作为占位符
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数,索引从 1 开始
pstmt.setString(1, name);
pstmt.setString(2, email);
// 执行更新
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
System.out.println("成功插入新用户: " + name);
} else {
System.out.println("插入用户失败。");
}
} catch (SQLException e) {
System.err.println("插入数据时出错: " + e.getMessage());
}
}
// ... main 方法 ...
}
使用连接池 (强烈推荐)
在真实的应用程序中,为每个请求都创建和销毁数据库连接是非常低效的,连接池(Connection Pool)可以在应用程序启动时预先创建一组数据库连接,并重复使用它们,从而大大提高性能。
HikariCP 是目前公认性能最好、最可靠的 JDBC 连接池实现。
a. 添加 HikariCP 依赖
Maven (pom.xml):
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version> <!-- 使用最新版本 -->
</dependency>
Gradle (build.gradle):
implementation 'com.zaxxer:HikariCP:5.0.1'
b. 连接池示例代码
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class PostgresWithHikariCP {
// HikariCP 数据源
private static HikariDataSource dataSource;
// 静态代码块,在类加载时初始化连接池
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/java_test_db");
config.setUsername("java_app_user");
config.setPassword("your_strong_password");
// 连接池优化配置 (可根据实际情况调整)
config.setDriverClassName("org.postgresql.Driver");
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接数
config.setConnectionTimeout(30000); // 连接超时时间 (毫秒)
config.setIdleTimeout(600000); // 空闲连接超时时间 (毫秒)
config.setMaxLifetime(1800000); // 连接最大存活时间 (毫秒)
dataSource = new HikariDataSource(config);
}
public static void main(String[] args) {
// 从连接池获取连接
try (Connection conn = dataSource.getConnection()) {
System.out.println("从连接池成功获取连接!");
// 执行查询
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT name FROM users")) {
while (rs.next()) {
System.out.println("User: " + rs.getString("name"));
}
}
} catch (SQLException e) {
System.err.println("从连接池获取连接或执行查询时出错: " + e.getMessage());
e.printStackTrace();
}
}
// 应用程序关闭时,关闭数据源
public static void closeDataSource() {
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
}
}
}
代码解释:
HikariConfig: 用于配置连接池的各种参数。HikariDataSource: 是连接池的核心,我们只创建一个实例,并在整个应用中复用它。dataSource.getConnection(): 从连接池中获取一个连接,这个过程非常快,因为连接已经被预先创建好了。try-with-resources: 同样适用,当conn.close()被调用时,连接不会被真正关闭,而是被返回到连接池中,以供后续使用。closeDataSource(): 在应用程序(如 Web 服务器)关闭时,调用此方法来关闭整个连接池,释放所有资源。
最佳实践与注意事项
-
永远不要硬编码密码 将数据库连接信息(URL, 用户名, 密码)硬编码在代码中是非常危险的,应该使用外部配置文件(如
application.properties)、环境变量或专门的配置中心来管理。 -
永远不要手动关闭连接 (当使用连接池时) 使用连接池时,调用
connection.close()的目的是将连接归还给池,而不是销毁它,如果您手动关闭了连接,池将不知道这一点,可能导致池中的连接计数错误。 -
始终使用
PreparedStatement对于所有包含用户输入的 SQL 查询,都必须使用PreparedStatement,它可以对输入进行转义,有效防止 SQL 注入攻击,即使是静态 SQL,使用PreparedStatement通常也更高效,因为它可以被数据库预编译和缓存。 -
正确处理异常
SQLException是一个检查型异常,必须被处理或声明抛出,在 catch 块中,至少应该打印出异常信息(e.printStackTrace()或使用日志框架),以便于调试。 -
使用日志框架 在生产环境中,不要使用
System.out.println,应该使用像 SLF4J + Logback 或 Log4j2 这样的日志框架,它们提供了更强大的日志管理功能(如日志级别、文件输出等)。 -
配置合理的连接池大小 连接池的大小不是越大越好,它应该根据您的数据库服务器的性能、应用的并发量以及每个 SQL 查询的平均耗时来调整,一个常用的经验公式是:
((core_count * 2) + effective_spindle_count),但最佳值通常需要通过压力测试来确定。
