Для самых тяжёлых случаев 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