我有一个小型数据结构,我使用 JPA 注释在 Hibernate 中序列化它:
(下面非常简化)
public class Result {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public int id;
@OneToMany(cascade=CascadeType.ALL)
@OrderColumn("row")
@JoinColumn("ResultId)
public List<Row> rows
}
public class Row {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public int id;
@ElementCollection
@OrderColumn("col")
public List<Double> value;
}
当我尝试persist()
结果时,我收到 TransientObjectException。怎么可能? cascade=ALL 不应该解决这个问题吗?
最佳答案
事实证明,我在 Hibernate 和 Guava(Google Collections)之间遇到了相当多的交互。事实上,Guava 的这一功能确实需要更明确地公布。
我的问题中的代码示例不完整。相关的(和缺失的)细节是我有一个 Result
的构造函数,它将 List<List<Double>>
作为参数 - 这对于应用程序的其余部分来说更加自然。
不幸的是,Hibernate 不擅长存储二维数组,因此 Row
类是一个快速解决方法。
为了维护与其余代码的接口(interface),我在构造函数中使用 - 还有什么 - 来自 Guava 的 List<List<>>
将 List<Row>
转换为 Lists.transform
。
当我尝试将生成的对象保存到数据库时,我从 Hibernate 中得到了异常:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: <my class here>
我调试得更深入一些,我意识到 Row
对象调用了 setId
,而不是调用 getId
的行对象(我有 getter 和 setter - 我的代码示例中的另一个简化)。
然后我就明白了。 transform()
生成基础列表的 View ,而不是副本。每次我请求一个转换后的对象时,它都会从头开始创建,并将全新的“id”字段初始化为 0。
让这成为一个教训:如果您的转换添加了额外状态的可能性,请遵循 Guava Lists.transform
文档中的建议:
To avoid lazy evaluation when the returned list doesn't need to be a view, copy the returned list into a new list of your choosing.
关于java - 当所有@OneToMany都是cascade=ALL时,org.hibernate.TransientObjectException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18409083/