QueryDSL关联查询时如何指定投影排除关联实体的CLOB字段?
解决QueryDSL关联查询排除CLOB字段的问题
要在不修改领域模型、不使用原生SQL的前提下,排除关联UserEntity中的CLOB字段,可通过投影查询实现,以下是两种可行方案:
方案1:自定义投影类(类型安全)
创建仅包含必要字段的投影类(无需修改原CategoryEntity和UserEntity),通过QueryDSL的构造函数投影完成查询:
步骤1:定义投影类
// 包含Category核心字段及精简版User的投影类 public class CategoryWithSlimUser { private Long id; private String name; // 补充Category的其他必要字段 private List<SlimUser> users; // 构造函数需与QueryDSL查询中选定的字段顺序一致 public CategoryWithSlimUser(Long id, String name, List<SlimUser> users) { this.id = id; this.name = name; this.users = users; } // 精简版User投影,仅包含非CLOB字段 public static class SlimUser { private Long id; private String username; // 补充User的其他非CLOB必要字段 public SlimUser(Long id, String username) { this.id = id; this.username = username; } } }
步骤2:编写QueryDSL查询代码
JPAQuery<CategoryWithSlimUser> query = new JPAQuery<>(entityManager) .select(Projections.constructor( CategoryWithSlimUser.class, QCategoryEntity.categoryEntity.id, QCategoryEntity.categoryEntity.name, // 对应Category投影类的其他字段 Projections.list( Projections.constructor( CategoryWithSlimUser.SlimUser.class, QUserEntity.userEntity.id, QUserEntity.userEntity.username // 对应SlimUser的其他非CLOB字段 ) ) )) .from(QCategoryEntity.categoryEntity) .leftJoin(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity) .where(somePredicate) .groupBy(QCategoryEntity.categoryEntity.id); // 分组避免Category重复 List<CategoryWithSlimUser> result = query.fetch();
方案2:Tuple投影手动组装结果
若不想创建额外投影类,可使用Tuple获取零散字段,再手动组装成目标实体结构:
JPAQuery<Tuple> query = new JPAQuery<>(entityManager) .select( QCategoryEntity.categoryEntity.id, QCategoryEntity.categoryEntity.name, // 补充Category的其他必要字段 QUserEntity.userEntity.id, QUserEntity.userEntity.username // 补充User的其他非CLOB必要字段 ) .from(QCategoryEntity.categoryEntity) .leftJoin(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity) .where(somePredicate); List<Tuple> tuples = query.fetch(); // 手动组装CategoryEntity与精简User的关联集合 Map<Long, CategoryEntity> categoryMap = new HashMap<>(); for (Tuple tuple : tuples) { Long categoryId = tuple.get(QCategoryEntity.categoryEntity.id); CategoryEntity category = categoryMap.computeIfAbsent(categoryId, id -> { CategoryEntity cat = new CategoryEntity(); cat.setId(id); cat.setName(tuple.get(QCategoryEntity.categoryEntity.name)); // 设置Category的其他字段 cat.setUsers(new ArrayList<>()); return cat; }); // 处理关联的User(避免空指针) Long userId = tuple.get(QUserEntity.userEntity.id); if (userId != null) { UserEntity slimUser = new UserEntity(); slimUser.setId(userId); slimUser.setUsername(tuple.get(QUserEntity.userEntity.username)); // 设置User的其他非CLOB字段 category.getUsers().add(slimUser); } } List<CategoryEntity> result = new ArrayList<>(categoryMap.values());
原理说明
直接查询QCategoryEntity.categoryEntity时,JPA会加载关联UserEntity的所有字段(包括CLOB)。通过投影查询,我们可以精确指定需要获取的字段,从而跳过不必要的CLOB数据,同时满足不修改领域模型、不使用原生SQL的要求。
内容的提问来源于stack exchange,提问作者Bruno Vandekerkhove
相关产品推荐
相关产品推荐

