Java 数据库连接全教程
本教程将带你学习如何使用 Java 连接到关系型数据库(以 MySQL 为例),并执行基本的增、删、改、查操作,我们将遵循技术发展的脉络,从最基础的 JDBC 开始,逐步过渡到更现代、更便捷的 JPA 和 Spring Data JPA。

目录
-
第一部分:基础篇 - JDBC (Java Database Connectivity)
- 1 什么是 JDBC?
- 2 环境准备
- 3 JDBC 连接数据库的 6 个步骤
- 4 执行 SQL 语句
- 5 使用
PreparedStatement防止 SQL 注入 - 6 完整的 JDBC 增删改查 示例
- 7 JDBC 的优缺点
-
第二部分:进阶篇 - 连接池
- 1 为什么需要连接池?
- 2 主流连接池介绍
- 3 使用 HikariCP 连接池
-
第三部分:现代篇 - ORM 与 JPA
- 1 什么是 ORM?什么是 JPA?
- 2 核心概念(实体、主键、映射等)
- 3 使用 JPA 进行 CRUD 操作
- 4 JPA 的实现:Hibernate
-
第四部分:企业级篇 - Spring Data JPA
(图片来源网络,侵删)- 1 Spring Data JPA 简介
- 2 快速上手:创建一个 Spring Boot 项目
- 3 定义 Repository 接口
- 4 编写 Service 和 Controller
- 5 测试与总结
第一部分:基础篇 - JDBC (Java Database Connectivity)
1 什么是 JDBC?
JDBC (Java Database Connectivity) 是 Java API 的一部分,它定义了一套标准,允许 Java 程序与各种数据库进行交互,你可以把它想象成一座桥梁,Java 应用程序通过这座桥梁来“驾驶” SQL 语句,在数据库这个“仓库”里存取数据。
JDBC 本身只是一个接口规范,具体的实现由各个数据库厂商(如 Oracle, MySQL, Microsoft)提供,这些实现被称为数据库驱动。
2 环境准备
-
安装 JDK: 确保你的电脑上安装了 Java 开发工具包 (JDK)。
-
安装数据库: 本教程以 MySQL 为例,请确保你已经安装并启动了 MySQL 数据库服务。
(图片来源网络,侵删) -
创建数据库和表:
-- 创建一个名为 `java_test` 的数据库 CREATE DATABASE java_test; -- 使用该数据库 USE java_test; -- 创建一个 `users` 表 CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(50) NOT NULL, email VARCHAR(100) ); -
下载 MySQL 驱动: JDBC 驱动是 Java 连接 MySQL 的桥梁,你需要下载对应的
.jar文件。- 访问 Maven Central Repository。
- 搜索 "mysql-connector-j"。
- 下载最新版本的
.jar文件。 - 在 IDE 中使用: 如果你使用 IntelliJ IDEA 或 Eclipse,可以直接在项目中添加依赖(推荐),在 Maven 项目的
pom.xml中添加:<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> <!-- 使用你下载的对应版本 --> </dependency>
3 JDBC 连接数据库的 6 个步骤
这是 JDBC 编程的经典流程,务必牢记。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JdbcConnectionDemo {
public static void main(String[] args) {
// 1. 定义数据库连接信息
String url = "jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC";
String user = "root"; // 你的数据库用户名
String password = "your_password"; // 你的数据库密码
Connection conn = null;
try {
// 2. 加载并注册 JDBC 驱动 (对于较新版本的驱动,此步骤可以省略)
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 获取数据库连接对象
System.out.println("正在连接数据库...");
conn = DriverManager.getConnection(url, user, password);
System.out.println("数据库连接成功!");
// 4. 后续操作... (创建 Statement, 执行 SQL 等)
} catch (ClassNotFoundException e) {
System.err.println("找不到 JDBC 驱动类!");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("数据库连接失败或 SQL 执行出错!");
e.printStackTrace();
} finally {
// 5. 关闭连接 (非常重要!)
if (conn != null) {
try {
conn.close();
System.out.println("数据库连接已关闭。");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
步骤详解:
- URL: 数据库的地址,格式为
jdbc:mysql://主机名:端口/数据库名?参数。localhost: 本地主机。3306: MySQL 的默认端口。java_test: 你创建的数据库名。useSSL=false: 禁用 SSL(开发环境常用)。serverTimezone=UTC: 设置服务器时区,避免警告。
- 加载驱动:
Class.forName("...")会加载驱动类,并自动向DriverManager注册,在 Java 6+ 后,驱动会自动注册,所以这行代码在很多情况下可以省略,但保留它是好的习惯。 - 获取连接:
DriverManager.getConnection()是核心方法,它会尝试使用已注册的驱动来建立连接。 - 执行操作: 稍后会详细说明。
- 关闭连接: 资源必须关闭!
Connection,Statement,ResultSet等都是 JDBC 资源,不关闭会导致数据库连接泄漏,最终可能导致系统崩溃。finally块能确保即使发生异常,资源也会被尝试关闭。
4 执行 SQL 语句
获取连接后,我们需要一个“工具”来执行 SQL,这个工具就是 Statement 或其子类 PreparedStatement。
Statement 用于执行静态 SQL 语句
// 假设 conn 已经成功获取
Statement stmt = null;
try {
// 创建 Statement 对象
stmt = conn.createStatement();
// 1. 执行 DML (数据操作语言): INSERT, UPDATE, DELETE
String sql1 = "INSERT INTO users (username, password, email) VALUES ('zhangsan', '123456', 'zhangsan@example.com')";
int affectedRows = stmt.executeUpdate(sql1); // 返回受影响的行数
System.out.println("插入了 " + affectedRows + " 行数据。");
// 2. 执行 DQL (数据查询语言): SELECT
String sql2 = "SELECT * FROM users";
ResultSet rs = stmt.executeQuery(sql2); // 返回一个 ResultSet 结果集
// 3. 遍历结果集
while (rs.next()) { // rs.next() 将光标移动到下一行,如果存在则返回 true
// 通过列名或列索引获取数据
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
System.out.println("ID: " + id + ", 用户名: " + username + ", 邮箱: " + email);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭 Statement
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
ResultSet: 查询结果是一个指向数据行的“光标”。rs.next() 方法用于移动光标,常用获取数据的方法:
rs.getInt("列名")rs.getString("列名")rs.getObject("列名")(通用方法)
5 使用 PreparedStatement 防止 SQL 注入
Statement 拼接 SQL 的方式非常危险,容易遭受 SQL 注入 攻击。
String username = "admin"; String password = "' OR '1'='1"; // 恶意输入 String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"; // 拼接后的 SQL 变成: SELECT * FROM users WHERE username = 'admin' AND password = '' OR '1'='1' // 这会永远返回结果,导致任何人都能以 admin 身份登录。
解决方案:使用 PreparedStatement
PreparedStatement 是 Statement 的子类,它预编译 SQL 语句,并使用 作为参数占位符,这种方式更安全、更高效。
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
PreparedStatement pstmt = null;
try {
// 创建 PreparedStatement 对象
pstmt = conn.prepareStatement(sql);
// 设置参数 (索引从 1 开始)
pstmt.setString(1, "lisi");
pstmt.setString(2, "654321");
pstmt.setString(3, "lisi@example.com");
// 执行
int affectedRows = pstmt.executeUpdate();
System.out.println("使用 PreparedStatement 插入了 " + affectedRows + " 行数据。");
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
6 完整的 JDBC 增删改查 示例
这是一个整合了所有步骤的完整工具类。
import java.sql.*;
public class JdbcUtils {
private static final String URL = "jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASSWORD = "your_password";
// 1. 获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
// 2. 释放资源
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if (conn != null) {
try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
}
// 3. 通用增删改方法
public static int update(String sql, Object... params) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
return pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn, pstmt, null);
}
return 0;
}
// 4. 通用查询方法
public static void query(String sql, Object... params) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
rs = pstmt.executeQuery();
// 处理结果集 (这里简单打印)
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
while (rs.next()) {
for (int i = 1; i <= columnCount; i++) {
System.out.print(metaData.getColumnName(i) + ": " + rs.getObject(i) + "\t");
}
System.out.println();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(conn, pstmt, rs);
}
}
}
7 JDBC 的优缺点
- 优点:
- 标准: 作为 Java 标准,所有数据库都支持。
- 灵活: 可以执行任何复杂的 SQL 语句。
- 无额外依赖: 只需一个驱动 Jar 包。
- 缺点:
- 繁琐: 代码模板化严重,样板代码多。
- 手动映射: 需要手动从
ResultSet中取数据并封装到 Java 对象中,容易出错。 - 性能问题: 频繁创建和关闭连接开销大。
- SQL 与代码耦合: SQL 语句硬编码在 Java 代码中。
第二部分:进阶篇 - 连接池
1 为什么需要连接池?
JDBC 的性能瓶颈主要在于创建和关闭连接的过程,每次创建连接都需要进行 TCP 握手、身份验证等,这是一个相对耗时的操作。
连接池是一个管理数据库连接的缓存对象,它预先创建一定数量的连接,当应用程序需要连接时,从池中获取一个,用完后归还给池,而不是销毁,这极大地提高了性能和响应速度。
2 主流连接池介绍
- HikariCP: 目前性能最好的连接池,被 Spring Boot 2.x+ 默认采用,轻量、快速、稳定。
- Druid: 阿里巴巴开源,功能强大,除了高性能,还提供了强大的监控功能。
- C3P0: 一个老牌的连接池,性能稳定,但相比前两者稍慢。
3 使用 HikariCP 连接池
-
添加依赖:
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> <!-- 使用最新版本 --> </dependency> -
配置和使用: 我们将使用一个配置文件
db.properties来管理连接信息,使代码更清晰。db.properties:jdbcUrl=jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC username=root password=your_password driverClassName=com.mysql.cj.jdbc.Driver poolName=MyHikariPool maximumPoolSize=10 minimumIdle=5 connectionTimeout=30000
JdbcPoolDemo.java:import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.sql.Connection; import java.sql.SQLException; import java.io.InputStream; import java.util.Properties; public class JdbcPoolDemo { private static HikariDataSource dataSource; static { try (InputStream input = JdbcPoolDemo.class.getClassLoader().getResourceAsStream("db.properties")) { Properties props = new Properties(); props.load(input); HikariConfig config = new HikariConfig(); config.setJdbcUrl(props.getProperty("jdbcUrl")); config.setUsername(props.getProperty("username")); config.setPassword(props.getProperty("password")); config.setDriverClassName(props.getProperty("driverClassName")); config.setPoolName(props.getProperty("poolName")); config.setMaximumPoolSize(Integer.parseInt(props.getProperty("maximumPoolSize"))); config.setMinimumIdle(Integer.parseInt(props.getProperty("minimumIdle"))); config.setConnectionTimeout(Long.parseLong(props.getProperty("connectionTimeout"))); dataSource = new HikariDataSource(config); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Failed to initialize HikariCP datasource", e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void main(String[] args) { try (Connection conn = getConnection()) { System.out.println("从连接池成功获取连接: " + conn); // 执行你的数据库操作... } catch (SQLException e) { e.printStackTrace(); } } }使用连接池后,获取连接的方式变为
dataSource.getConnection(),关闭连接时调用conn.close()实际上是将连接归还给连接池。
第三部分:现代篇 - ORM 与 JPA
1 什么是 ORM?什么是 JPA?
- ORM (Object-Relational Mapping): 对象关系映射,是一种编程技术,用于将面向对象模型与关系型数据库的数据结构进行映射,它允许开发者使用面向对象的方式操作数据库,而无需编写复杂的 SQL 语句。
- JPA (Java Persistence API): Java 持久化 API,这是 Java EE 提供的一套规范(接口),用于对象持久化,它定义了如何将 Java 对象映射到数据库表,以及如何进行增删改查等操作。
核心思想: 让开发者从繁琐的 JDBC 和 SQL 中解放出来,专注于业务逻辑,你不再需要写 INSERT INTO users ...,而是直接调用 user.save()。
JPA 的一个著名实现是 Hibernate。
2 核心概念
- 实体: 一个与数据库表映射的 Java 类,使用
@Entity注解标记。@Entity public class User { // ... } - 主键: 数据库表的主键,使用
@Id注解标记。@Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增策略 private Long id;
- 映射: 将 Java 对象的属性映射到表的列。
@Column: 指定列名、长度等。@Table: 指定对应的表名。@GeneratedValue: 指定主键生成策略。
3 使用 JPA 进行 CRUD 操作
-
添加依赖:
<!-- JPA API --> <dependency> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> <version>3.1.0</version> </dependency> <!-- Hibernate 实现 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>6.2.5.Final</version> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency> -
创建实体类
User.java:import jakarta.persistence.*; import java.io.Serializable; @Entity // 声明这是一个实体类 @Table(name = "users") // 指定对应的表名 public class User implements Serializable { // 实现 Serializable 是一个好习惯 @Id // 声明这是主键 @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增策略 @Column(name = "id") private Long id; @Column(name = "username", nullable = false, length = 50) private String username; @Column(name = "password", nullable = false, length = 50) private String password; @Column(name = "email", length = 100) private String email; // Getters, Setters, and Constructors (省略...) } -
配置
persistence.xml: 在src/main/resources/META-INF目录下创建persistence.xml文件。<persistence version="3.0" xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"> <persistence-unit name="myPersistenceUnit" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.HibernatePersistenceProvider</provider> <properties> <!-- 数据库连接信息 --> <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC"/> <property name="jakarta.persistence.jdbc.user" value="root"/> <property name="jakarta.persistence.jdbc.password" value="your_password"/> <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/> <!-- Hibernate 特定配置 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <!-- 自动更新表结构 --> </properties> </persistence-unit> </persistence> -
使用
EntityManager进行 CRUD:EntityManager是 JPA 的核心接口,用于管理实体对象的生命周期。import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; import java.util.List; public class JpaDemo { public static void main(String[] args) { // 1. 创建 EntityManagerFactory (通常只创建一次) EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit"); // 2. 创建 EntityManager EntityManager em = emf.createEntityManager(); try { // 开启事务 em.getTransaction().begin(); // --- C (Create) --- User newUser = new User(); newUser.setUsername("wangwu"); newUser.setPassword("789"); newUser.setEmail("wangwu@example.com"); em.persist(newUser); // 将实体对象持久化到数据库 System.out.println("新增用户 ID: " + newUser.getId()); // --- R (Read) --- User user = em.find(User.class, 1L); // 根据主键查询 if (user != null) { System.out.println("查找到用户: " + user.getUsername()); } // --- U (Update) --- if (user != null) { user.setEmail("new_email@example.com"); em.merge(user); // 合并修改 System.out.println("更新用户邮箱: " + user.getEmail()); } // --- D (Delete) --- User userToDelete = em.find(User.class, 1L); if (userToDelete != null) { em.remove(userToDelete); // 从数据库中删除 System.out.println("删除用户 ID: " + 1L); } // 提交事务 em.getTransaction().commit(); } catch (Exception e) { em.getTransaction().rollback(); // 出错则回滚 e.printStackTrace(); } finally { // 关闭资源 em.close(); emf.close(); } } }
第四部分:企业级篇 - Spring Data JPA
JPA 虽然比 JDBC 好用,但仍然需要编写 EntityManager 和事务管理代码。Spring Data JPA 在 JPA 的基础上,通过约定优于配置的理念,极大地简化了数据访问层的开发。
1 Spring Data JPA 简介
它是 Spring 框架的一部分,核心思想是:你只需要定义一个接口,继承 Repository 或其子接口,Spring 就会自动为你实现这个接口中定义的方法(如 save(), findById(), findAll() 等),你无需编写任何实现代码。
2 快速上手:创建一个 Spring Boot 项目
-
使用 Spring Initializr 创建项目。
-
选择以下依赖:
Spring Web: 用于构建 Web 应用。Spring Data JPA: 核心 JPA 支持。MySQL Driver: MySQL 数据库驱动。Lombok(可选): 简化 Java 代码。
-
配置
application.properties:# DataSource Configuration spring.datasource.url=jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # JPA and Hibernate Configuration spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.format_sql=true
3 定义 Repository 接口
-
创建实体类
User.java:import jakarta.persistence.*; import lombok.Data; @Data // Lombok 注解,自动生成 getter, setter, toString 等 @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; private String email; } -
创建 Repository 接口
UserRepository.java:import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; // JpaRepository<实体类型, 主键类型> // Spring Data JPA 会自动为这个接口创建代理对象 public interface UserRepository extends JpaRepository<User, Long> { // 自定义查询方法,Spring Data JPA 会根据方法名自动生成 SQL Optional<User> findByUsername(String username); }JpaRepository已经提供了大量现成的 CRUD 方法,如save(),findById(),findAll(),deleteById()等。
4 编写 Service 和 Controller
UserService.java:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service // 标记为 Spring 服务层组件
public class UserService {
@Autowired // 自动注入 UserRepository
private UserRepository userRepository;
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
public User createUser(User user) {
return userRepository.save(user);
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
UserController.java:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController // 标记为 REST 控制器
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
5 测试与总结
你可以启动你的 Spring Boot 应用,并使用 Postman 或其他 API 工具来测试这些接口了。
GET /api/users: 获取所有用户GET /api/users/1: 根据 ID 获取用户POST /api/users: 创建一个新用户 (Body 中传入 JSON)DELETE /api/users/1: 根据 ID 删除用户
总结与演进路线
| 技术 | 核心概念 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| JDBC | Connection, Statement, ResultSet |
标准、灵活、无额外依赖 | 繁琐、易错、性能差、SQL 耦合 | 学习基础、简单脚本、无框架项目 |
| JDBC + 连接池 | DataSource (如 HikariCP) |
解决了频繁创建连接的性能问题 | 仍需处理大量样板代码 | 几乎所有需要数据库的传统 Java 项目 |
| JPA / Hibernate | EntityManager, Entity, ORM |
面向对象、减少 SQL、自动映射 | 学习曲线陡峭、复杂查询性能可能不佳、需要 XML/注解配置 | 中大型项目,追求开发效率和代码可维护性 |
| Spring Data JPA | Repository 接口、约定优于配置 |
极致简化、自动实现 CRUD、与 Spring 生态无缝集成 | 对复杂查询的支持有限,需要自定义 | 现代 Java Web 应用的首选和标准 |
学习建议:
- 必须掌握 JDBC: 理解其原理是后续所有技术的基础。
- 熟练使用连接池: 在任何实际项目中,连接池都是必需品。
- 深入理解 JPA: 理解 ORM 的思想和 JPA 的核心概念,这会让你对数据访问有更深的认识。
- 精通 Spring Data JPA: 这是目前企业级开发的主流,能极大提升你的开发效率。
希望这份详尽的教程能帮助你全面掌握 Java 数据库连接技术!
