Spring Data JDBC 入门指南 – wiki基地

文章标题:Spring Data JDBC 入门指南:简化关系型数据库访问

I. 引言

在现代企业级应用开发中,数据持久化是一个核心环节。Spring Framework 提供了多种数据访问解决方案,其中 Spring Data JDBC 为使用 JDBC 访问关系型数据库提供了一种极简且功能强大的方式。它旨在简化数据库交互,同时避免了像 JPA 那样复杂的对象关系映射(ORM)机制,让开发者能够更专注于业务逻辑。

A. 什么是 Spring Data JDBC?

Spring Data JDBC 是 Spring Data 项目家族的一部分,它为 JDBC 提供了高级抽象,使得开发者可以使用 Spring Data 的编程模型(如 Repository 接口)来操作关系型数据库。它不是一个成熟的 ORM 框架,而是更像一个“轻量级”的持久化层,专注于将聚合根(Aggregate Root)的概念引入到数据库交互中。

B. 为什么选择 Spring Data JDBC?

  • 简化性与可预测性: 相较于 JPA/Hibernate 等全功能 ORM 框架,Spring Data JDBC 提供了更直接、更可预测的映射方式。它不涉及懒加载、会话管理等复杂概念,避免了许多 JPA 中常见的“意外”行为。
  • 更接近 SQL: 对于那些更喜欢直接与 SQL 交互,或者对 ORM 框架的“魔法”感到不适的开发者来说,Spring Data JDBC 提供了一个更透明的抽象层。
  • 聚合根为中心: 它鼓励使用领域驱动设计(DDD)中的聚合根概念,确保数据的一致性和完整性。

C. 本文目标

本文将引导您从零开始,逐步学习如何使用 Spring Data JDBC 构建一个简单的数据库访问应用,涵盖项目初始化、核心概念、实体定义、Repository 接口以及基本的 CRUD 操作。

II. Spring Data JDBC 核心概念

在使用 Spring Data JDBC 之前,理解其几个核心概念至关重要。

A. 聚合根 (Aggregate Root)

在领域驱动设计中,聚合根是封装一组相关领域对象(实体和值对象)的实体,并负责维护这些对象之间的一致性。Spring Data JDBC 强烈推荐将每个表映射为一个聚合根。一个聚合根通常由一个根实体和其包含的(一对多、一对一)其他实体或值对象组成,并通过根实体进行所有外部访问。

B. 实体 (Entity)

在 Spring Data JDBC 中,实体是映射到数据库表行的普通 Java 对象。与 JPA 实体不同,它们不需要实现特定的接口或包含特定的注解(除了主键)。

C. Repository 接口 (CrudRepository)

Spring Data 提供了一系列 Repository 接口来简化数据访问层。CrudRepository 是最基本的接口,提供了基本的 CRUD (Create, Read, Update, Delete) 操作方法。通过继承它,您可以获得无需编写实现代码的数据访问功能。

D. 映射规则 (Mapping)

Spring Data JDBC 使用相对简单的映射规则:
* 表名和列名: 默认情况下,类名映射到表名(驼峰命名转下划线命名),字段名映射到列名。
* 主键: 使用 @org.springframework.data.annotation.Id 注解标记实体的主键字段。
* 嵌入式类型: 可以使用 @Embedded 注解将一个对象嵌入到实体中,其字段将作为列添加到主表中。
* 关联关系: Spring Data JDBC 对关联关系的处理更倾向于直接映射外键,而不是复杂的对象图加载。一对多关系通常通过在聚合根中包含一个集合来表示。

III. 环境搭建与项目初始化

我们将使用 Spring Boot 来快速搭建项目。

A. 使用 Spring Initializr 创建项目

访问 https://start.spring.io,创建一个新的 Spring Boot 项目。

  1. 项目元数据:

    • Project: Maven Project 或 Gradle Project (推荐 Maven)
    • Language: Java
    • Spring Boot: 选择最新的稳定版本
    • Group / Artifact: 根据您的喜好填写,例如 com.example / spring-data-jdbc-demo
  2. 依赖选择:
    在 “Dependencies” 中添加以下依赖:

    • Spring Data JDBC
    • H2 Database (作为开发环境的内存数据库,方便快速测试)
    • Lombok (可选,但强烈推荐,可以减少实体类的样板代码,如 Getter/Setter/Constructor)
  3. 点击 “Generate” 下载项目压缩包,解压并导入到您偏好的 IDE (如 IntelliJ IDEA, VS Code, Eclipse) 中。

B. 数据库配置

我们将使用 H2 内存数据库,它在应用程序关闭时会自动清除数据,非常适合开发和测试。

  1. src/main/resources/application.properties 配置 H2 内存数据库:
    properties
    spring.datasource.url=jdbc:h2:mem:testdb
    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=sa
    spring.datasource.password=
    spring.h2.console.enabled=true # 开启 H2 Console,方便查看数据库内容
    spring.sql.init.mode=always # 总是初始化数据库 Schema 和数据

    spring.sql.init.mode=always 配置将确保每次应用启动时都会执行 schema.sqldata.sql

  2. src/main/resources/schema.sql 定义表结构:
    创建 schema.sql 文件并定义一个 BOOK 表。
    sql
    CREATE TABLE IF NOT EXISTS `BOOK` (
    `ID` BIGINT AUTO_INCREMENT PRIMARY KEY,
    `TITLE` VARCHAR(255) NOT NULL,
    `AUTHOR` VARCHAR(255) NOT NULL
    );

  3. src/main/resources/data.sql 插入初始化数据 (可选):
    创建 data.sql 文件并插入一些初始数据。
    sql
    INSERT INTO `BOOK` (TITLE, AUTHOR) VALUES ('Spring Data JDBC 入门', '张三');
    INSERT INTO `BOOK` (TITLE, AUTHOR) VALUES ('深入理解 Spring Boot', '李四');

IV. 实体与 Repository 定义

接下来,我们将定义数据库表对应的 Java 实体类和用于数据访问的 Repository 接口。

A. 定义实体类 (例如:Book 类)

创建一个 Book 类,它将映射到 BOOK 表。

“`java
package com.example.springdatajdbcdemo.entity; // 请根据您的包名调整

import org.springframework.data.annotation.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data // Lombok 注解,自动生成 Getter, Setter, equals, hashCode, toString
@NoArgsConstructor // Lombok 注解,自动生成无参构造函数
@AllArgsConstructor // Lombok 注解,自动生成包含所有字段的构造函数
public class Book {
@Id // 标记为主键,注意是 org.springframework.data.annotation.Id
private Long id;
private String title;
private String author;
}
``
**注意:** 这里的
@Id注解是org.springframework.data.annotation.Id,而不是 JPA 的javax.persistence.Idjakarta.persistence.Id`。

B. 创建 Repository 接口 (例如:BookRepository)

创建一个 BookRepository 接口,继承 CrudRepository

“`java
package com.example.springdatajdbcdemo.repository; // 请根据您的包名调整

import com.example.springdatajdbcdemo.entity.Book;
import org.springframework.data.repository.CrudRepository;

public interface BookRepository extends CrudRepository {
// CrudRepository 提供了基本的 CRUD 操作,如 save(), findById(), findAll(), delete() 等。

// 除了 CrudRepository 提供的功能,Spring Data 还支持通过方法名定义查询。
// 例如,通过书名查找书籍:
Iterable<Book> findByTitle(String title);

// 还可以定义更复杂的查询,例如通过作者查找,并按标题排序:
Iterable<Book> findByAuthorOrderByTitleAsc(String author);

}
``
通过继承
CrudRepositoryBookRepository自动获得了对Book` 实体执行基本 CRUD 操作的能力,无需编写任何实现代码。

V. CRUD 操作实践

我们将通过一个 CommandLineRunner 来演示如何使用 BookRepository 进行 CRUD 操作。

在您的 Spring Boot 主应用类 (例如 SpringDataJdbcDemoApplication.java) 中添加以下 CommandLineRunner Bean:

“`java
package com.example.springdatajdbcdemo; // 请根据您的包名调整

import com.example.springdatajdbcdemo.entity.Book;
import com.example.springdatajdbcdemo.repository.BookRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringDataJdbcDemoApplication {

public static void main(String[] args) {
    SpringApplication.run(SpringDataJdbcDemoApplication.class, args);
}

@Bean
public CommandLineRunner demo(BookRepository repository) {
    return (args) -> {
        System.out.println("--- 插入初始数据(由 data.sql 完成) ---");
        repository.findAll().forEach(book -> System.out.println("现有书籍: " + book));

        // A. 创建 (Save)
        System.out.println("\n--- 添加一本新书 ---");
        Book newBook = new Book(null, "Spring Data JDBC 实践", "王五");
        repository.save(newBook);
        System.out.println("新添加书籍: " + newBook);

        // B. 读取 (Read)
        System.out.println("\n--- 查找 ID 为 1 的书籍 ---");
        repository.findById(1L).ifPresent(book -> System.out.println("找到书籍: " + book));

        System.out.println("\n--- 查找所有书籍 ---");
        repository.findAll().forEach(book -> System.out.println("所有书籍: " + book));

        System.out.println("\n--- 通过书名查找 'Spring Data JDBC 实践' ---");
        repository.findByTitle("Spring Data JDBC 实践").forEach(book -> System.out.println("按标题找到书籍: " + book));

        // C. 更新 (Update)
        System.out.println("\n--- 更新 ID 为 1 的书籍标题 ---");
        repository.findById(1L).ifPresent(book -> {
            book.setTitle("更新后的 Spring Data JDBC 入门");
            repository.save(book);
            System.out.println("更新后的书籍: " + book);
        });

        // D. 删除 (Delete)
        System.out.println("\n--- 删除 ID 为 2 的书籍 ---");
        repository.findById(2L).ifPresent(book -> {
            repository.delete(book);
            System.out.println("已删除书籍: " + book);
        });

        System.out.println("\n--- 删除操作后,再次查找所有书籍 ---");
        repository.findAll().forEach(book -> System.out.println("剩余书籍: " + book));
    };
}

}
“`

运行应用程序:

您可以通过 IDE 运行 SpringDataJdbcDemoApplicationmain 方法,或在项目根目录打开终端,执行 Maven 或 Gradle 命令:
* Maven: ./mvnw spring-boot:run
* Gradle: ./gradlew bootRun

观察控制台输出,您将看到 Spring Data JDBC 执行的各项数据库操作结果。如果开启了 H2 Console,您还可以通过浏览器访问 http://localhost:8080/h2-console (JDBC URL: jdbc:h2:mem:testdb) 查看数据库内容。

VI. Spring Data JDBC 与 JPA 的区别 (简述)

虽然 Spring Data JDBC 和 Spring Data JPA 都旨在简化数据持久化,但它们的设计理念和适用场景有所不同:

  • 简化映射 vs 完整 ORM: Spring Data JDBC 提供了更直接的数据库到对象映射,避免了复杂的对象图管理。JPA 是一个全功能的 ORM 框架,提供更丰富的对象关系映射功能(如懒加载、级联操作等)。
  • 无懒加载,无会话管理: Spring Data JDBC 在每次操作时都会从数据库中加载数据,不涉及懒加载和持久化上下文(会话)的概念,这使得其行为更容易预测。JPA 则依赖于持久化上下文和懒加载来优化性能和管理对象生命周期。
  • 更接近 SQL: Spring Data JDBC 更倾向于暴露底层 JDBC 的简单性,允许开发者对 SQL 有更多的控制。JPA 则试图将 SQL 抽象化,让开发者更多地与对象模型交互。

VII. 总结

Spring Data JDBC 提供了一种清新、简化的方式来与关系型数据库交互。它非常适合那些需要数据访问层的便捷性,但又希望避免全功能 ORM 框架复杂性的项目。通过专注于聚合根和直接映射,Spring Data JDBC 实现了高性能和可预测性。

A. 优点回顾
* 简单易用,学习曲线平缓。
* 行为可预测,没有隐式的懒加载问题。
* 与 Spring Data 生态系统无缝集成。
* 有助于强制执行领域驱动设计中的聚合根概念。

B. 适用场景
* 需要高性能且对数据库操作有精细控制的应用。
* 业务逻辑相对简单,不需要复杂对象图映射的项目。
* 领域模型与关系型模型接近,或者希望减少 ORM 阻抗失配的项目。
* 作为 Spring Data JPA 的轻量级替代方案。

C. 进一步学习资源
* Spring Data JDBC 官方文档:https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/
* Spring Data Commons 官方文档

滚动至顶部