【问题标题】:Right approach for aggregating data from multiple tables in Spring JPA在 Spring JPA 中从多个表中聚合数据的正确方法
【发布时间】:2020-04-23 02:30:06
【问题描述】:

我有一个问答部分,其中每个问题和答案都有与之关联的用户个人资料卡。它与堆栈溢出时的情况非常相似。构成此用户资料卡的数据分布在多个表中。我从这些表中的每一个中获取一两列。现在我不确定应该采取什么方法将用户个人资料卡与每个问题和答案相关联。以下是我的想法

  1. 我无法在问答实体中建立直接关联,因为用户个人资料卡本身不是单个实体,而是一个聚合对象
  2. 我考虑过创建 DTO 作为预测,以将问题和用户个人资料卡的数据聚合到一个 DTO 中,如下所示

    @Query("select new com.QuestionDto(q.id, q.topic, q.views, ud.username, ur.points) from QuestionEntity q join UserDetails ud ON q.userId = ud.userId join userRanking ur ON ud.userId = ur.userId where q.id = :id")
    QuestionDto findQuestionDto(@Param("id") Long id);
    

我看到这种方法的问题是会有这样的 DTO 来回答,cmets 等。因此,每当用户资料卡发生更改时,我都需要相应地对所有 DTO 进行更改。从代码可维护性的角度来看,这将是一个非常糟糕的设计。

  1. 另一种方法是将用户资料卡数据聚合到其自身的 DTO 中,然后将从每个问题、答案等中调用此 DTO。使用这种方法,至少用户资料卡的逻辑在一个地方。

    @Query("select new com.UserProfileCard( ud.username, ur.points) from UserDetails ud join userRanking ur ON ud.userId = ur.userId where ud.userId = :id")
    UserProfileCard getUserProfileCard(@Param("id") Long userId);
    

但另一方面,如果一个问题有 15 条回复,则每个用户都会有 (1 + 15) 次查询。从性能的角度来看,我认为这不是很有效。

还有什么?从代码可维护性和性能的角度来看,我还缺少什么其他方法吗?

谢谢

【问题讨论】:

    标签: java spring jpa entity projection


    【解决方案1】:
    1. 我考虑过创建 DTO 作为预测,以将问题和用户个人资料卡的数据聚合到一个 DTO 中,如下所示

    在拥有单独的 UserProfileCard dto 时,您仍然可以使用选项 2。

    包 javax.persistance 通过使用注解 javax.persistence.Embedded 提供了将 dto 嵌入到另一个实体 dto 的这一特性。嵌入 dto 类由 javax.persistence.Embeddable 注释。您的 dto 类将如下所示 -

    @Embeddable
    public class UserProfileCard {
    
        private String username;
    
        private Integer points;
    }
    
    @Entity
    public class QuestionDto {
    
        @Id
        private Long id;
    
        private String topic;
    
        private Long views;
    
        @Embedded
        private UserProfileCard userProfileCard;
    }
    

    您可以使用相同的查询来获取 QuestionDto -

    @Query(nativeQuery = true, value = "select q.id, q.topic, q.views, ud.username, ur.points from question_entity q join user_details ud ON q.user_Id = ud.user_Id join user_ranking ur ON ud.user_Id = ur.user_Id where q.id = :id")
    QuestionDto findQuestionDto(Long id);
    

    ***更新***

    使用 UserProfileCard 的 sql 视图并在所有相关查询中使用它来避免 dao 接口中的代码重复。

    String USER_PROFILE_CARD_VIEW = "select ud.user_id, ud.username, ur.points from user_details as ud join user_ranking as ur on ur.user_id = ud.user_id";
    
    @Query(nativeQuery = true, value = "select q.id, q.topic, q.views, userprofilecard.* from question_entity q join (" + USER_PROFILE_CARD_VIEW +") as userprofilecard on userprofilecard.user_id = q.user_id where q.id = :id")
    QuestionDto findQuestionDto(Long id);
    

    【讨论】:

    • 但问题是代码重复。对于需要UserProfileCard 的其他实体,我将不得不这样做。未来对UserProfileCard 的更改将产生维护问题。
    • 现在代码重复只在 dao 接口中,应该通过定义 UserProfileCard 的 sql 视图并在每个依赖查询中使用相同的视图来解决。
    猜你喜欢
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-15
    • 2016-03-17
    相关资源
    最近更新 更多