持久化对象 jpa 时出现 java.lang.StackOverflowError
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13224145/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
java.lang.StackOverflowError when persisting an object jpa
提问by lv10
I am building an application using JPA, JSF, EJB, Derby. At this point the application is still small. I have a form in the application to add new products. When adding data to the db it goes smoothly until I restart the application or the server. When I restart either the server or app I get java.lang.StackOverflowError
, I still can query the db for the data represented by the product db, but creation product is not possible. I have only 5 entries in the db, as of now, but I am concerned about this happening so early.
我正在使用 JPA、JSF、EJB、Derby 构建应用程序。此时应用程序仍然很小。我在应用程序中有一个表单来添加新产品。将数据添加到数据库时,它会顺利进行,直到我重新启动应用程序或服务器。当我重新启动服务器或应用程序时java.lang.StackOverflowError
,我仍然可以查询数据库以获取产品数据库所代表的数据,但无法创建产品。到目前为止,我在 db 中只有 5 个条目,但我很担心这种情况会这么早发生。
This is the Ejb (Getter, setter and constructors removed for simplicity):
这是 Ejb(为了简单起见,删除了 Getter、setter 和构造函数):
@Stateless
public class ProductEJB{
@PersistenceContext(unitName = "luavipuPU")
private EntityManager em;
public List<Product> findAllProducts()
{
TypedQuery<Product> query = em.createNamedQuery("findAllProducts", Product.class);
return query.getResultList();
}
public Product findProductById(int productId)
{
return em.find(Product.class, productId);
}
public Product createProduct(Product product)
{
product.setDateAdded(productCreationDate());
em.persist(product);
return product;
}
public void updateProduct(Product product)
{
em.merge(product);
}
public void deleteProduct(Product product)
{
product = em.find(Product.class, product.getProduct_id());
em.remove(em.merge(product));
}
this is the ProductController (Getter, setter and constructors removed for simplicity):
这是 ProductController(为了简单起见,删除了 Getter、setter 和构造函数):
@Named
@RequestScoped
public class ProductController {
@EJB
private ProductEJB productEjb;
@EJB
private CategoryEJB categoryEjb;
private Product product = new Product();
private List<Product> productList = new ArrayList<Product>();
private Category category;
private List<Category> categoryList = new ArrayList<Category>();
public String doCreateProduct()
{
product = productEjb.createProduct(product);
productList = productEjb.findAllProducts();
return "listProduct?faces-redirect=true";
}
public String doDeleteProduct()
{
productEjb.deleteProduct(product);
return "deleteProduct?faces-redirect=true";
}
public String cancelDeleteAction()
{
return "listProduct?faces-redirect=true";
}
@PostConstruct
public void init()
{
categoryList = categoryEjb.findAllCategory();
productList = productEjb.findAllProducts();
}
Category Entity (Getters, setters, hash() and constructors removed for simplicity):
类别实体(为简单起见,删除了 Getter、setter、hash() 和构造函数):
@Entity
@NamedQueries({
@NamedQuery(name= "findAllCategory", query="SELECT c FROM Category c")
})
public class Category implements Serializable
{
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy= GenerationType.AUTO)
private int category_id;
private String name;
private String description;
@OneToMany(mappedBy = "category_fk")
private List<Product> product_fk;
// readObject() and writeObject()
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
{
// default deserializer
ois.defaultReadObject();
// read the attributes
category_id = ois.readInt();
name = (String)ois.readObject();
description = (String)ois.readObject();
}
private void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException
{
// default serializer
oos.defaultWriteObject();
// write the attributes
oos.writeInt(category_id);
oos.writeObject(name);
oos.writeObject(description);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Category other = (Category) obj;
if (this.category_id != other.category_id) {
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if ((this.description == null) ? (other.description != null) : !this.description.equals(other.description)) {
return false;
}
if (this.product_fk != other.product_fk && (this.product_fk == null || !this.product_fk.equals(other.product_fk))) {
return false;
}
return true;
}
Product Entity (Getters, setters, hash() and constructors removed for simplicity):
产品实体(为简单起见,删除了 Getter、setter、hash() 和构造函数):
@Entity
@NamedQueries({
@NamedQuery(name="findAllProducts", query = "SELECT p from Product p")
})
public class Product implements Serializable
{
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy= GenerationType.AUTO)
private int product_id;
private String name;
private String description;
protected byte[] imageFile;
private Float price;
@Temporal(TemporalType.TIMESTAMP)
private Date dateAdded;
@ManyToOne
private Category category_fk;
@ManyToOne
private SaleDetails saleDetails_fk;
// readObject() and writeObject() methods
private void readObject (ObjectInputStream ois)throws IOException, ClassNotFoundException
{
// default deserialization
ois.defaultReadObject();
// read the attributes
product_id = ois.readInt();
name = (String)ois.readObject();
description = (String)ois.readObject();
for(int i=0; i<imageFile.length; i++ )
{
imageFile[i]=ois.readByte();
}
price = ois.readFloat();
dateAdded = (Date)ois.readObject();
category_fk = (Category)ois.readObject();
saleDetails_fk = (SaleDetails)ois.readObject();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Product other = (Product) obj;
if (this.product_id != other.product_id) {
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if ((this.description == null) ? (other.description != null) : !this.description.equals(other.description)) {
return false;
}
if (!Arrays.equals(this.imageFile, other.imageFile)) {
return false;
}
if (this.price != other.price && (this.price == null || !this.price.equals(other.price))) {
return false;
}
if (this.dateAdded != other.dateAdded && (this.dateAdded == null || !this.dateAdded.equals(other.dateAdded))) {
return false;
}
if (this.category_fk != other.category_fk && (this.category_fk == null || !this.category_fk.equals(other.category_fk))) {
return false;
}
if (this.saleDetails_fk != other.saleDetails_fk && (this.saleDetails_fk == null || !this.saleDetails_fk.equals(other.saleDetails_fk))) {
return false;
}
return true;
}
private void writeObject(ObjectOutputStream oos) throws IOException, ClassNotFoundException
{
// default serialization
oos.defaultWriteObject();
// write object attributes
oos.writeInt(product_id);
oos.writeObject(name);
oos.writeObject(description);
oos.write(imageFile);
oos.writeFloat(price);
oos.writeObject(dateAdded);
oos.writeObject(category_fk);
oos.writeObject(saleDetails_fk);
}
This is the stacktrace:
这是堆栈跟踪:
javax.faces.el.EvaluationException: java.lang.StackOverflowError
at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
at javax.faces.component.UICommand.broadcast(UICommand.java:315)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.StackOverflowError
at java.util.Vector$Itr.<init>(Vector.java:1120)
at java.util.Vector.iterator(Vector.java:1114)
at java.util.AbstractList.hashCode(AbstractList.java:540)
at java.util.Vector.hashCode(Vector.java:988)
at org.eclipse.persistence.indirection.IndirectList.hashCode(IndirectList.java:460)
at com.lv.Entity.Category.hashCode(Category.java:96)
at com.lv.Entity.Product.hashCode(Product.java:148)
at java.util.AbstractList.hashCode(AbstractList.java:541)
回答by Sajan Chandran
Your Category
class has list of Products
and in the equals
method of the Category
class you are doing
你的Category
班级有你正在做的班级的列表Products
和equals
方法Category
if (this.product_fk != other.product_fk && (this.product_fk == null || !this.product_fk.equals(other.product_fk))) {
return false;
}
which invokes the equals
method on the Product
class, the equals
method on the Product
class which does
它调用equals
在方法Product
的类,该equals
方法在Product
其不类
if (this.category_fk != other.category_fk && (this.category_fk == null || !this.category_fk.equals(other.category_fk))) {
return false;
}
which invokes the equals
method on the Category
once again and the whole process repeats causing an Stack overflow.
再次调用equals
方法,Category
整个过程重复导致堆栈溢出。
Solution:
解决方案:
- Either remove the bi-direction dependency.
- Fix the equals method.
- 要么删除双向依赖项。
- 修复equals方法。
Hope it helps.
希望能帮助到你。
回答by Yogendra Singh
I am suspecting circular dependency as the root cause of the issue. I am thinking you have mapped Product
either in Category
or SaleDetails
or both the objects. If so, it will call circular reference issue while serializing the Product
object, while will result into StackOverFlow
error.
我怀疑循环依赖是问题的根本原因。我想你已经映射Product
无论是在Category
或SaleDetails
或两者的对象。如果是这样,它会在序列化Product
对象时调用循环引用问题,同时会导致StackOverFlow
错误。
I think you have two options:
我认为你有两个选择:
- Remove
bi-dreictional
mapping if it can be avoided. - Please implement
readObject()
andwriteObject()
methods in yourProduct
,Category
andSaleDetails
classes and avoid reading/writing objects in circles.
bi-dreictional
如果可以避免,请删除映射。- 请在您的,和类中实现
readObject()
和writeObject()
方法,并避免在圈子中读取/写入对象。Product
Category
SaleDetails
EDIT:
编辑:
private void writeObject(ObjectOutputStream oos) throws IOException {
// default serialization
oos.defaultWriteObject();
// write the object attributes
oos.writeInt(product_id);
oos.writeObject(name);
oos.writeObject(description);
oos.write(imageFile);
oos.writeFloat(price);
oos.writeObject(dateAdded);
oos.writeObject(category_fk);
oos.writeObject(saleDetails_fk);
}
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
// default deserialization
ois.defaultReadObject();
//read the attributes
product_id = ois.readInt();
name = (String)ois.readObject();
description = (String)ois.readObject();
imageFile = ois.read();
price = ois.readFloat();
dateAdded = (Date)ois.readObject();
category_fk = (Category)ois.readObject();
saleDetails_fk = (SaleDetails)ois.readObject();
}
Hope this helps.
希望这可以帮助。
回答by Sashi
As @Sajan mentioned. You have a cyclic dependency in your equals(). You need to change the equals() method in your Category class not to refer to to 'Product' list and 'categoryId' . It should probably be as follows -
正如@Sajan 提到的。您的equals() 中有循环依赖项。您需要更改 Category 类中的 equals() 方法,不要引用 'Product' 列表和 'categoryId' 。大概应该是这样的——
public class Category implements Serializable
{
...
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if ((this.description == null) ? (other.description != null) : !this.description.equals(other.description)) {
return false;
}
return true;
}
}
In the equals() method in Product class you may have to remove 'ProductId' and 'Price'.
在 Product 类的 equals() 方法中,您可能需要删除“ProductId”和“Price”。
The equals() and hashcode() methods are important because they determine object equality when you use objects in detached state and add them to a java.util.Set. The recommended implementation is to use 'properties that form a natural key identifier' in your equals() and hashcode() implementations.
equals() 和 hashcode() 方法很重要,因为当您使用处于分离状态的对象并将它们添加到 java.util.Set 时,它们确定对象是否相等。推荐的实现是在你的 equals() 和 hashcode() 实现中使用“形成自然键标识符的属性”。
Category - Let's say you had CategoryA with ProductA and ProductB. It is not possible that the same ProductA and ProductB will be tied to a different category called CategoryB. So, they should not be part of the equals() implementation.
类别 - 假设您有类别 A 和产品 A 和产品 B。相同的 ProductA 和 ProductB 不可能绑定到称为 CategoryB 的不同类别。因此,它们不应该是 equals() 实现的一部分。
Product - Whether to include 'Category' in Product.equals() depends on whether 'Category' is part of the natural key identifier for a Product. And the fact that you are using a List inside Category means that you are not too concerned about object equality. If you are concerned about equality(), I would recommend that you change it to a Set.
产品 - 是否在 Product.equals() 中包含“类别”取决于“类别”是否是产品的自然键标识符的一部分。您在 Category 中使用 List 的事实意味着您不太关心对象相等性。如果您担心equals(),我建议您将其更改为Set。
If you had the following scenario -
如果你有以下场景——
Category -
Electronics
Product1 -
name - Camera | price - $100 | category - Electronics
Product2 -
name - HandyCam | Price - $200 | Category - Electronics
类别 -
电子产品
产品1 -
名称 - 相机 | 价格 - 100 美元 | 类别 - 电子
产品2 -
名称 - HandyCam | 价格 - 200 美元 | 类别 - 电子产品
If 'Electronics' category has Set of two products as shown above. If you had the following sample code -
如果“电子”类别有如上图所示的两个产品组合。如果您有以下示例代码 -
session.startTransaction();
Category electronics = session.get(Category.class, 1234);
Set<Product> products = electronics.getProducts();
session.commit();
Product camera = product.get(0);
camera.setPrice("300");
products.add(camera);
When you change the price of camera and add it back in to the Set, you want to make sure that the set still contains only two elements and not add a third new element because you are modifying an existing product, not adding a new Product.
当您更改相机的价格并将其重新添加到 Set 时,您希望确保该集合仍然只包含两个元素,并且不会添加第三个新元素,因为您正在修改现有产品,而不是添加新产品。
For the above scenario, you need to have 'Category' and 'name' in the equals() method of 'Product'.
对于上述场景,您需要在 'Product' 的 equals() 方法中包含 'Category' 和 'name'。
回答by Jorn
From the List.equals
and Vector.equals
calls, I would think there's a list X
containing a vector Y
containing list X
somewhere in your entity. Performing an equals call on that list will iterate through that list, which will iterate through the vector, which will iterate through the list... and so on.
从List.equals
andVector.equals
调用中,我认为有一个列表X
包含一个向量,Y
其中包含X
您实体中某处的列表。对该列表执行 equals 调用将遍历该列表,该列表将遍历该向量,该向量将遍历该列表......等等。
回答by thedayofcondor
It seems that the problem is in the Category class - the equals does something which in turn calls equals, creating an endless loop
问题似乎出在 Category 类中 - equals 做了一些事情,然后又调用了 equals,从而创建了一个无限循环