I am trying to return an interface from a call to createQuery()
. It fails when trying to create the query with
org.hibernate.query.QueryTypeMismatchException: Result type 'DcListInterface' cannot be used to package the selected expressions
The same query works when defined with @Query
on a JPA repository, so I know the interface matches the query.
Can EntityManager
's createQuery()
return a proxy of the interface or is this functionality that is available only on a repository that extends one of the JPA repositories?
JPA based repo:
public interface DcRepository extends JpaRepository<DcMotor, Long> {
@Query("select dc.id, dc.manufacturer from DcMotor dc ")
List<DcListInterface> testQuery();
}
Successful test using JPA repository
@Test
void givenRepository_whenTest_thenSuccess() {
assertThat(dcRepository).isNotNull();
List<DcListInterface> dcListInterface = dcRepository.testQuery();
assertThat(dcListInterface).isNotNull();
}
Failing test using the EntityManger
directly without JPA repository
@Test
@Transactional(readOnly = true)
void test() {
TypedQuery<DcListInterface> query = em.createQuery("select dc.id, dc.manufacturer from DcMotor dc ", DcListInterface.class);
// org.hibernate.query.QueryTypeMismatchException thrown on above line.
List<DcListInterface> result = query.getResultList();
assertThat(result).isNotNull();
}
Can Spring Data JPA Entity Manager createQuery() return an interface?
Trivially, yes. <T>EntityManager.createQuery(String, Class<T>)
returns TypedQuery<T>
, and TypedQuery
is an interface.
I think you actually meant to ask whether the method's type argument can be an interface type. Also yes. The API docs already linked require of the type argument only that the result type of the query be assignable to it.
But, per the docs,
Your query has two items in its select list, so it is not suitable for the createQuery()
method invoked by your example code.
It's not clear what DcListInterface
is supposed to represent. If you mean it to be a list of all the results then that's just not how the API works. But if you mean it to correspond to each individual result, and if com.my.DcList
is a concrete class that implements that interface and has a suitable constructor, then this would work:
TypedQuery<DcListInterface> query = em.createQuery(
"select new com.my.DcList(dc.id, dc.manufacturer) from DcMotor dc",
DcListInterface.class);
But given the need to use a constructor expression to convert your multiple individual entity properties into a single item, you cannot get away without providing and a concrete class for the elements and naming it in the query.