jpa使用分组和人行横道将数据连接在一起

mwecs4sa  于 2021-06-21  发布在  Mysql
关注(0)|答案(1)|浏览(311)

在这里构建一个由mysql支持的spring boot rest服务。我正在给一个应用程序添加一个超级简单的聊天功能,这个服务将处理它的后端/enndpoints。我是jpa新手,有两个问题:(1)我的原始数据模型本身可能有点错误;(2)我没有使用jpa约定/最佳实践正确 Package 该模型。
首先:我要解决的一个简单问题的概述:用户可以向1+个其他用户发送消息。这将创建一个会话,实际上它只是一个包含1+条消息的容器。如果对话仅在两个用户之间进行,则(应用程序)将其视为直接消息(dm)。否则就被认为是群聊。
我的表(伪架构):

[users]
=======
id PRIMARY KEY AUTO_INC INT NOT NULL,
username VARCHAR(255) NOT NULL

[conversations]
===============
id PRIMARY KEY AUTO_INC INT NOT NULL,
created_on DATETIME NOT NULL

[messages]
==========
id PRIMARY KEY AUTO_INC INT NOT NULL,
conversation_id FOREIGN KEY INT NOT NULL, # on conversations table
sender_id FOREIGN KEY INT NOT NULL, # on users table
text VARCHAR(2000) NOT NULL,
sent_at DATETIME

[users_x_conversations]
=======================
id PRIMARY KEY AUTO_INC INT NOT NULL,
conversation_id FOREIGN KEY INT NOT NULL, # on conversations table
user_id FOREIGN KEY INT NOT NULL, # on users table

所以在我上面的设计中,你可以看到我只是在使用 [conversations] 表作为占位符和将消息分组到单个 conversation_id ,然后 [users_x_conversations] 是人行横道(多对多)表,我实际存储的是谁是哪个对话的“成员”。
这是正确的方法还是有更好的方法来关联这里的表?这就是问题1。
假设我在数据库中正确地建模了问题,那么我有以下jpa/实体类:

@MappedSuperclass
abstract public class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // Ctors, getters & setters down here...
}

@Entity(name = 'messages')
@AttributeOverrides({
        @AttributeOverride(name = 'id', column=@Column(name='message_id'))
})
public class Message extends BaseEntity {
    @OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
    @JoinColumn(name = 'conversation_id', referencedColumnName = 'conversation_id')
    @NotNull
    @Valid
    private Conversation conversation;

    @OneToOne(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
    @JoinColumn(name = 'user_id', referencedColumnName = 'user_id')
    @NotNull
    @Valid
    private User sender;

    @Column(name = 'message_text')
    @NotEmpty
    private String text;

    @Column(name = 'message_sent_at')
    @NotNull
    private Date sentAt;

    // Ctors, getters & setters down here...
}

@Entity(name = 'conversations')
@AttributeOverrides({
        @AttributeOverride(name = 'id', column=@Column(name='conversation_id'))
})
public class Conversation extends BaseEntity {
    @Column(name = 'conversation_created_on')
    @NotNull
    private Date createdOn;

    // Ctors, getters & setters down here...
}

我现在所坚持的是:我应该如何为自己的生活建模 [users_x_conversations] jpa层的table?我是否应该创建这样的内容:

@Entity(name = 'users_x_conversations')
@AttributeOverrides({
        @AttributeOverride(name = 'id', column=@Column(name='users_x_conversations_id'))
})
public class UserConversations extends BaseEntity {
    @ManyToMany(fetch = FetchType.EAGER, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
    @JoinTable(
            name="users_x_conversations",
            joinColumns=[
                    @JoinColumn(name="user_id")
            ],
            inverseJoinColumns=[
                    @JoinColumn(name="conversation_id")
            ]
    )
    private Map<User,Conversation> userConversations;

    // Ctors, getters & setters down here...
}

基本上,我的服务希望能够执行以下查询:
给予 conversationId ,哪些用户是该对话的成员?;和
给予 userId ,用户所参加的所有对话(dm和群聊都一样)是什么?

prdp8dxp

prdp8dxp1#

这是正确的方法还是有更好的方法来关联这里的表?
您的方法在db层似乎还可以,只是如果 users_x_conversations 仅用作联接表(即,如果没有与中表示的(用户、会话)关联相关联的额外属性),则我将使用 (conversation_id, user_id) 作为它的pk,而不是给它一个代理密钥。如果不这样做,那么至少应该对该对设置唯一性约束。
我现在的问题是:我应该如何在jpa层为我的[users\ux\u conversations]表建模?
我想问您是否应该将该表建模为一个实体。如果您坚持要像以前那样给它一个代理密钥,那么这就意味着“是”。但正如我已经讨论过的,我不认为这是必要的。也没什么用。我建议您在 Conversation 以及 User 实体,使用此表(减去其 id 列)用作联接表:

@Entity
@Table(name = "converations")
public class Conversation extends BaseEntity {
    @Column(name = 'conversation_created_on')
    @NotNull
    private Date createdOn;

    @ManyToMany(mappedBy = "conversations")
    @JoinTable(name = "users_x_conversations",
        joinColumns = @JoinColumn(name="conversation_id", nullable = false, updateable = false),
        inverseJoinColumns = @JoinColumn(name = "user_id", nullable = false, updateable = false)
    )
    private Set<User> users;

    // Ctors, getters & setters down here...
}
@Entity
@Table(name = "users")
public class User extends BaseEntity {

    @NotNull
    private String username;

    @ManyToMany(mappedBy = "users")
    // this is the non-owning side of the relationship; the join table mapping
    // is declared on the other side
    private Set<Conversation> conversations;

    // Ctors, getters & setters down here...
}

注意在这种情况下 User 以及 Conversation 实体在对象模型中直接关联。
另一方面,如果你选择做模特 users_x_conversations 通过它自己的实体,那么你为它呈现的代码是完全错误的。它看起来更像这样:

@Entity
@Table(name = "users_x_converations", uniqueConstraints =
        @UniqueConstraint(columnNames={"converation_id", "user_id"}))
public class UserConversation extends BaseEntity {
    @ManyToOne(optional = false)
    @JoinColumn(name = "conversation_id", nullable = false, updatable = false)
    Conversation conversation;

    @ManyToOne(optional = false)
    @JoinColumn(name = "user_id", nullable = false, updatable = false)
    User user;

    // Ctors, getters & setters down here...
}

请注意:
这使得 Conversation s和 User s间接,via UserConversation 实体。如果这些关系可以从另一端导航,那么它们将通过 @OneToMany 类型的关系字段 Set<UserConversation> 或者 List<UserConversation> .
它需要更多的代码和运行时系统中更多的对象。
另一方面,它确实有一个小优势,那就是可以避免您在选择直接对话的哪一方时有点武断 @ManyToMany 关系是拥有的一方。

相关问题