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
接口体系的一部分。不要和普通的 CrudRepository
或 PagingAndSortingRepository
混淆。
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 提供了两种高级方案:
- Specifications:基于 JPA Criteria API 封装,支持
and()
/or()
组合条件,适合复杂动态查询。 - 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