48

I want to use spring data repository interface to execute native queries - I think this way is the simplest because of low complexity.

But when extending interface ex. CrudRepository<T, ID> I need to write T - my entity, which is not available.

My native queries does not return any concrete entity, so what is the best way to create spring repository without entity?

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
dramcio
  • 551
  • 1
  • 5
  • 6

6 Answers6

28

CrudRepository or JpaRepository were not designed to work without an <Entity,ID> pair.

You are better off creating a custom repo, inject EntityManager and query from there:

  @Repository
  public class CustomNativeRepositoryImpl implements CustomNativeRepository {

    @Autowired
    private EntityManager entityManager;

    @Override
    public Object runNativeQuery() {
        entityManager.createNativeQuery("myNativeQuery")
         .getSingleResult();
    }
}
Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
22

Currently there is no functionality in JPA to create repositories with only native or JPQL/HQL queries (using @Query notation). To get around this, you can create a dummy object to insert into the extension interface like below:

@Entity
public class RootEntity {
    @Id
    private Integer id;
}

@Repository
public interface Repository extends JpaRepository<RootEntity, Integer> {
}
gagarwa
  • 1,426
  • 1
  • 15
  • 28
  • 1
    That's some weird code. It won't even compile. A private modifier on a interface? OK, maybe it's a typo. But even changing interface to class won't let your spring application run. – ioikka May 20 '20 at 15:45
  • Yes, it was a typo, it should work now. If you are having issue, can you provide error message, or open another question? – gagarwa Jun 04 '20 at 09:19
8

This works for us. See the entity manager

https://www.baeldung.com/hibernate-entitymanager

@Repository
public class MyRepository {

    @PersistenceContext
    EntityManager entityManager;

    public void doSomeQuery(){
        Query query = entityManager.createNativeQuery("SELECT foo FROM bar");
        query.getResultsList()
        ...
    }

}
Adam Hughes
  • 14,601
  • 12
  • 83
  • 122
5

You can just annotate your implementation with @Repository, and get an instance of 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);
    }
}
2

I think that using JdbcTemplate can be considered as an alternative when you do not have a concrete entity class for the resultset of a native query. Querying data using JdbcTemplate requires a POJO class for the resultset and a mapper implementing the RowMapper interface for the POJO class.

Manas
  • 888
  • 10
  • 20
  • 1
    It is a bad approach. For example, can you answer quickly for this question: how many transaction managers will be used for such mixed application? – v.ladynev Aug 06 '20 at 12:13
1

If you are using JPA you need Entities. As previous answers you can create NativeQueries or use Criteria API directly from EntityManager.

Some documentation about custom reports and common repo behaviour:

https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html#repositories.custom-behaviour-for-all-repositories