1. 概述

本文将重点介绍如何在 Spring 项目中引入 Spring Data JPA,并完整配置持久层。关于使用基于 Java 的配置方式搭建 Spring 上下文、以及项目基础的 Maven pom.xml 配置,可参考之前的文章《使用 Java 配置引导 Spring Web 应用》。

Spring Data JPA 的核心价值在于:极大简化数据访问层(DAO)的开发,让我们从繁琐的模板代码中解放出来。它的目标不仅是减少样板代码,更是实现数据访问模式和配置的一致性,降低维护成本。


2. Spring Data 自动生成 DAO —— 再也不用手写 DAO 实现类

在传统开发中,DAO 层往往充斥着大量重复的 CRUD 模板代码。虽然 Spring 早期提供了 JpaTemplate 等工具来缓解,但依然需要手动编写实现类。

Spring Data JPA 的突破在于:彻底消灭 DAO 实现类。你只需要定义一个接口,Spring Data 就能自动生成实现。

具体做法是:让 DAO 接口继承 Spring Data JPA 提供的 JpaRepository<T, ID> 接口:

public interface IFooDAO extends JpaRepository<Foo, Long> {
    // 无需实现类,Spring Data 自动提供 CRUD 实现
}

只要这样声明,Spring Data 就会通过扫描机制发现该接口,并动态生成代理实现类,自动注入容器。你立刻就能获得以下开箱即用的方法:

  • save() / saveAll()
  • findById() / getById()
  • findAll()
  • deleteById() / deleteAll()

⚠️ 注意:JpaRepository 是 JPA 特定的接口,属于 Repository 接口体系的一部分。不要和普通的 CrudRepositoryPagingAndSortingRepository 混淆。


3. 自定义查询方法与复杂查询

虽然 JpaRepository 提供了基础 CRUD,但实际开发中我们经常需要自定义查询。Spring Data JPA 提供了多种灵活方式,无需手写实现类。

3.1 自动解析方法名生成查询(Query by Method Name)

这是最简单粗暴的方式。Spring Data 会根据接口方法名自动解析并生成对应的 JPQL 查询。

例如,假设 Foo 实体有一个 name 字段,我们只需在接口中声明:

public interface IFooDAO extends JpaRepository<Foo, Long> {

    Foo findByName(String name);

}

Spring Data 会自动翻译成类似 SELECT f FROM Foo f WHERE f.name = ?1 的查询。

✅ 支持的关键字非常丰富,比如:

  • findByAgeGreaterThan(int age)
  • findByNameContaining(String name)
  • findByActiveTrue()
  • findFirstByOrderByCreatedAtDesc()

❌ 如果方法名中的属性名拼错,比如 findByNamee("john"),启动时会抛出异常:

java.lang.IllegalArgumentException: No property nam found for type class com.baeldung.jpa.simple.model.Foo

建议:命名时严格遵循 JavaBean 规范,避免踩坑。

3.2 使用 @Query 注解自定义 JPQL 或原生 SQL

当方法名无法表达复杂逻辑时,可以用 @Query 注解手动写查询。

JPQL 示例:

@Query("SELECT f FROM Foo f WHERE LOWER(f.name) = LOWER(:name)")
Foo retrieveByName(@Param("name") String name);
  • @Param("name") 明确绑定参数,推荐使用,避免位置参数混乱。
  • JPQL 面向实体,更安全,支持数据库无关性。

原生 SQL 示例:

@Query(value = "SELECT * FROM foo WHERE name = ?1", nativeQuery = true)
Foo retrieveByNameNative(String name);

⚠️ 使用原生 SQL 时需设置 nativeQuery = true,注意 SQL 与数据库强绑定,移植性差。

3.3 高级查询:Specifications 与 Querydsl

对于动态组合查询(如后台管理的多条件筛选),硬编码 JPQL 不够灵活。Spring Data 提供了两种高级方案:

  1. Specifications:基于 JPA Criteria API 封装,支持 and() / or() 组合条件,适合复杂动态查询。
  2. Querydsl:通过 Java 代码构建类型安全的查询,语法更流畅,IDE 支持好。

详细用法可参考官方博客:Advanced Spring Data JPA – Specifications and Querydsl

⚠️ 不推荐使用 JPA 命名查询(Named Queries),因为要么写在 XML,要么污染实体类(用 @NamedQuery 注解),维护性差。


4. 事务配置

Spring Data JPA 的实现类(SimpleJpaRepository)默认已配置事务。其机制如下:

  • 类级别标注 @Transactional(readOnly = true),对所有查询方法生效。
  • save(), delete() 等写操作方法,通过方法级别的 @Transactional 覆盖,开启可写事务。

这意味着:你无需手动添加 @Transactional,除非需要自定义事务传播行为或隔离级别

4.1 异常转换依然有效

Spring 5 移除了 JpaTemplate,但很多人担心:JPA 原生异常(如 PersistenceException)还能自动转换成 Spring 的 DataAccessException 吗?

✅ 完全可以。只要你的 DAO 接口或类上标注了 @Repository,Spring 就会通过 PersistenceExceptionTranslator 自动完成异常翻译。

验证示例(集成测试):

@Test(expected = DataIntegrityViolationException.class)
public void whenInvalidEntityIsCreated_thenDataException() {
    service.create(new Foo());
}

⚠️ 注意:异常转换依赖 Spring 的代理机制。因此,DAO 类不能声明为 final,否则无法创建代理,导致异常无法翻译。


5. 启用 Spring Data JPA 仓库

要让 Spring Data 扫描并生成仓库实现,必须启用仓库支持。

Java 配置

使用 @EnableJpaRepositories 注解指定 DAO 接口所在的包:

@EnableJpaRepositories(basePackages = "com.baeldung.jpa.simple.repository") 
public class PersistenceConfig { 
    // 其他配置...
}

XML 配置

<jpa:repositories base-package="com.baeldung.jpa.simple.repository" />

⚠️ 确保该配置被 Spring 容器加载(如通过 @Import 或 XML <import>)。


6. Java 与 XML 配置共存

Spring Data JPA 依赖 Spring 的 @PersistenceContext 机制注入 EntityManager。它通过 JpaRepositoryFactoryBean 创建代理实现。

如果你使用 XML 配置,需确保引入 Spring Data 的 XML 命名空间:

@Configuration
@EnableTransactionManagement
@ImportResource("classpath*:springDataConfig.xml")
public class PersistenceJPAConfig {
    // ...
}

对应的 XML 文件需包含:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="
           http://www.springframework.org/schema/data/jpa
           http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    <jpa:repositories base-package="com.example.repo"/>
</beans>

7. Maven 依赖

除了基础的 JPA 提供者(如 Hibernate)依赖外,必须引入 spring-data-jpa

<dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-jpa</artifactId>
   <version>3.1.3</version>
</dependency>

该依赖会自动引入 spring-data-commons 等核心模块。


8. 结合 Spring Boot 使用

使用 Spring Boot 可以进一步简化配置。只需引入:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

✅ Boot 会自动完成以下工作:

  • 配置 DataSource
  • 启用 @EnableJpaRepositories
  • 配置 EntityManagerFactory 和事务管理器
  • 默认开启 spring.jpa.hibernate.ddl-auto=create-drop(测试环境友好)

你只需在 application.properties 中配置数据库连接:

spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

当然,你也可以通过自定义 @Configuration 覆盖默认行为。


9. 开发工具推荐

Eclipse

  • 安装 Dali JPA Tools:支持实体关系图(ER Diagram)、DDL 生成、反向工程。
  • 使用 Spring Tool Suite (STS):可校验 Spring Data JPA 方法名是否合法。

IntelliJ IDEA

  • Ultimate 版:内置 JPA 支持,提供 ER 图、JPQL 控制台、代码检查。
  • Community 版:功能有限,但可通过插件增强。

✅ 推荐安装 JPA Buddy 插件(支持 Community 和 Ultimate):

  • 自动生成实体、Repository、DTO
  • 生成 DDL、Flyway/Liquibase 脚本
  • 支持数据库反向工程
  • 提升开发效率神器

10. 总结

本文系统介绍了如何在 Spring 项目中使用 Spring Data JPA 构建持久层,涵盖:

  • ✅ 无需手写 DAO 实现类,接口即契约
  • ✅ 方法名自动解析、@Query、Specifications 多种查询方式
  • ✅ 默认事务管理与异常转换机制
  • ✅ Java 与 XML 配置方式
  • ✅ Spring Boot 零配置集成
  • ✅ 推荐开发工具提升效率

最终实现:几乎零实现代码,高效、简洁、可维护的数据访问层

完整示例代码见 GitHub 仓库:
https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa-simple


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