7

我正在使用 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、setters、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;
    }

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

    }

这是堆栈跟踪:

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

6 回答 6

14

你的班级有你正在做的班级的Category清单Productsequals方法Category

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

它调用类equals上的方法,类上的方法ProductequalsProduct

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

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

解决方案:

  1. 要么删除双向依赖。
  2. 修复equals方法。

希望能帮助到你。

于 2012-11-05T23:25:52.680 回答
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() 中包含“类别”取决于“类别”是否是产品的自然键标识符的一部分。您在 Category 中使用 List 的事实意味着您不太关心对象的相等性。如果您担心 equal(),我建议您将其更改为 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);

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

对于上述场景,您需要在“Product”的 equals() 方法中包含“Category”和“name”。

于 2012-11-06T14:51:50.143 回答
1

我怀疑循环依赖是问题的根本原因。我认为您已经映射到对象Product中的一个CategorySaleDetails两个对象。如果是这样,它将在序列化Product对象时调用循环引用问题,而将导致StackOverFlow错误。

我认为你有两个选择:

  1. bi-dreictional如果可以避免,请删除映射。
  2. 请在你的,和类中实现readObject()writeObject()方法,避免在圈子中读/写对象。ProductCategorySaleDetails

编辑:

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

希望这可以帮助。

于 2012-11-04T23:46:25.927 回答
0

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

于 2012-11-04T23:45:45.910 回答
0

List.equalsandVector.equals调用中,我认为有一个列表X包含一个向量,该向量Y包含X您实体中某处的列表。对该列表执行 equals 调用将遍历该列表,该列表将遍历该向量,该向量将遍历该列表......等等。

于 2012-11-04T23:50:28.900 回答
0

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

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

于 2021-06-01T07:58:29.517 回答