" I'm playing with JPA criteria and result transformers, inspired by https://vladmihalcea.com/why-you-should-use-the-hibernate-resulttransformer-to-customize-result-set-mappings/
\r\n\r\n\r\n {code:java} private \r\n\tprivate static class IdDTO { private \r\n\t\tprivate final int id;
private \r\n\r\n\t\tprivate IdDTO(int id) { this \r\n\t\t\tthis .id = id; \r\n\t\t } \r\n\t }
private \r\n\r\n\tprivate static class UserDTO { private \r\n\t\tprivate final int id; private \r\n\t\tprivate final String name;
private \r\n\r\n\t\tprivate UserDTO(int id, String name) { this \r\n\t\t\tthis .id = id; this \r\n\t\t\tthis .name = name; \r\n\t\t } \r\n\t }
private \r\n\r\n\tprivate static class IdResultTransformer implements ResultTransformer {
\r\n\r\n\t\t @Override public \r\n\t\tpublic Object transformTuple(Object[] tuple, String[] aliases) { return \r\n\t\t\treturn new IdDTO((int) tuple[0]); \r\n\t\t }
\r\n\r\n\t\t @Override public \r\n\t\tpublic List transformList(List collection) { return \r\n\t\t\treturn collection; \r\n\t\t } \r\n\t }
private \r\n\r\n\tprivate static class UserResultTransformer implements ResultTransformer {
\r\n\r\n\t\t @Override public \r\n\t\tpublic Object transformTuple(Object[] tuple, String[] aliases) { return \r\n\t\t\treturn new UserDTO((int) tuple[0], (String) tuple[1]); \r\n\t\t }
\r\n\r\n\t\t @Override public \r\n\t\tpublic List transformList(List collection) { return \r\n\t\t\treturn collection; \r\n\t\t } \r\n\t } \r\n {code}
Using \r\n\r\nUsing the criteria builder, I can define a result transformer to my query when I only use a single select:
\r\n\r\n {code:java} doInOpenTransaction \r\ndoInOpenTransaction ((s, tx) -> { CriteriaQuery \r\n\t\t\tCriteriaQuery <User> q = s.getCriteriaBuilder().createQuery(User.class); Root \r\n\t\t\tRoot <User> root = q.from(User.class); q \r\n\t\t\tq .select(root.get( \ "id \ ")); List \r\n\t\t\tList <IdDTO> ids = s.createQuery(q).unwrap(Query.class) \r\n\t\t\t\t\t .setResultTransformer(new IdResultTransformer()).getResultList(); assertThat \r\n\t\t\tassertThat (ids.get(0).id, is(2)); \r\n\t\t }); \r\n {code}
The \r\n\r\nThe IdResultTransformer creates the IdDTO, this works fine.
However \r\n\r\nHowever , when I want to expand the projected fields to more than one column using a multiselect \r\n {code:java} doInOpenTransaction \r\ndoInOpenTransaction ((s, tx) -> { CriteriaQuery \r\n\t\t\tCriteriaQuery <User> q = s.getCriteriaBuilder().createQuery(User.class); Root \r\n\t\t\tRoot <User> root = q.from(User.class); q \r\n\t\t\tq .multiselect(root.get( \ "id \ "), root.get( \ "name \ ")); List \r\n\t\t\tList <UserDTO> ids = s.createQuery(q).unwrap(Query.class) \r\n\t\t\t\t\t .setResultTransformer(new UserResultTransformer()).getResultList(); assertThat \r\n\t\t\tassertThat (ids.get(0).id, is(2)); assertThat \r\n\t\t\tassertThat (ids.get(0).name, is( \ "user \ ")); \r\n\t\t }); \r\n {code} where \r\nwhere UserTransformer has two fields, \ "id \ " and \ "name \ ", I cannot do this by using the result transformer because
\r\n\r\n {noformat} java \r\njava .lang.RuntimeException: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [models.common.security.User]. Expected arguments are: int, java.lang.String [select new models.common.security.User(generatedAlias0.id, generatedAlias0.name) from models.common.security.User as generatedAlias0] \r\n {noformat}
the \r\n\r\nthe query automatically uses \ "new \ " in the statement, so I cannot use a result transformer here. The only solution is the to use \r\n {code:java} s \r\ns .getCriteriaBuilder().createQuery(UserDTO.class) \r\n {code} which required requires me to have an appropriate constructor here. using createQuery also forces me to decide quite early the resulting object class, which is IMHO not necessarily very convenient:
a \r\n\r\na ) Requires code refactoring, especially for legacy criteria code b \r\nb ) the \ "new \ " constructor detection does not work for static inner classes, I have to move UserDTO to top level.
Test \r\n\r\nTest case: https://github.com/nikowitt/hibernate-test-case-templates/tree/HHH-12706 " |
|