【问题标题】:Best JPA entity model for HashtagsHashtags 的最佳 JPA 实体模型
【发布时间】:2018-07-10 11:55:30
【问题描述】:

我需要为我的应用程序中的标签设计一个 JPA 实体模型,类似于 Instagram、twitter 和 Stack Overflow。我的应用程序。以下是我们的应用程序使用的架构特定点

  • 标签应该属于一个问题
  • 标签是用户特定的。
  • 每个用户都可以用相同的主题标签标记他们的问题

目前,我有两个用于上述架构的 JPA 模型

适用于两种型号的通用表

表格:问题

列:id、问题、描述等,

表:用户

列:id、名称、角色、组等,

模型 1

表格:question_hash_tags

列:id、question_id、user_id、hashtag_text

模型 2

表格:主题标签

列:id、hastag_text

表:user_hashtags

列:user_id、hashtag_id、question_id

即使用户之间的标签相同,模型 1 也会有每一行。

模型 2 将具有唯一的主题标签行,并使用 user_hashtags 在用户之间进行引用。

我期待这两个模型之外的更好和标准的模型。

注意:可以根据主题标签和用户搜索问题

【问题讨论】:

  • 这里没有明确的问题,而是要求我们为您的域问题提供完美的设计。像这样的不好的问题无法关闭。
  • 如果没有看到暂定的SELECT 语句,我就忍不住要设计一个架构。

标签: java mysql hibernate jpa


【解决方案1】:

我们在这里有三个“东西”:用户、问题和标签。在您的示例中,我会避​​免使用模型 #1,主要是因为它不灵活。当产品所有者后来决定向标签添加描述时会发生什么?如果产品所有者下周希望用户仅从现有池中选择标签会怎样?虽然您的领域需求不完整且不清楚,但更灵活的解决方案允许在您的模型之上实现未来的功能,而无需进行重大重构。话虽如此,我肯定会认为 Hashtag 是它自己的独立实体。

在您的第二个模型中,USER_HASHTAGS 表中表示的第三级关系似乎假定用户和问题之间存在可选的多对多关系。如果您的域确实需要许多用户可以编写相同的问题,我认为模型 #2 将满足您的需求。更有可能的是,您的要求可能会明确限制多个用户编写单个特定问题的能力。如果是这样,则应在模型中考虑此类约束。另外,如果用户决定用 5 个不同的标签标记问题,则用户和问题之间的一对多关系将在您的 user_hashtags 表中为每个标签断言 5 次。显示用户提出的问题的简单查询将涉及使用 DISTINCT,它应该立即对设计敲响警钟。

假设用户和问题之间是一对多的关系,我会从“USER_HASHTAGS”表中删除 USER_ID,而是将 USER_ID 作为外键放入 QUESTIONS 表中。然后将您的 USER_HASHTAGS 表重命名为 QUESTION_HASHTAGS。这使得它既简单又高效,因为现在您可以简单地查询单个表来获取由特定 USER_ID 编写的问题,而无需加入和添加数据库需要使用 DISTINCT 过滤的谁知道多少重复项。现在还支持用户选择不为其问题包含主题标签的可能性(没有空白外键)。

注意:

有许多因素会影响数据库的物理设计——不仅是“选择”访问模式,甚至可能是每种访问模式的相对频率。数据库写入与读取之间的比率、可以更新的内容以及与其他更新相关的频率等也会影响您最终构建表的方式。因此,没有“确定的”答案,只是基于一些假设和您问题中提供的有限信息的答案。

【讨论】:

    【解决方案2】:

    标签已经是一种 ID,因此不需要专门的表格。您只需要questions 表:

    create table questions (
        id bigint not null constraint questions_pkey primary key,
        user_id bigint constraint fk_questions_users references users,
        question text not null;
    )
    

    questions_hashtags关系表,索引由hashtag字段:

    create table questions_hashtags (
        question_id bigint not null fk_questions_hashtags_questions references questions,
        hashtag text not null,
        constraint uk_questions_hashtags unique (question_id, hashtag)
    );
    
    create index index_questions_hashtags_hashtag on questions_hashtags(hashtag);
    

    (这里是 PostgreSQL 方言。)

    这些表简单映射到单个(!)实体(不考虑 User 实体):

    @Entity
    @Table(name = "questions")
    public class Question {
        @Id
        @GeneratedValue
        private Long id;
    
        @Column(nullable = false)
        private String question;
    
        @ManyToOne(optional = false)
        private User user;
    
        @CollectionTable(name = "questions_hashtags", joinColumns = @JoinColumn(name = "question_id"))
        @Column(name = "hashtag")
        @ElementCollection(fetch = FetchType.EAGER)
        @BatchSize(size = 20)
        private Set<String> hashtags = new HashSet<>();
    
        public Question(User user, String question) {
            this.user = user;
            this.question = question;
        }
    
        private Set<String> extractHashtags() {
          // extract hashtags from question to Set...
        }
    
        @PrePersist
        @PreUpdate
        private void populateHashtags() {
            hashtags.clear();
            hashtags.addAll(extractHashtags());
        }
    
        // other stuff
    }
    

    这是一个非常方便的模型。要使用主题标签创建和保存问题,您只需执行以下操作:

    questionRepo.save(new Question(user, question));
    

    要获取所有主题标签,您可以使用questionRepo 的这种查询方法:

    @Query("select distinct h as hashtag from Question q join q.hashtags h")
    List<String> getAllHashtags();
    

    要查找与特定主题标签相关的所有问题,您可以使用以下查询方法:

    @Query("select q from Question q join q.hashtags h where h = ?1")
    List<Question> getQuestionsByHashtag(String hashtag);
    

    要通过多个主题标签查找问题,您可以使用此方法:

    @Query("select q from Question q join q.hashtags h where h in (?1)")
    List<Question> getQuestionsByHashtag(Set<String> hashtags);
    

    要查找与给定主题标签相关的用户,您可以使用此方法:

    @Query("select distinct u from Question q join q.user u join q.hashtags h where h in (?1)")
    List<User> getUsersByHashtag(Set<String> hashtags);
    

    查看我的 REST 服务主题标签使用示例sb-hashtag-usage-example

    1) POST /users - 创建新用户

    {
        "name": "user1"
    }
    

    2) POST /questions - 创建新问题

    {
        "question": "How implement best JPA #entity #model for Hashtags?",
        "user": "/user/1"
    }
    

    3) GET /hashtags - 获取所有主题标签

    4) GET/questions/search/by_hashtag?hashtag=%23model - 通过一个标签获取问题

    5) GET /questions/search/by_hashtags?hashtags=%23entity,%23model - 通过多个主题标签获取问题

    6) GET /users/search/by_hashtags?hashtags=%23entity - 通过多个主题标签获取用户

    (也可以使用其他方法,如 PATCH、DELETE。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-05
      • 2012-11-01
      • 1970-01-01
      • 2011-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-18
      相关资源
      最近更新 更多