持久化对象jpa时的java.lang.StackOverflowError

时间:2012-11-04 23:39:38

标签: java jpa persistence ejb-3.0

我正在使用JPA,JSF,EJB,Derby构建应用程序。此时应用程序仍然很小。我在应用程序中有一个表单来添加新产品。将数据添加到数据库时,它会顺利进行,直到我重新启动应用程序或服务器。当我重新启动服务器或应用程序时,我得到java.lang.StackOverflowError,我仍然可以在数据库中查询产品数据库所代表的数据,但是无法创建产品。截至目前,我在数据库中只有5个条目,但我很早就关注这种情况。

这是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));
    }

这是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();        
    }

类别实体(为简单起见,删除了Getters,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;
    }

产品实体(为简单起见,删除了Getters,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);

    }

这是堆栈跟踪:

    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)

6 个答案:

答案 0 :(得分:12)

您的Category班级列有Products以及您正在进行的equals班级的Category方法

if (this.product_fk != other.product_fk && (this.product_fk == null || !this.product_fk.equals(other.product_fk))) {
    return false;
}

调用equals类上的Product方法,equals类上的Product方法

if (this.category_fk != other.category_fk && (this.category_fk == null || !this.category_fk.equals(other.category_fk))) {
    return false;
}

再次调用equals上的Category方法,整个过程重复,导致堆栈溢出。

解决方案:

  1. 删除双向依赖关系。
  2. 修正等于方法。
  3. 希望它有所帮助。

答案 1 :(得分:1)

我怀疑循环依赖是问题的根本原因。我想你已经在ProductCategory或两个对象中映射了SaleDetails。如果是这样,它会在序列化Product对象时调用循环引用问题,同时会导致StackOverFlow错误。

我认为你有两个选择:

  1. 如果可以避免,请删除bi-dreictional映射。
  2. 请在readObject()writeObject()Product课程中实施CategorySaleDetails种方法,避免在圈子中阅读/撰写对象。
  3. 编辑:

     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();
        } 
    

    希望这有帮助。

答案 2 :(得分:1)

正如@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;
    }
}

在Product类的equals()方法中,您可能必须删除“ProductId”和“Price”。

equals()和hashcode()方法很重要,因为当您使用处于分离状态的对象并将它们添加到java.util.Set时,它们确定对象相等。 推荐的实现是在equals()和hashcode()实现中使用“形成自然键标识符的属性”。

类别 - 假设您使用ProductA和ProductB进行了CategoryA。同一ProductA和ProductB不可能绑定到名为CategoryB的不同类别。因此,它们不应该是equals()实现的一部分。

产品 - 是否在Product.equals()中包含“类别”取决于“类别”是否是产品的自然键标识符的一部分。而你在类别中使用List的事实意味着你不太关心对象的平等性。如果您担心相等(),我建议您将其更改为Set。

如果您有以下情况 -

  

类别 -

     

电子产品

     

产品1 -

     

名称 - 相机|价格 - 100美元|类别 - 电子产品

     

产品2 -

     

名称 - HandyCam |价格 - 200美元|类别 - 电子产品

如果'电子'类别具有如上所示的两种产品。 如果您有以下示例代码 -

        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);

当您更改相机的价格并将其重新添加到Set中时,您需要确保该集合仍然只包含两个元素,而不是添加第三个新元素,因为您正在修改现有产品,而不是添加新产品。

对于上述场景,您需要在“产品”的equals()方法中使用“类别”和“名称”。

答案 3 :(得分:0)

似乎问题出现在Category类中 - equals做的事情反过来调用equals,创建无限循环

答案 4 :(得分:0)

List.equalsVector.equals来电中,我认为其中包含一个包含列表X的向量Y的列表X在您的某个地方实体。在该列表上执行等号调用将遍历该列表,该列表将遍历向量,该向量将遍历列表......依此类推。

答案 5 :(得分:0)

您的代码中有一个循环结构。包括 equals、hashCode、toString 在内的类生成方法无法处理这种循环行为。 这些方法没有办法处理这种情况。

请从可能导致这些情况的方法中排除字段。