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实现基类)
  • 必须实现带JpaEntityInformationEntityManager参数的构造函数
  • 保留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());        
    }
}

📝 测试流程:

  1. 创建3条学生记录
  2. 调用自定义方法查找name包含"john"的学生
  3. 验证返回结果数量(应返回"john"和"johnson"两条记录)

ExtendedStudentRepository同时支持标准方法(如save())和自定义方法。

7. 总结

本文演示了如何为所有Spring Data JPA Repository添加全局自定义方法,核心步骤包括:

  1. 创建带@NoRepositoryBean的基接口
  2. 实现基类(继承SimpleJpaRepository
  3. 通过repositoryBaseClass指定自定义实现
  4. 实体Repository继承基接口

完整示例代码可在GitHub仓库查看。


原始标题:Spring Data JPA - Add Method in All Repositories | Baeldung

« 上一篇: Java周报, 200
» 下一篇: Jackson嵌套字段映射