Spring Data JPA 添加自定义默认方法

通常使用 Spring Data JPA 时,都会创建一个 BaseRepository,或者继承 Spring 已经预设的一些预设的接口例如 CrudRepository 等,以自建 BaseRepository 为例(为了简化只保留了一个方法):

1
2
3
4
@NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {
T findOne(ID id);
}

加载基础配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "foo.bar.repository")
public class JpaConfig {
@Override
protected String entityPackagesToScan() {
return "foo.bar.entity";
}
private Properties additionalProperties() {
Properties properties = new Properties();
// 省略各种参数配置
return properties;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
// 指定要扫描的 entity 包路径
em.setPackagesToScan(entityPackagesToScan());
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@PreDestroy
public void preDestroy() {
dataSource().close();
}
@PostConstruct
public void postConstruct() {
try {
dataSource().init();
} catch (SQLException e) {
logger.error(e.getMessage(), e);
}
}
@Bean
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
// 省略数据源配置
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(){
EntityManagerFactory factory = entityManagerFactory().getObject();
return new JpaTransactionManager(factory);
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
}

以上就完成了基本配置可以使用 Spring Data JPA 了,回到本文的目的:如何添加一个自定义的默认方法?
为单个 Repository 添加自定义实现的方法很简单略过,下面以添加一个名为 findOneForUpdate 的自定义默认方法为例进行代码示例(即全局为每个 Entity 的 Repository 添加按 ID 锁行功能)。

首先创建一个新的接口 BaseInternalRepository :

1
2
3
4
5
6
7
8
9
@NoRepositoryBean
public interface BaseInternalRepository<T, ID extends Serializable> extends Repository<T, ID> {
/**
* 锁行读写的方式通过ID获取对象
* @param id 对象ID
* @return 对应的对象
*/
T findOneForUpdate(ID id);
}

修改原先的 BaseRepository,使其继承新的这个接口:

1
2
3
4
@NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends BaseInternalRepository<T, ID> {
T findOne(ID id);
}

为新创建的 BaseInternalRepository 接口添加实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class BaseInternalRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseInternalRepository<T, ID> {
private final EntityManager entityManager;
public BaseInternalRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
public BaseInternalRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
}
@Override
public T findOneForUpdate(ID id) {
return entityManager.find(this.getDomainClass(), id, LockModeType.PESSIMISTIC_WRITE);
}
protected Page<T> readPageWithoutCountQuery(TypedQuery<T> query, Pageable pageable, Specification<T> spec) {
query.setFirstResult(pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
List<T> content = query.getResultList();
return new PageImpl<T>(content, pageable, content.size());
}
}

注:SimpleJpaRepository 即 Spring Data JPA 默认用来代理创建 Repository 实例的实现类。

接下来要把自定义的实现替换掉默认实现,先创建一个自定义的生成 Repository 实例的工厂类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T,
I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
return new BaseRepositoryFactory(em);
}
private static class BaseRepositoryFactory<T, I extends Serializable>
extends JpaRepositoryFactory {
public BaseRepositoryFactory(EntityManager em) {
super(em);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// 使用自定义的实现类
return BaseInternalRepositoryImpl.class;
}
}
}

修改 JPA 配置使用自定义的工厂类:

1
2
3
4
5
6
7
8
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "foo.bar.repository",
repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class)
public class JpaConfig {
// 省去中间代码
}

大功告成!

基本原理是利用了 Repository 实例的创建过程:

  1. 查找是否有自定义的实现,如果有使用自定义实现提供的方法
  2. 如果还有未实现的方法,使用默认代理类中的方法,即 BaseInternalRepositoryImpl

通过这种方式,可以很方便的添加各种自定义的默认方法,如果有需要定制的,也可以在每个 Repository 的自定义实现中进行覆盖。