Spring Data Projections问题:如何返回用户文档数量而非列表
嘿,我来帮你搞定这个问题!你遇到的情况是在Spring Data JPA的Projection里想返回文档数量而不是整个集合,之前的两种尝试都有小问题,下面给你几个可行的解决方案:
方案1:修正SpEL表达式调用集合size方法
你第一次用@Value注解的时候写错了集合长度的获取方式——Java/Kotlin里集合的长度是通过size()方法获取的,不是count字段。修正后的代码应该是这样:
interface UserProfile { fun getUsername(): String fun getName(): String fun getFavoriteTags(): MutableSet<Tag>? fun getFavoriteDocuments(): MutableSet<Document>? fun getAverageScore(): Double fun getUserPicPath(): String // 用SpEL调用size()方法,同时处理null情况返回0 @Value("#{target.documents != null ? target.documents.size() : 0}") fun getDocumentsCount(): Int }
这样Spring就能正确识别并计算文档集合的数量了,而且我们还处理了集合为null的情况,避免返回null值,更符合业务逻辑。
方案2:在Repository查询中直接计算数量(推荐)
如果你的文档集合数据量比较大,加载整个集合再计算size会浪费性能,更优的方式是直接在JPQL查询里用SIZE()函数计算集合大小,不需要加载整个Documents集合。修改你的Repository代码:
interface UserRepository : JpaRepository<User, String> { @Query( value = "SELECT u.username as username, u.name as name, u.favoriteTags as favoriteTags, " + "u.favoriteDocuments as favoriteDocuments, u.averageScore as averageScore, " + "u.userPicPath as userPicPath, SIZE(u.documents) as documentsCount " + "FROM User u WHERE u.name LIKE %:name%", countQuery = "SELECT COUNT(u) FROM User u WHERE u.name LIKE %:name%" ) fun findByNameContainingIgnoreCase(name: String, page: Pageable): Page<UserProfile> }
然后对应的UserProfile接口里只需要保留getDocumentsCount()方法,删掉原来的getDocuments()方法即可:
interface UserProfile { fun getUsername(): String fun getName(): String fun getFavoriteTags(): MutableSet<Tag>? fun getFavoriteDocuments(): MutableSet<Document>? fun getAverageScore(): Double fun getUserPicPath(): String fun getDocumentsCount(): Int }
这种方式直接在数据库层面计算数量,性能更好,也避免了不必要的数据加载。
方案3:改用基于类的DTO Projection
如果接口式Projection的默认方法总是有问题,你可以换成基于类的DTO(数据传输对象),这种方式更灵活,也不会有默认方法的识别问题:
首先定义DTO类:
data class UserProfileDTO( val username: String, val name: String, val favoriteTags: MutableSet<Tag>?, val favoriteDocuments: MutableSet<Document>?, val averageScore: Double, val userPicPath: String, val documentsCount: Int )
然后修改Repository的查询方法返回Page<UserProfileDTO>,同样可以用上面的@Query语句,Spring Data会自动把查询结果映射到DTO类的属性上。
以上三种方案都能解决你的问题,我个人推荐方案2,因为它在性能上更有优势,尤其是当你的文档集合很大的时候。
内容的提问来源于stack exchange,提问作者Cilla

