休眠,双向更新
Hibernate, bidirecctional updating
我正在尝试使用休眠更新 2 个表(User 和 UserRole)中具有多个角色的用户记录。但是,当我进行保存时,它可以正常工作,但是,当我尝试更新时,如果对象包含存储在数据库中的角色,则会失败。我没有找到好的答案,但我想这不能直接用方法 "update" 完成,我应该在更新中手动实现双向,不是吗?。我关注了structure of Spring Security for users.
这是我的用户 class:
@Entity
@Table(name = "users", schema = "cmsdb")
public class User {
private String username;
private String password;
private boolean enabled;
private Set<UserRole> userRole = new HashSet<UserRole>(0);
public User() {
}
public User(String username, String password, boolean enabled) {
this.username = username;
this.password = password;
this.enabled = enabled;
}
public User(String username, String password,
boolean enabled, Set<UserRole> userRole) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.userRole = userRole;
}
@Id
@Column(name = "username", unique = true, nullable = false, length = 45)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Column(name = "password", nullable = false, length = 60)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(name = "enabled", nullable = false)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@OneToMany(fetch = FetchType.EAGER, mappedBy = "user") //,cascade = {CascadeType.ALL}
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.DELETE})
@OnDelete(action = OnDeleteAction.CASCADE)
public Set<UserRole> getUserRole() {
return userRole;
}
public void setUserRole(Set<UserRole> userRole) {
this.userRole = userRole;
}
}
这是我的用户角色 class:
@Entity
@Table(name = "user_roles", schema = "cmsdb",
uniqueConstraints = @UniqueConstraint(
columnNames = { "role", "username" }))
public class UserRole {
private Integer userRoleId;
private User user;
@ValidRole
private String role;
public UserRole () {
}
public UserRole(User user, String role) {
this.user = user;
this.role = role;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "user_role_id",
unique = true, nullable = false)
public Integer getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(Integer userRoleId) {
this.userRoleId = userRoleId;
}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "username", nullable = false)
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Column(name = "role", nullable = false, length = 45)
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String toString () {
return role;
}
@Override
public int hashCode() {
return userRoleId;
}
@Override
public boolean equals(Object obj) {
UserRole role = (UserRole) obj;
return (userRoleId == role.getUserRoleId());
}
}
这些是我的 DAO 方法:
// IT WORKS PROPERLY
@Override
@Transactional
public boolean createUser(User user) {
String hashedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(hashedPassword);
Set<UserRole> roles = new HashSet<UserRole> ();
for(UserRole role: user.getUserRole()) {
roles.add(new UserRole(user,role.getRole()));
}
user.setUserRole(roles);
sessionFactory.getCurrentSession().save(user);
return true;
}
// It doesn't work if it tries to update an User with an UserRole stored in the UserRole table in the database
@Override
@Transactional
public boolean updateUser(User user) {
// Get user by id
User userpersis = (User) getSessionFactory().getCurrentSession().load(User.class, user.getUsername());
// Primary key shouldn't be modified
userpersis.setEnabled(user.isEnabled());
userpersis.getUserRole().retainAll( user.getUserRole() );
userpersis.getUserRole().addAll( user.getUserRole() );
sessionFactory.getCurrentSession().update(userpersis);
return true;
}
错误是:
WARN : org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry 'ROLE_MAINTENANCE-asdf' for key 'uni_username_role'
WARN : org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Warning Code: 1062, SQLState: 23000 ...
提前致谢。
在User
中,添加orphanRemoval
级联:
@Cascade(cascade={CascadeType.ALL}, orphanRemoval=true)
@OnDelete(action = OnDeleteAction.CASCADE)
public Set<UserRole> getUserRole() { ...
(参见 JPA 2.0 orphanRemoval=true VS on delete Cascade)
然后,而不是
Set<UserRole> roles = new HashSet<UserRole> ();
// Guess for roles, should be needed to load them also from the db
for(UserRole role : user.getUserRole()) {
roles.add(new UserRole(userpersis,role.getRole()));
}
userpersis.setUserRole(roles);
尝试
userpersis.getUserRole().clear(); // Let Hibernate know that we're really removing all the references
for(UserRole role : user.getUserRole()) {
userpersis.getUserRole().add(new UserRole(userpersis,role.getRole()));
}
虽然事实上,您可能甚至不想删除所有 UserRoles 然后插入所有新数据。你也可以做类似
的事情
userpersis.getUserRole().retainAll( user.getUserRole() );
userpersis.getUserRole().addAll( user.getUserRole() );
但是,这需要您在 UserRole
中实现 equals()
和 hashCode()
。
编辑:当 user
的角色对象不能与 userpersis
的角色对象相同时,当然不起作用,因为对拥有对象的反向引用.
我正在尝试使用休眠更新 2 个表(User 和 UserRole)中具有多个角色的用户记录。但是,当我进行保存时,它可以正常工作,但是,当我尝试更新时,如果对象包含存储在数据库中的角色,则会失败。我没有找到好的答案,但我想这不能直接用方法 "update" 完成,我应该在更新中手动实现双向,不是吗?。我关注了structure of Spring Security for users.
这是我的用户 class:
@Entity
@Table(name = "users", schema = "cmsdb")
public class User {
private String username;
private String password;
private boolean enabled;
private Set<UserRole> userRole = new HashSet<UserRole>(0);
public User() {
}
public User(String username, String password, boolean enabled) {
this.username = username;
this.password = password;
this.enabled = enabled;
}
public User(String username, String password,
boolean enabled, Set<UserRole> userRole) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.userRole = userRole;
}
@Id
@Column(name = "username", unique = true, nullable = false, length = 45)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Column(name = "password", nullable = false, length = 60)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(name = "enabled", nullable = false)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@OneToMany(fetch = FetchType.EAGER, mappedBy = "user") //,cascade = {CascadeType.ALL}
@Cascade({CascadeType.SAVE_UPDATE,CascadeType.DELETE})
@OnDelete(action = OnDeleteAction.CASCADE)
public Set<UserRole> getUserRole() {
return userRole;
}
public void setUserRole(Set<UserRole> userRole) {
this.userRole = userRole;
}
}
这是我的用户角色 class:
@Entity
@Table(name = "user_roles", schema = "cmsdb",
uniqueConstraints = @UniqueConstraint(
columnNames = { "role", "username" }))
public class UserRole {
private Integer userRoleId;
private User user;
@ValidRole
private String role;
public UserRole () {
}
public UserRole(User user, String role) {
this.user = user;
this.role = role;
}
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "user_role_id",
unique = true, nullable = false)
public Integer getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(Integer userRoleId) {
this.userRoleId = userRoleId;
}
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "username", nullable = false)
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Column(name = "role", nullable = false, length = 45)
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String toString () {
return role;
}
@Override
public int hashCode() {
return userRoleId;
}
@Override
public boolean equals(Object obj) {
UserRole role = (UserRole) obj;
return (userRoleId == role.getUserRoleId());
}
}
这些是我的 DAO 方法:
// IT WORKS PROPERLY
@Override
@Transactional
public boolean createUser(User user) {
String hashedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(hashedPassword);
Set<UserRole> roles = new HashSet<UserRole> ();
for(UserRole role: user.getUserRole()) {
roles.add(new UserRole(user,role.getRole()));
}
user.setUserRole(roles);
sessionFactory.getCurrentSession().save(user);
return true;
}
// It doesn't work if it tries to update an User with an UserRole stored in the UserRole table in the database
@Override
@Transactional
public boolean updateUser(User user) {
// Get user by id
User userpersis = (User) getSessionFactory().getCurrentSession().load(User.class, user.getUsername());
// Primary key shouldn't be modified
userpersis.setEnabled(user.isEnabled());
userpersis.getUserRole().retainAll( user.getUserRole() );
userpersis.getUserRole().addAll( user.getUserRole() );
sessionFactory.getCurrentSession().update(userpersis);
return true;
}
错误是:
WARN : org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1062, SQLState: 23000
ERROR: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry 'ROLE_MAINTENANCE-asdf' for key 'uni_username_role'
WARN : org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Warning Code: 1062, SQLState: 23000 ...
提前致谢。
在User
中,添加orphanRemoval
级联:
@Cascade(cascade={CascadeType.ALL}, orphanRemoval=true)
@OnDelete(action = OnDeleteAction.CASCADE)
public Set<UserRole> getUserRole() { ...
(参见 JPA 2.0 orphanRemoval=true VS on delete Cascade)
然后,而不是
Set<UserRole> roles = new HashSet<UserRole> ();
// Guess for roles, should be needed to load them also from the db
for(UserRole role : user.getUserRole()) {
roles.add(new UserRole(userpersis,role.getRole()));
}
userpersis.setUserRole(roles);
尝试
userpersis.getUserRole().clear(); // Let Hibernate know that we're really removing all the references
for(UserRole role : user.getUserRole()) {
userpersis.getUserRole().add(new UserRole(userpersis,role.getRole()));
}
虽然事实上,您可能甚至不想删除所有 UserRoles 然后插入所有新数据。你也可以做类似
的事情userpersis.getUserRole().retainAll( user.getUserRole() );
userpersis.getUserRole().addAll( user.getUserRole() );
但是,这需要您在 UserRole
中实现 equals()
和 hashCode()
。
编辑:当 user
的角色对象不能与 userpersis
的角色对象相同时,当然不起作用,因为对拥有对象的反向引用.