1. 概述

Spring Data JDBC 是一个轻量级的持久化框架,相比 Spring Data JPA 更加简单直接。它没有缓存、懒加载、写后回刷(write-behind)等复杂机制,也没有 JPA 那么多“魔法”。但这不意味着它功能弱——它有自己的 ORM 模型,并支持我们熟悉的大部分 Spring Data 特性,比如:

✅ 映射实体(mapped entities)
✅ 仓库接口(repositories)
✅ 查询注解(query annotations)
✅ 原生集成 JdbcTemplate

⚠️ 但有个关键点必须注意:Spring Data JDBC 不支持数据库 schema 自动生成。这意味着你需要手动建表,不能靠 hibernate.hbm2ddl.auto 那一套。表结构得自己写 DDL 或通过脚本管理。

这也带来了好处:逻辑更透明,没有隐藏行为,适合追求可控性和性能的项目。


2. 添加 Spring Data JDBC 依赖

如果你用的是 Spring Boot,引入官方 starter 就行:

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

📌 注意:这个 starter 不会自动包含数据库驱动!你需要自己指定,比如用 H2、MySQL 或 PostgreSQL。

我们这里以 H2 内存数据库为例:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

由于 Spring Data JDBC 不生成 schema,我们可以手动创建 schema.sql 文件,放在 src/main/resources 下:

DROP TABLE IF EXISTS person;

CREATE TABLE person (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(255),
    last_name VARCHAR(255)
);

Spring Boot 启动时会自动执行这个文件,创建表结构。简单粗暴,但很可靠。


3. 定义实体类

和 Spring Data 家族其他成员一样,我们用注解把 POJO 映射到数据库表。

⚠️ 重要限制:实体类必须显式声明 @Id 字段,否则 Spring Data JDBC 无法识别主键。

默认命名策略采用驼峰转下划线:

  • Java 类 Person → 表 person
  • 属性 firstName → 列 first_name

当然你也可以用 @Table@Column 显式指定:

public class Person {
    @Id
    private long id;
    private String firstName;
    private String lastName;

    // 构造方法、getter、setter 省略
}

是不是很清爽?没有 @Entity,没有 @Table(name = "..."),除非必要,否则不用加。


4. 声明 JDBC 仓库接口

Spring Data JDBC 的仓库写法和 JPA 几乎一模一样。你可以继承以下任一接口:

  • Repository<T, ID> —— 最基础的标记接口
  • CrudRepository<T, ID> —— 提供常用增删改查方法
  • PagingAndSortingRepository<T, ID> —— 支持分页和排序

我们来定义一个 PersonRepository

@Repository 
public interface PersonRepository extends CrudRepository<Person, Long> {
}

✅ 继承后自动获得:

  • save(person)
  • findById(id)
  • deleteById(id)
  • findAll()

如果需要分页能力,直接换 PagingAndSortingRepository 就行,API 一致,无缝切换。


5. 自定义仓库方法

虽然内置方法够用,但实际开发中总要写自定义查询。Spring Data JDBC 支持两种方式:

5.1 方法名推导查询(Query Methods)

从 2.0 版本开始支持。只要方法名符合命名规范,框架会自动生成 SQL。

List<Person> findByFirstName(String firstName);

👉 框架会生成:

SELECT id, first_name, last_name FROM person WHERE first_name = ?

支持的操作符包括:And, Or, Between, Like, In, IsNull 等,基本够用。

5.2 手写 SQL 查询

复杂逻辑还是得自己写 SQL。使用 @Query 注解,并配合 @Modifying 标记更新操作:

@Repository
public interface PersonRepository extends CrudRepository<Person, Long> {

    List<Person> findByFirstName(String firstName);

    @Modifying
    @Query("UPDATE person SET first_name = :name WHERE id = :id")
    boolean updateByFirstName(@Param("id") Long id, @Param("name") String name);
}

📌 关键细节:

  • ❌ 不支持位置参数(如 ?1, ?2
  • ✅ 只支持命名参数(:paramName),必须配合 @Param 使用
  • SQL 是原生 SQL,不是 JPQL 或 HQL

⚠️ 踩坑提醒:因为是原生 SQL,一旦换数据库(比如从 MySQL 切到 Oracle),可能因方言差异导致 SQL 报错。迁移成本比 JPA 高。


6. 初始化测试数据

为了验证功能,我们需要插入几条测试数据。可以用 JdbcTemplate 手动初始化:

@Component
public class DatabaseSeeder {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insertData() {
        jdbcTemplate.execute("INSERT INTO person(first_name,last_name) VALUES('Victor', 'Hugo')");
        jdbcTemplate.execute("INSERT INTO person(first_name,last_name) VALUES('Dante', 'Alighieri')");
        jdbcTemplate.execute("INSERT INTO person(first_name,last_name) VALUES('Stefan', 'Zweig')");
        jdbcTemplate.execute("INSERT INTO person(first_name,last_name) VALUES('Oscar', 'Wilde')");
    }
}

💡 小技巧:可以在 CommandLineRunner 中调用 insertData(),应用启动时自动执行:

@Bean
CommandLineRunner init(DatabaseSeeder seeder) {
    return args -> seeder.insertData();
}

JdbcTemplate 直接操作,灵活又高效,完全掌控 SQL 执行过程。


7. 总结

Spring Data JDBC 的定位很清晰:在保留 Spring Data 编程模型的同时,去掉 JPA 的复杂性,贴近 JDBC 的性能和可控性

✅ 优势

  • 架构简单,无隐藏逻辑,学习成本低
  • 性能优于 JPA,直接与数据库通信,无一级/二级缓存开销
  • 支持熟悉的 Repository 模式,开发效率高
  • JdbcTemplate 天然集成,适合混合使用

❌ 劣势

  • 不支持 schema 自动生成,需手动维护 DDL
  • SQL 写死在代码里,数据库迁移时容易踩坑
  • 缺少延迟加载、关联映射等高级特性(但这也是设计取舍)

📌 适用场景:

  • 中小项目,追求简洁和性能
  • 团队对 SQL 熟悉,愿意手动管理 schema
  • 不想被 JPA 的“魔法”困扰,想要更透明的数据访问层

示例代码已上传至 GitHub:https://github.com/baeldung/spring-data-jdbc-example


原始标题:Introduction to Spring Data JDBC | Baeldung

» 下一篇: Java周报,346