I'm trying to figure out how to do a DTO projection of an Entity with a list of Enums (@ElementCollection). Unfortunately, the QueryDsl Documentation is lacking and here I only find results for version 3 which are not applicable for version 4.
@Entity
public class User {
private String username;
@Enumerated(EnumType.STRING)
@ElementCollection(targetClass = Permission.class)
private Set<Permission> permissions;
}
And I'd like a DTO with a set/list/array of either Permission-Enums or simply Strings (will be converted to JSON anyway). A simple constructor expression doesn't work:
List<UserDto> users = new JPAQueryFactory(eM).select(
Projections.constructor(UserDto.class,
QUser.user.username, QUser.user.permissions))
.from(QUser.user)
.fetch();
Gives me org.hibernate.QueryException: not an entity
All the examples with .transform() I've seen use a groupBy and return a Map. I'm generating these queries dynamically and I want a List of DTOs, not sometimes a List of DTO and sometimes a Map.
EDIT:
Something like that if I were to write a native PostgreSQL query:
select id, username, array_remove(array_agg(up.permissions), null) as permissions
from users u
left join users_permissions up on up.uid = u.id
group by u.id;
EDIT 2:
I guess this is what I'd have to do with JPQL? :puke:
List<UserDto> users = (List<UserDto>) eM.getEntityManager().createQuery(
"SELECT u.id, u.username, u.tenantId, u.fullname, u.active, u.localeKey, perms " +
"FROM User u " +
"LEFT JOIN u.permissions perms")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(
new ResultTransformer() {
private Map<Long, UserDto> res = new HashMap<>();
@Override
public Object transformTuple(Object[] tuple, String[] aliases) {
UserDto u = res.get(tuple[0]);
if (u == null) {
u = new UserDto((Long) tuple[0], (String) tuple[1], "", (String) tuple[2], (String) tuple[3], (boolean) tuple[4], (String) tuple[5], EnumSet.of((Permission) tuple[6]));
res.put(u.getId(), u);
} else {
u.getPermissions().add((Permission) tuple[6]);
}
return null;
}
@Override
public List<UserDto> transformList(List tuples) {
return new ArrayList<>(res.values());
}
})
.getResultList();
OK, I finally figured it out. In this case you actually have to use a transformer, which makes sense, as you want to aggregate several rows.
I've had to dig through QueryDsl's unit tests. The static imports actually make it tricky if you're not using an IDE, but read it on Github like me. I almost had the Solution, but I used Expressions.set(), instead of GroupBy.set():
EnumPath<Permission> perm = Expressions.enumPath(Permission.class, "perm");
List<UserDto> users = new JPAQueryFactory(eM.getEntityManager())
.selectFrom(QUser.user)
.leftJoin(QUser.user.permissions, perm)
.transform(
groupBy(QUser.user.id)
.list(Projections.constructor(UserDto.class,
QUser.user.id, QUser.user.username, Expressions.stringTemplate("''"), QUser.user.tenantId,
QUser.user.fullname, QUser.user.active, QUser.user.localeKey, GroupBy.set(perm))));
And this is much nicer on the eye then the JPQL/Hibernate-ResultTransformer version.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With