我正在使用Spring Framework v3.2.4和JPA + Hibernate 4实现RESTful服务。 我正在以JSON格式返回资源(使用Jackson Mapper),但现在我遇到了这个例外:
Could not write JSON: failed to lazily initialize a collection of role: it.teck.service.model.Canvas.params, could not initialize proxy - no Session (through reference chain: it.teck.service.model.Canvas["params"]);
Canvas
和Param
个实体之间存在“多对多”的关系,我需要在向服务请求画布时序列化params列表。
在我的课程中,我有:
@Entity
public class Canvass {
@ManyToMany
@JoinTable(name = "canvas_params", joinColumns = { @JoinColumn(name = "id_canvas", referencedColumnName = "id_canvas") }, inverseJoinColumns = { @JoinColumn(name = "id_param", referencedColumnName = "id_param") })
private List<Param> params;
// ...
}
和
@Entity
public class Param {
@ManyToMany(mappedBy = "params")
private List<Canvas> canvasList;
// ...
}
在几个帖子和SO答案中建议从序列化中排除ManyToMany
字段,以打破序列化循环,但我需要序列化链接到我的画布实体的参数。那么,我该怎么办?
答案 0 :(得分:4)
这是因为Jackson试图在会话之外访问由Hibernate管理的bean的属性。因此该属性是延迟加载的,当您尝试在会话外访问它时,hibernate将无法从数据库中获取它。
您有三种选择:
对于Spring,还有另一种选择,它包括使控制器方法发生序列化@Transactional,这将使会话保持打开状态!但它会使该方法具有事务性,因此要小心不必要的后果。
答案 1 :(得分:3)
由于序列化时间的Hibernate延迟加载,最终会出现Hibernate代理集合。在这个时间段,它已经关闭或不在范围内。
您可以编写在范围内具有JPA实体管理器的自定义转换器(查找OpenEntityManagerInViewFilter
示例)或者如果性能不是问题(集合非常小)并且您始终需要完全填充的对象,则可以指定对映射的热切提取策略:
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "canvas_params", joinColumns = { @JoinColumn(name = "id_canvas", referencedColumnName = "id_canvas") }, inverseJoinColumns = { @JoinColumn(name = "id_param", referencedColumnName = "id_param") })
private List<Param> params;
答案 2 :(得分:0)
您的问题似乎没有链接到序列化循环,而是链接到在检索父对象的事务外部完成的序列化。
要解决此问题,您可以在此tx中进行序列化吗?
您还有其他几个选项,在hibernate“lazily init exception”线程中描述如:failed to lazily initialize a collection of role
答案 3 :(得分:0)
提供的其他答案确实涵盖了问题的原因。对于解决方案,我建议你研究类似六角架构的东西。
基本上,您需要一个存储库层来获取要使用的数据,强制您自己将实体数据对象映射到域数据对象,实际上您将绕过此问题。原因是你正在分离你的担忧。您将在一个位置加载您将使用的所有数据(不是您不会使用的额外数据),然后您在不同的位置执行任何逻辑。这将使您首先完成所有数据库工作并释放这些资源,以便您可以随意使用逻辑。
用于此目的的一个很好的链接是:http://alistair.cockburn.us/Hexagonal+architecture
即使你的实施并不严格,应用它所涉及的一些学科也会有很大的帮助。
希望这有帮助。
答案 4 :(得分:0)
您可以尝试以下操作来更改运行时的提取策略以克服此问题。
User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
“FetchMode”中还有更多选项。