在我的Jboss7 Java EE 6 Web应用程序中,我需要管理像这样的简单“类别”实体的树结构:
@Entity
@Table(name="categorie")
@NamedQueries({
@NamedQuery(name="selezionaTutti", query="select c from Categoria c left join fetch c.children left join fetch c.parent")
})
public class Categoria implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
private String nome;
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
@JoinColumn(name = "parent_id")
private List<Categoria> children = new LinkedList<Categoria>();
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "parent_id",insertable=false,updatable=false)
private Categoria parent;
... //various getter, setter, and so on
}
命名查询一次加载我的整个(小)树,第一次,然后它停留在持久化上下文中。
然后我想“探索”树,所以我得到根节点并将其传递给这个函数:
private List<Categoria> getAlberoCategorie(Categoria root, int profondita) {
List<Categoria> tmpList = new ArrayList<Categoria>();
root.setProfondita(profondita);
if ( root.getParent() != null ) {
tmpList.add(root);
}
if (!root.getChildren().isEmpty()) {
profondita++;
for (Categoria figlia : root.getChildren()) {
tmpList.addAll(getAlberoCategorie(figlia,profondita)); // this line generates the stack overflow!!!
}
}
return tmpList;
}
异常的确切堆栈跟踪是:
java.lang.StackOverflowError的 org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112) org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:137) org.hibernate.collection.internal.PersistentBag.isEmpty(PersistentBag.java:249) it.trew.data.CategoriaFacade.getAlberoCategorie(CategoriaFacade.java:59)
在我的本地机器中,一切正常。 在小型测试服务器上,它在读取类别时崩溃了!
如何改善我的功能?
答案 0 :(得分:0)
超出了您的筹码空间。你在堆栈上放了太多数据。这可能是因为您使用了太多局部变量(局部变量保留在堆栈上),或者因为递归深度是深或无限的。
您可以做什么:按此顺序:
1)检查无限递归。如果类别实体在其子列表中具有自身或其父或祖先,则会发生这种情况。在这种情况下,你得到一个无限循环(由于无限的重新生成),当堆栈满时,它会中断,这恰好是你得到的异常。
要检查这一点,您可以查看数据库或调试代码,或者将递归深度打印到System.out(您已经在参数profondita
中有深度)。
如果没有不定式循环:
2)tmpList保持在堆栈上。可能,那里需要太多的空间。将它作为参数而不是返回值(最终不那么漂亮,但使用更少的资源):
private void getAlberoCategorie(List<Categoria> list, Categoria root, int profondita) {
root.setProfondita(profondita);
if ( root.getParent() != null ) {
list.add(root);
}
if (!root.getChildren().isEmpty()) {
profondita++;
for (Categoria figlia : root.getChildren()) {
getAlberoCategorie(list, figlia,profondita);
}
}
}
如果这仍然没有帮助,深度非常深,但不是无限的:
3)通过迭代替换递归(通用编程技术,但在这种情况下非常难看,因为这是为了递归)。
可能1)(我猜)或2)将解决问题。