58

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?

7 Answers 7

31

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();
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

This isn't complete code. What is CustomNativeRepository and how do you define it?
Just an interface that has public Object runNativeQuery() method in this case
Would be better to add that simple snippet for completeness
Or better still, not have an interface.
Whilst this answer appears to be correct, I don't understand why Spring doesn't allow an interface with @ Repository on it and @ Query on the methods.
24

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 MyRepository extends JpaRepository<RootEntity, Integer> {
}

4 Comments

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.
Yes, it was a typo, it should work now. If you are having issue, can you provide error message, or open another question?
It won't compile because of... 1. You need to change name of Repository Interface, because you have names conflict(interface and annotation have the same name) 2. If you have validation schema turned on(every good application has), then you will need to create table RootEnity in DB
If you want to use validation without an additional entity in table, you can use a filter as an alternative. See stackoverflow.com/a/51740484/3862024 for more info.
12

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()
        ...
    }

}

3 Comments

In my case, it worked with @Repository annotation so I think it is needed.
@Repository is not needed. @Component will be enaugh @Akankshit_98
You'd prefer @Repository for semantic reasons. That's exactly the reason what it is there for-
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);
    }
}

Comments

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.

1 Comment

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?
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

Comments

0

If you want to use JPA without implementing your own Entity Manager: Even if your native query does not return a concrete entity, Create a dummy entity. Use that here: CrudRepository<DummyEntity, ID> Then use

@Query("<query here>", nativeQuery=true)
CustomInterfaceType methodName()

in your repository interface.

You also need to create an interface with getter methods for the columns you are selecting in your query. This order should match with what is there in the query.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.