使用@Version列不允许@ManyToOne关系属性作为引用



我正在使用休眠3.6.3.Final。我有两个关系实体A和B,单向ManyToOne定义为:


@Entity public class A {
...
@Version @Column ( name = "ChangeNumber" )
public Long getVersion() { return version; }
@ManyToOne @JoinColumn( name = "B_ID" )
public B getRelationalB() { return relationalB; }
...
}
@Entity public class B {
@Version @Column ( name = "ChangeNumber" )
public Long getVersion() { return version; }
....
}

现在假设我已经有一个 b 实例持久化在 db 中,pk id = 1,然后执行以下操作:


A a = new A();
a.setRelationalB( new B( 1 ) );
session.saveOrUpdate( a ) ;
throws an the famous "TransientObjectException: object references an unsaved transient..." exception. Surprisingly enough, if @Version @Column is removed or made @Transient, the above code would work perfectly fine. Any idea why i'm observing this behavior?

The problem is that you're not using the entity B which is in the database

A a = new A();
// you're instantiating a "new" B with id 1, this is not the same 
// as the "object" you have in the database.
a.setRelationalB( new B( 1 ) );   
session.saveOrUpdate( a ) ;

问题是休眠无法识别您创建的新 B 与存储在数据库上的 B 相同。如果您想知道这背后的原因,请阅读工作单元模式。

尝试做类似的事情

B b = sessions.get(B.class, 1)
A a = new A();
a.setRelationalB( b );
session.saveOrUpdate( a ) ;

关于另一点:如果你想一次性保存两个对象的新实例,那么你需要做一个级联。

我有"对象引用未保存的瞬态..."添加@Version列后也异常很多。这是我在我的案例中解决它的方法:

在我的场景中,问题出在外键上。编写代码是为了不加载引用的对象,而是创建一个新实例并设置主键,希望 Hibernate 能够找到真正的实例。我再说一遍:在@Version的事情之前,它工作得很好。

说明:

考虑数据库中存在 id 1 的类 B 对象,现在我们正在保存 A 的新实例。

  • 以前(不好)(添加后@Version例外):

    a.setB(new B(1));

    sf.getCurrentSession().save(a);

  • 固定(运行良好) - 使用@Version没有问题:

    a.setB((B) sf.getCurrentSession().get(B.class, 1));

    sf.getCurrentSession().save(a);

调试 Hibernate 时,我发现当@Version列存在时,它会进行一些额外的检查。

因此,我相信遇到此错误的人要么没有加载他们试图引用的对象(无论如何这是一种更好的做法),要么他们真的在尝试保存B的新实例(例如),但没有为它配置级联保存。

最新更新