Is it possible to write a TypedQuery
which contains multiple custom constructors? For example if you want to create objects which contain an id and another object, such as:
TypedQuery<ChapterWithBookId> query = em.createQuery(
"SELECT new " ChapterWithBookId.class.getName() "(book.id, new example.Chapter(chapter.id, "
"chapter.size"
")) "
"FROM " Book.class.getName() " AS book JOIN " Chapter.class.getName() " AS chapter WHERE "
"book.id IN :ids", ChapterWithBookIds.class)
.setParameter("ids", ids);
Disregarding whether this example makes practical sense, this query should return a list of objects of type ChapterWithBookId
, where in that list there is an object for each chapter for all books which have an id, contained in the list of ids set as a parameter.
CodePudding user response:
That's right, you can't have two constructor calls in JPQL - it's not Java, but a custom language and only supports "SELECT NEW".
There are several ways to remedy this:
.1. Have your constructor build the second object as well - admittedly an ugly hack:
public ChapterWithBookId(Long bookId, Long chapterId, Long size) {
this.bookId = bookId;
this.chapter = new Chapter(chapterId, size);
}
.2. Just return the result as a tuple, iterate over the list of tuples and build objects (also ugly).
.3. If you use Hibernate, use Hibernate's ResultTransformer:
List<ChapterWithBookId> chapterWithBookDtos = entityManager
.createQuery(
"SELECT book.id, chapter.id, chapter.size "
"FROM " Book.class.getName() " AS book JOIN " Chapter.class.getName() " AS chapter WHERE "
"book.id IN :ids", ChapterWithBookIds.class)
.setParameter("ids", ids);)
.unwrap( org.hibernate.query.Query.class )
.setResultTransformer(
new ResultTransformer() {
@Override
public Object transformTuple(
Object[] tuple,
String[] aliases) {
return new ChapterWithBookId(
(Long) tuple[0],
new Chapter((Long) tuple[1], (Long) tuple[2])
);
}
@Override
public List transformList(List collection) {
return collection;
}
}
)
.getResultList();
Here a general article about your options: Hibernate’s ResultTransformer in Hibernate 4, 5 & 6
Blog post about why ResultTransformer also improves query efficiency by Vlad Mihalcea.
And Vlad also offers the hibernate-types library, which has a much nice ListResultTransformer.
I definitely recommend the last two articles and everything Vlad has written - blog and book.
CodePudding user response:
As it's defined in the JPA specification (see section 4.8 SELECT Clause):
The SELECT clause has the following syntax:
select_clause ::= SELECT [DISTINCT] select_item {, select_item}* select_item ::= select_expression [ [AS] result_variable] select_expression ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable | OBJECT(identification_variable) | constructor_expression constructor_expression ::= NEW constructor_name ( constructor_item {, constructor_item}* ) constructor_item ::= single_valued_path_expression | scalar_expression | aggregate_expression | identification_variable aggregate_expression ::= { AVG | MAX | MIN | SUM } ([DISTINCT] state_valued_path_expression) | COUNT ([DISTINCT] identification_variable | state_valued_path_expression | single_valued_object_path_expression) | function_invocation
So, as you can see the constructor_item
can not be constructor_expression
. But you can easy refactor your class ChapterWithBookId
constructor to construct example.Chapter
by id
and size
and, IMHO, it will make the JPQL much readable.