Создание репозитория Spring без сущности
Я хочу использовать интерфейс репозитория Spring Data для выполнения нативных запросов — я считаю, что это самый простой способ из-за низкой сложности.
Однако при расширении интерфейса, например, CrudRepository<T, ID>
, мне нужно указать T — мою сущность, которая недоступна.
Мои нативные запросы не возвращают конкретную сущность, поэтому каков лучший способ создать репозиторий Spring без сущности?
5 ответ(ов)
CrudRepository
и JpaRepository
не предназначены для работы без пары <Entity, ID>
.
Вам лучше создать пользовательский репозиторий, внедрить EntityManager
и выполнять запросы оттуда:
@Repository
public class CustomNativeRepositoryImpl implements CustomNativeRepository {
@Autowired
private EntityManager entityManager;
@Override
public Object runNativeQuery() {
return entityManager.createNativeQuery("myNativeQuery")
.getSingleResult();
}
}
Таким образом, вы получите более гибкое управление запросами и сможете использовать произвольные SQL-запросы.
В данный момент в JPA нет функциональности для создания репозиториев, которые бы использовали только нативные запросы или JPQL/HQL запросы с использованием аннотации @Query
. Чтобы обойти это ограничение, вы можете создать заглушечный объект, который будет вставлен в расширяемый интерфейс, как показано ниже:
@Entity
public class RootEntity {
@Id
private Integer id;
}
@Repository
public interface MyRepository extends JpaRepository<RootEntity, Integer> {
}
Таким образом, вы можете использовать этот подход для реализации своих собственных методов с нативными или JPQL/HQL запросами, добавляя их в ваш репозиторий.
Это работает для нас. Обратите внимание на менеджер сущностей в примере:
@Repository
public class MyRepository {
@PersistenceContext
EntityManager entityManager;
public void doSomeQuery() {
Query query = entityManager.createNativeQuery("SELECT foo FROM bar");
List<?> results = query.getResultList();
// Дальнейшая обработка результатов...
}
}
В этом примере мы используем EntityManager
для выполнения нативного SQL-запроса. Метод createNativeQuery
позволяет создавать запрос, который будет выполняться непосредственно в базе данных. После выполнения запроса мы получаем список результатов с помощью метода getResultList()
.
Вы можете просто аннотировать вашу реализацию с помощью @Repository
и получить экземпляр EntityManager
.
public interface ProductFilterRepository {
Page<Product> filter(FilterTO filter, Pageable pageable);
}
@Repository
@AllArgsConstructor
public class ProductFilterRepositoryImpl implements ProductFilterRepository {
private final EntityManager em;
@Override
public Page<Product> filter(FilterTO filter, Pageable pageable) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Product> cq = cb.createQuery(Product.class);
Root<Product> root = cq.from(Product.class);
List<Predicate> predicates = new ArrayList<>();
if (filter.getPriceMin() != null) {
predicates.add(cb.ge(root.get("price"), filter.getPriceMin()));
}
if (filter.getPriceMax() != null) {
predicates.add(cb.le(root.get("price"), filter.getPriceMax()));
}
if (filter.getBrands() != null && !filter.getBrands().isEmpty()) {
predicates.add(root.get("brand").in(filter.getBrands()));
}
if (filter.getCategories() != null && !filter.getCategories().isEmpty()) {
predicates.add(root.get("category").in(filter.getCategories()));
}
cq.where(predicates.toArray(new Predicate[0]));
TypedQuery<Product> tq = em.createQuery(cq);
tq.setMaxResults(pageable.getPageSize());
tq.setFirstResult(pageable.getPageNumber() * pageable.getPageSize());
CriteriaQuery<Long> countCq = cb.createQuery(Long.class);
countCq.select(cb.count(countCq.from(Product.class)));
countCq.where(predicates.toArray(new Predicate[0]));
TypedQuery<Long> countTq = em.createQuery(countCq);
Long count = countTq.getSingleResult();
return new PageImpl<>(tq.getResultList(), pageable, count);
}
}
В данном примере мы создаем интерфейс ProductFilterRepository
с методом filter
, который принимает объект фильтра и параметры постраничной навигации. Реализация ProductFilterRepositoryImpl
, аннотированная @Repository
, предоставляет конкретную реализацию этого метода с использованием EntityManager
для создания произвольных критериев выборки.
Обратите внимание на использование CriteriaBuilder
и CriteriaQuery
для создания условий фильтрации, что позволяет нам гибко управлять запросами к базе данных. Мы также используем Pageable
для управления постраничным выводом результатов.
Использование JdbcTemplate может рассматриваться как альтернатива, когда у вас нет конкретного класса сущности для получаемого результата нативного запроса. Для выполнения запросов данных с помощью JdbcTemplate требуется класс POJO для результата и маппер, реализующий интерфейс RowMapper для данного класса POJO.
Как зарегистрировать SQL-запросы в Spring Boot?
Как исправить ошибку Hibernate "объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед сбросом"
Разница между @Inject и @Autowired в Spring Framework: когда и что использовать?
В чем разница между JPA и Hibernate? [закрыт]
Где находится аннотация @Transactional?