1. 概述
Spring Data通过简单的Repository接口定义,极大简化了实体操作流程。这些接口自带预定义方法,并支持在各个接口中添加自定义方法。
但如果我们想添加一个所有Repository都能用的自定义方法,过程就稍微复杂些。本文将探讨如何用Spring Data JPA实现这个需求。
关于Spring Data JPA的配置和使用基础,可参考我们之前的文章:Hibernate与Spring 4集成指南 和 Spring Data JPA入门。
2. 定义基础Repository接口
首先,创建一个声明自定义方法的新接口:
@NoRepositoryBean
public interface ExtendedRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
public List<T> findByAttributeContainsText(String attributeName, String text);
}
✅ 关键点:
- 继承
JpaRepository
以保留所有标准功能 - 添加
@NoRepositoryBean
注解- 避免 Spring 默认行为(为所有Repository子接口创建实现)
- 因为我们需要提供自定义实现类
⚠️ 该接口仅作为实际DAO接口的父类,不应被Spring实例化。
3. 实现基础类
接下来,提供ExtendedRepository
接口的实现类:
public class ExtendedRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
private EntityManager entityManager;
public ExtendedRepositoryImpl(JpaEntityInformation<T, ?>
entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
// ...
}
✅ 实现要点:
- 继承
SimpleJpaRepository
(Spring默认的Repository实现基类) - 必须实现带
JpaEntityInformation
和EntityManager
参数的构造函数 - 保留
EntityManager
引用用于自定义方法
然后实现自定义方法:
@Transactional
public List<T> findByAttributeContainsText(String attributeName, String text) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> cQuery = builder.createQuery(getDomainClass());
Root<T> root = cQuery.from(getDomainClass());
cQuery
.select(root)
.where(builder
.like(root.<String>get(attributeName), "%" + text + "%"));
TypedQuery<T> query = entityManager.createQuery(cQuery);
return query.getResultList();
}
🚀 方法功能:查找所有类型T的对象,其指定属性包含给定字符串值。
4. JPA配置
告诉Spring使用自定义实现类替代默认实现:
@Configuration
@EnableJpaRepositories(basePackages = "com.baeldung.persistence.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
// 其他JPA配置
}
✅ 配置核心:
- 使用
repositoryBaseClass
属性指定自定义实现类 - 设置正确的
basePackages
扫描路径
5. 创建实体Repository
现在看如何使用新接口。首先添加简单实体:
@Entity
public class Student {
@Id
private long id;
private String name;
// 标准构造函数、getter/setter
}
然后创建Student实体的DAO,继承ExtendedRepository
:
public interface ExtendedStudentRepository extends ExtendedRepository<Student, Long> {
}
🎉 完成!现在该接口自动获得findByAttributeContainsText()
方法。
类似地,任何继承ExtendedRepository
的接口都会获得该方法。
6. 测试Repository
创建JUnit测试验证自定义方法:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { StudentJPAH2Config.class })
public class ExtendedStudentRepositoryIntegrationTest {
@Resource
private ExtendedStudentRepository extendedStudentRepository;
@Before
public void setup() {
extendedStudentRepository.save(new Student(1, "john"));
extendedStudentRepository.save(new Student(2, "johnson"));
extendedStudentRepository.save(new Student(3, "tom"));
}
@Test
public void givenStudents_whenFindByName_thenOk(){
List<Student> students
= extendedStudentRepository.findByAttributeContainsText("name", "john");
assertEquals("数量不正确", 2, students.size());
}
}
📝 测试流程:
- 创建3条学生记录
- 调用自定义方法查找name包含"john"的学生
- 验证返回结果数量(应返回"john"和"johnson"两条记录)
✅ ExtendedStudentRepository
同时支持标准方法(如save()
)和自定义方法。
7. 总结
本文演示了如何为所有Spring Data JPA Repository添加全局自定义方法,核心步骤包括:
- 创建带
@NoRepositoryBean
的基接口 - 实现基类(继承
SimpleJpaRepository
) - 通过
repositoryBaseClass
指定自定义实现 - 实体Repository继承基接口
完整示例代码可在GitHub仓库查看。