【问题标题】:Hibernate, bidirecctional updating休眠,双向更新
【发布时间】:2015-10-14 19:32:02
【问题描述】:

我正在尝试使用休眠更新 2 个表(用户和用户角色)中具有多个角色的用户记录。当我进行保存时,它可以正常工作,但是,当我尝试更新时,如果对象包含存储在数据库中的角色,它将失败。我没有找到一个好的答案,但我想它不能直接使用“更新”方法来完成,我应该在更新中手动实现双向,不是吗?我关注了structure of Spring Security for users

这是我的用户类:

@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;
}
}

这是我的 UserRole 类:

@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 ...

提前致谢。

【问题讨论】:

    标签: java hibernate bidirectional-relation


    【解决方案1】:

    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 的角色对象不能相同时,当然不起作用,因为对拥有对象的反向引用。

    【讨论】:

    • 哦,抱歉,我没有注意到事实上您确实需要新对象。我正在编辑那个“新”信息:)
    • 感谢您的响应,实现了这一点,当我尝试更新具有一个角色的用户时,我得到了以下错误,我猜是因为试图将一个存在于数据库DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [uni_username_role]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
    • 最后我手动执行了 retainaddAll 方法,因为我遇到了 equals() 和 hashCode() 的问题。我看到这种方法在持久性方面存在一些问题,但这是这篇文章的正确答案。谢谢。
    猜你喜欢
    • 2012-10-14
    • 2023-04-09
    • 1970-01-01
    • 2023-04-11
    • 2015-09-10
    • 2016-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多