问题 Hibernate OneToOne映射实体将所有属性设置为null


使用Hibernate 5,Spring 4

请考虑以下代码和两个实体之间的映射:

用户类

@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
private TruckOwner truckOwner;

//下面的getter setters

TruckOwner类

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

//下面的getter setter

当我的代码尝试更新内部的值时 user 类如下代码: UserServiceImpl类

@Override
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void resetPassword(Long userId,String newPassword) {

    User user = userDAO.findById(userId);

    user.setPassword(newPassword);
    System.out.println(user.getTruckOwner().getTruckOwnerId());     
    userDAO.merge(user);
}

打电话时 userDAO.merge(user); 我得到以下错误: 非瞬态实体具有空id:com.mymodel.TruckOwner

我在项目的很多地方遇到这种问题,请帮我解决这个问题,为什么TruckOwner类的所有内容都由hibernate设置为null?


1273
2018-04-12 05:42


起源

尝试打印用户ID - Jay Smith
试过。它打印预期的一个...用户对象没有问题。
您使用什么ID生成策略? - galovics
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)  这个
那完全无效的答案。你不能保存/合并 truckOwner 通过调用对象 userDAO.merge  userDAO和truckOwnerDAO是分开的。用户和卡车实体彼此完全不同,仅通过映射连接。


答案:


我们应该知道的实施 userdao merge 方法,但我猜它被称为 merge 休眠的方法 Session 接口 在任何情况下,非瞬态对象都是 TruckOwner 目的;你打电话时,hibernat不会获取该对象 System.out.println(user.getTruckOwner().getTruckOwnerId()); 此外,在那一点上你离开了休眠会话,如果你调用除了getTruckOwnerId()之外的任何其他getOwner getter你应该得到 org.hibernate.LazyInitializationException (或类似..我不记得了)

我想你有两个选择:

  1. 根据staszko032的建议,您应该将获取类型更改为EAGER
  2. 使用时加载用户对象 userDAO.findById(userId); 你应该 fetch 通过调用其中的任何其他方法,在hibernate会话中的truckOwner对象 userDAO.findById(userId); 实现和hibernate会话内部

我希望它有用

安杰洛


3
2018-04-17 07:43





试试卡车类:

@Entity
@Table(name = "truckOwner")
public class TruckOwner{
...
private User user;


@OneToOne(fetch = FetchType.LAZY, mappedBy = "truckOwner", cascade = CascadeType.ALL)
public User getUser() {
    return this.user;
}
}

这对于User类:

@Entity
@Table(name = "user")
public class User{
     private TruckOwner truckOwner;


    @OneToOne(fetch = FetchType.LAZY)
    @PrimaryKeyJoinColumn
     public TruckOwner getTruckOwner() {
          return this.truckOwner ;
     }

}

3
2018-04-18 14:31





如果您正在制作生产应用程序,则渴望模式不是解决方案。当您尝试getTruckOwner时,您的会话中的问题已经关闭。尝试将会话传播给所有人 resetPassword 方法。


3
2018-04-21 07:04





首先你不应该使用 merge 这里!你几乎不应该使用合并。

当您拥有非托管实体(由先前的持久化上下文序列化或加载)并希望将其合并到当前持久性上下文中时,应使用合并,以使其成为托管实体。由于您的DAO在容器管理的事务中加载了持久性上下文,因此您的实体已经被管理。这意味着您甚至不必调用save,在事务提交时将检测并保留对托管实体的任何更改。

从表面上看,JPA看起来很容易,因为很多复杂性在表面上看不到,当我7年前开始使用TopLink时,上帝知道我撞到了墙头,但在阅读之后 物体生命周期 和应用程序与容器管理的持久性上下文,我犯了更多的错误,并且更容易破译错误消息。


3
2018-04-21 20:37





另一种解决方案是将用户类中的提取类型更改为EAGER模式。使用LAZY模式时,hibernate不会检索与用户连接的TruckOwner,因为在您的情况下并不明确需要它。最终,TruckOwner对于用户来说是空的,但是它设置了nullable = false选项,这就是合并失败的原因。


1
2018-04-12 07:43



试过了。不工作..相同的错误来了..而且,在懒惰提取的情况下,hibernate应该/必须获取 TruckOwner 对象当 System.out.println(user.getTruckOwner().getTruckOwnerId());  如果我没有错,就行了。