" 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 me to have an appropriate constructor here.
Test using createQuery also forces me to decide quite early the resulting object class, which is IMHO not necessarily very convenient.\r\n\r\nTest case: https://github.com/nikowitt/hibernate-test-case-templates/tree/HHH-12706 " |
|