"Using 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}\r\n\tprivate static class IdDTO {\r\n\t\tprivate final int id;\r\n\r\n\t\tprivate IdDTO(int id) {\r\n\t\t\tthis.id = id;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static class UserDTO {\r\n\t\tprivate final int id;\r\n\t\tprivate final String name;\r\n\r\n\t\tprivate UserDTO(int id, String name) {\r\n\t\t\tthis.id = id;\r\n\t\t\tthis.name = name;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static class IdResultTransformer implements ResultTransformer {\r\n\r\n\t\t@Override\r\n\t\tpublic Object transformTuple(Object[] tuple, String[] aliases) {\r\n\t\t\treturn new IdDTO((int) tuple[0]);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic List transformList(List collection) {\r\n\t\t\treturn collection;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static class UserResultTransformer implements ResultTransformer {\r\n\r\n\t\t@Override\r\n\t\tpublic Object transformTuple(Object[] tuple, String[] aliases) {\r\n\t\t\treturn new UserDTO((int) tuple[0], (String) tuple[1]);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic List transformList(List collection) {\r\n\t\t\treturn collection;\r\n\t\t}\r\n\t}\r\n{code}\r\n\r\n{code:java}\r\ndoInOpenTransaction((s, tx) -> {\r\n\t\t\tCriteriaQuery<User> q = s.getCriteriaBuilder().createQuery(User.class);\r\n\t\t\tRoot<User> root = q.from(User.class);\r\n\t\t\tq.select(root.get(\"id\"));\r\n\t\t\tList<IdDTO> ids = s.createQuery(q).unwrap(Query.class)\r\n\t\t\t\t\t.setResultTransformer(new IdResultTransformer()).getResultList();\r\n\t\t\tassertThat(ids.get(0).id, is(2));\r\n\t\t});\r\n{code}\r\n\r\nThe IdResultTransformer created an IdDTO having only one field and this works fine.\r\n\r\nHowever, when I want to expand the projected fields to more than one column using a multiselect\r\n{code:java}\r\ndoInOpenTransaction((s, tx) -> {\r\n\t\t\tCriteriaQuery<User> q = s.getCriteriaBuilder().createQuery(User.class);\r\n\t\t\tRoot<User> root = q.from(User.class);\r\n\t\t\tq.multiselect(root.get(\"id\"), root.get(\"name\"));\r\n\t\t\tList<UserDTO> ids = s.createQuery(q).unwrap(Query.class)\r\n\t\t\t\t\t.setResultTransformer(new UserResultTransformer()).getResultList();\r\n\t\t\tassertThat(ids.get(0).id, is(2));\r\n\t\t\tassertThat(ids.get(0).name, is(\"user\"));\r\n\t\t});\r\n{code}\r\nwhere UserTransformer has two fields, \"id\" and \"name\", I cannot do this by using the result transformer because\r\n\r\n{noformat}\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}\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}\r\ns.getCriteriaBuilder().createQuery(UserDTO.class)\r\n{code} which required me to have an appropriate constructor here.\r\n\r\nTest case: https://github.com/nikowitt/hibernate-test-case-templates/tree/HHH-12706" |
|