我正在使用休眠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
的新实例(例如),但没有为它配置级联保存。