【发布时间】:2021-04-16 11:47:02
【问题描述】:
上下文
有一个使用 Spring MVC 和 Spring Data JPA 的应用程序。 JPA 提供者是 Hibernate,视图层由 JSP 页面呈现。
有一个用于创建新用户的页面。通过此页面创建新用户时,该用户被分配到一个用户组(从列表中选择)。 User 是新的,但此时选定的 Group 已存在。 User @Entity 中有一组 Group 对象。 Users 和 Groups 之间的双向关联是 @ManyToMany,并且一个表 (group_members) 链接这些实体。
当视图层将User 对象传递给控制器时,setGroups(Set<Group> groups) 方法将在User 上调用。然后这个User 对象通过userDetailsService.add(user) 持久化,即@Transactional。
问题
Groups 在页面呈现时加载到视图中,然后它们与持久性单元分离。
在保持User 时,关联的Group 被分离,Group 端与User 不同步。
User 实体定义了可以同步事务两侧(addGroup(Group group) 和 removeGroup(Group group))的实用方法,但我找不到使用它们的方法,因为这样它们只能在获取的事务之外调用在服务层创建。
问题
在这种情况下,让所有实体始终处于有效状态的最佳方法是什么? User 和 Group 都应该传递给服务层吗?
代码
为了简洁,省略了一些部分。
createuser.jsp
<body>
<sf:form id="details" method="post" action="${pageContext.request.contextPath}/docreate" modelAttribute="user">
<h2>Sing up</h2>
<table class="formtable">
<tr>
<td class="label">Userame: </td> <td><sf:input path="username" name="username" type="text" /> <div class="error"> <sf:errors path="username"></sf:errors> </div></td>
</tr>
<tr>
<td class="label">Password: </td> <td><sf:input id="password" path="password" name="password" type="password" /> <div class="error"><sf:errors path="password"></sf:errors> </div></td>
</tr>
<tr>
<td class="label">Confirm password: </td> <td><input id="confirmpass" name="confirmpass" type="password" /> <div id="matchpass"></div></td>
</tr>
<tr>
<td class="label" align="right">Group</td><td><sf:select id="groups" path="groups" items="${groups}" itemValue="id"
itemLabel="groupName"/></td>
</tr>
<tr>
<td> </td> <td class="button"><input value="Create user" type="submit" /> </td>
</tr>
</table>
</sf:form>
</body>
UserController.java
@RequestMapping(value="/docreate", method=RequestMethod.POST)
public String doCreate(Model model, @Validated(FormValidationGroup.class) User user, BindingResult result) {
// User validation ...
user.setEnabled(true);
// Duplicate user check ...
userDetailsService.add(user);
return "usercreated";
}
UserDetailsServiceImpl.java
@Transactional
public void add(User user) {
if (!contains(user.getUsername())) {
userRepository.save(user);
}
}
用户.java
@Entity
@Table(name="users",schema="sec")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String username;
private String password;
private boolean enabled;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name="group_members", schema="sec", joinColumns= { @JoinColumn(name="user_id") }, inverseJoinColumns = { @JoinColumn(name="group_id") } )
private Set<Group> groups = new HashSet<>();
// ...
public void addGroup(Group group) {
this.groups.add(group);
group.getUsers().add(this);
}
public void removeGroup(Group group) {
this.groups.remove(group);
group.getUsers().remove(this);
}
public void setGroups(Set<Group> groups) {
for (Group group : groups) {
this.groups.add(group);
}
}
}
Group.java
@Entity
@Table(name="groups",schema="sec")
public class Group {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String groupName;
// ...
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "groups")
private Set<User> users = new HashSet<>();
// Method to synchronize bidirectional association
public void addUser(User user) {
this.users.add(user);
user.getGroups().add(this);
}
public void removeUser(User user) {
this.users.remove(user);
user.getGroups().remove(this);
}
// ...
public Set<User> getUsers() {
return users != null ? users : new HashSet<>();
}
public void setUsers(Set<User> users) {
this.users = users;
}
// ...
}
【问题讨论】:
标签: java spring hibernate spring-mvc jsp