Для самых тяжёлых случаев Spring Data Common поддерживает подмешивание собственного кода к генерируемым репозиториями.
Код примеров ниже основан на коде из статьи Hello, Spring Data JPA
Добавление кода к конкретному репозиторию
Все не просто, а очень просто. Вначале необходимо описать интерфейс с методами, которые необходимо добавить:
1
2
3
4
|
public interface PassportRepositoryCustom {
List<Passport> passportOwnedBy(final String lastName);
List<Passport> passportOwnerStartsWith(final String lastName);
}
|
И потом подмешать его к интерфейсам вашего репозитория:
1
|
public interface PassportRepository extends CommonRepository<Passport, Long>, PassportRepositoryCustom { }
|
Наконец, необходимо реализовать код дополнительных методов:
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
|
@Repository
public class PassportRepositoryImpl implements PassportRepositoryCustom {
@PersistenceUnit
private EntityManagerFactory emf;
@Override
public List<Passport> passportOwnedBy(final String lastName) {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Passport> passportOwnerCriteria = cb.createQuery(Passport.class);
Root<Passport> ownerPassportRoot = passportOwnerCriteria.from(Passport.class);
passportOwnerCriteria.select(ownerPassportRoot);
passportOwnerCriteria.where(cb.equal(ownerPassportRoot.get("owner").get("lastName"), lastName));
return em.createQuery(passportOwnerCriteria)
.getResultList();
}
@Override
public List<Passport> passportOwnerStartsWith(String lastName) {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Passport> passportLikeCriteria = cb.createQuery(Passport.class);
Root<Passport> likePassportRoot = passportLikeCriteria.from(Passport.class);
passportLikeCriteria.select(likePassportRoot);
passportLikeCriteria.where(cb.like(likePassportRoot.get("owner").get("lastName"), lastName+"%"));
return em.createQuery(passportLikeCriteria)
.getResultList();
}
}
|
Реализация интерфейса представляет собой обычный Spring bean, в котором можно использовать любой функционал Spring. Например, я внедрил EntityManagerFactory и обращаюсь к нему.
На реализацию интерфейса накладывается одно интересное ограничение — имя класса должно заканчиваться на Impl, чтобы Spring смог распознать реализацию и связать её с репозиторием. Впрочем, суффикс можно изменить настройками, в частности параметром repositotyImplementationPostfix аннотации @EnableJpaRepositories
Добавления кода ко всем репозиториям
Spring Data Common позволяет определить базовый интерфейс, от реализации которого автоматически будут унаследованы все остальные репозитории. Как и в случае с одним репозиторием, требуется определить отдельно интерфейс, отдельно реализацию:
1
2
3
4
|
@NoRepositoryBean
public interface CommonRepository<T, ID extends Serializable> extends CrudRepository<T, ID>{
String idToString(ID id);
}
|
1
2
3
4
5
6
7
8
9
10
11
|
public class CommonRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements CommonRepository<T, ID> {
public CommonRepositoryImpl(JpaEntityInformation entityInformation, EntityManager em) {
super(entityInformation, em);
}
@Override
public String idToString(ID id) {
return id.toString();
}
}
|
Метод выше достаточно бессмысленен, но хорошо показывает принцип.
Наконец, надо указать Spring Data Common где найти реализацию общего репозитория:
1
|
@EnableJpaRepositories(repositoryBaseClass = CommonRepositoryImpl.class)
|
И после этого можно наследовать ваши репозитории от базового интерфейса:
1
|
public interface PersonRepository extends CommonRepository<Person, Long> { }
|
1
|
public interface PersonRepository extends CommonRepository<Person, Long> { }
|
Код примера доступен на github