1. Обзор
Spring Data значительно упрощает процесс работы с сущностями, просто определяя интерфейсы репозитория. Они поставляются с набором предопределенных методов и позволяют добавлять пользовательские методы в каждый интерфейс.
Однако, если мы хотим добавить пользовательский метод, доступный во всех репозиториях, процесс немного усложняется. Итак, это то, что мы будем исследовать здесь с помощью Spring Data JPA.
Для получения дополнительной информации о настройке и использовании Spring Data JPA ознакомьтесь с нашими предыдущими статьями: Guide to Hibernate with Spring 4 и Introduction to Spring Data JPA .
2. Определение интерфейса базового репозитория
Во-первых, мы должны создать новый интерфейс, который объявляет наш пользовательский метод:
@NoRepositoryBean
public interface ExtendedRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
public List<T> findByAttributeContainsText(String attributeName, String text);
}
Наш интерфейс расширяет интерфейс JpaRepository
, так что мы извлекаем выгоду из всего стандартного поведения.
Вы также заметите, что мы добавили аннотацию @NoRepositoryBean
. Это необходимо, поскольку в противном случае поведение Spring по умолчанию заключается в создании реализации для всех подынтерфейсов репозитория.
Здесь мы хотим предоставить нашу реализацию, которую следует использовать, поскольку это всего лишь интерфейс, предназначенный для расширения за счет реальных интерфейсов DAO для конкретных объектов.
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 использует для реализации интерфейсов репозитория.
Для этого необходимо создать конструктор с параметрами JpaEntityInformation
и EntityManager
, который вызывает конструктор из родительского класса.
Нам также нужно свойство EntityManager
для использования в нашем пользовательском методе.
Также нам нужно реализовать пользовательский метод, унаследованный от интерфейса ExtendedRepository :
@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();
}
Здесь метод findByAttributeContainsText()
ищет все объекты типа T, которые имеют определенный атрибут, содержащий значение String
, указанное в качестве параметра.
4. Конфигурация JPA
Чтобы указать Spring использовать наш пользовательский класс вместо класса по умолчанию для создания реализаций репозитория, мы можем использовать атрибут репозиторияBaseClass
:
@Configuration
@EnableJpaRepositories(basePackages = "org.foreach.persistence.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
// additional JPA Configuration
}
5. Создание репозитория сущностей
Далее давайте посмотрим, как мы можем использовать наш новый интерфейс.
Во-первых, давайте добавим простую сущность Student :
@Entity
public class Student {
@Id
private long id;
private String name;
// standard constructor, getters, setters
}
Затем мы можем создать DAO для объекта Student
, который расширяет интерфейс ExtendedRepository :
public interface ExtendedStudentRepository extends ExtendedRepository<Student, Long> {
}
Вот и все! Теперь наша реализация будет иметь собственный метод findByAttributeContainsText()
.
Точно так же любой интерфейс, который мы определяем, расширяя интерфейс ExtendedRepository
, будет иметь тот же метод.
6. Тестирование репозитория
Давайте создадим тест JUnit
, который показывает пользовательский метод в действии:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { StudentJPAH2Config.class })
public class ExtendedStudentRepositoryIntegrationTest {
@Resource
private ExtendedStudentRepository extendedStudentRepository;
@Before
public void setup() {
Student student = new Student(1, "john");
extendedStudentRepository.save(student);
Student student2 = new Student(2, "johnson");
extendedStudentRepository.save(student2);
Student student3 = new Student(3, "tom");
extendedStudentRepository.save(student3);
}
@Test
public void givenStudents_whenFindByName_thenOk(){
List<Student> students
= extendedStudentRepository.findByAttributeContainsText("name", "john");
assertEquals("size incorrect", 2, students.size());
}
}
В тесте сначала используется bean- компонент extendedStudentRepository
для создания 3 записей Student. Затем вызывается метод findByAttributeContains()
, чтобы найти всех студентов, чье имя содержит текст «джон».
Класс ExtendedStudentRepository
может использовать как стандартные методы, такие как save()
, так и добавленный нами пользовательский метод.
7. Заключение
В этой быстрой статье мы показали, как мы можем добавить пользовательский метод во все репозитории в Spring Data JPA.
Полный исходный код примеров можно найти на GitHub .