在这里构建一个由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和群聊都一样)是什么?
1条答案
按热度按时间prdp8dxp1#
这是正确的方法还是有更好的方法来关联这里的表?
您的方法在db层似乎还可以,只是如果
users_x_conversations
仅用作联接表(即,如果没有与中表示的(用户、会话)关联相关联的额外属性),则我将使用(conversation_id, user_id)
作为它的pk,而不是给它一个代理密钥。如果不这样做,那么至少应该对该对设置唯一性约束。我现在的问题是:我应该如何在jpa层为我的[users\ux\u conversations]表建模?
我想问您是否应该将该表建模为一个实体。如果您坚持要像以前那样给它一个代理密钥,那么这就意味着“是”。但正如我已经讨论过的,我不认为这是必要的。也没什么用。我建议您在
Conversation
以及User
实体,使用此表(减去其id
列)用作联接表:注意在这种情况下
User
以及Conversation
实体在对象模型中直接关联。另一方面,如果你选择做模特
users_x_conversations
通过它自己的实体,那么你为它呈现的代码是完全错误的。它看起来更像这样:请注意:
这使得
Conversation
s和User
s间接,viaUserConversation
实体。如果这些关系可以从另一端导航,那么它们将通过@OneToMany
类型的关系字段Set<UserConversation>
或者List<UserConversation>
.它需要更多的代码和运行时系统中更多的对象。
另一方面,它确实有一个小优势,那就是可以避免您在选择直接对话的哪一方时有点武断
@ManyToMany
关系是拥有的一方。