OpenJPA级联与@EmbeddedId保持一对多

时间:2011-06-03 00:19:02

标签: java jpa one-to-many openjpa

我有以下实体:

发票

@Entity
@Table(name = "invoice")
public class Invoice implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "InvoiceID")
    private Long invoiceID;

    @Basic(optional = false)
    @Column(name = "Date")
    @Temporal(TemporalType.DATE)
    private Date date;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "invoice")
    private List<Invoiceitem> invoiceitemCollection;

    @JoinColumn(name = "CustomerID", referencedColumnName = "CustomerID")
    @ManyToOne(fetch = FetchType.LAZY,optional = false)
    private Customer customerID;

    // getter/setter
}

Invoiceitem

@Entity
@Table(name = "invoiceitem")
public class Invoiceitem implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    protected InvoiceitemPK invoiceitemPK;

    @Basic(optional = false)
    @Column(name = "Quantity")
    private int quantity;

    @JoinColumn(name = "InvoiceID", referencedColumnName = "InvoiceID", insertable = false,         updatable = false)
    @ManyToOne(optional = false)
    private Invoice invoice;

    @JoinColumn(name = "ProductID", referencedColumnName = "ProductID")
    @ManyToOne(optional = false)
    private Product productID;

    // getter/setter
}

InvoiceItemPK

@Embeddable
public class InvoiceitemPK implements Serializable {

    @Column(name = "ItemID" ,nullable=false)
    private long itemID;

    @Column(name = "InvoiceID", nullable=false)
    private long invoiceID;

    // getter/setter
}

这是3层独立应用,我需要做以下事情: 测试1.为每张10张发票添加10张invoiceitem .... ....

第1步,填写Invoice - Invoiceitem:

for(int i = 0; i < invoiceNumber; i++ ){
 // set invoice where invoiceNumber = 10, 100
        Invoice invoice = new Invoice();
        String formatIn = "dd-MM-yyyy";
        SimpleDateFormat sdfi = new SimpleDateFormat(formatIn);
        java.util.Date inDate = sdfi.parse("29-04-2011");
        java.sql.Date sqlDate = new java.sql.Date(inDate.getTime());

        invoice.setCustomerID(newCust);
        invoice.setDate(sqlDate);
        // set Invoiceitem
        setInvoiceItem(prod, invItemNumber, invoice);

        saveInvoice(invoice);

}

方法 setInvoiceItem

 List<Invoiceitem> invoiceItemList = new ArrayList<Invoiceitem>();
for(int i = 0; i  < invItemNumber ; i++){
// set invoiceitem where invItemNumber = 10, 100
        Invoiceitem invoiceItem = new Invoiceitem();
        int quantity = new RandomGeneratorForInteger().generateRandomNumber(10);
        int genProductID = new   RandomGeneratorForInteger().generateRandomNumber(ProductCollectionOpenJPA.getProdIDLi().size());
        Long pid = (Long) ProductCollectionOpenJPA.getProdIDLi().get(genProductID);
        // set relation
        invoiceItem.setInvoice(invoice);
        invoiceItem.setProductID(prod = new Product(pid));
        invoiceItem.setQuantity(quantity);
        invoiceItemList.add(invoiceItem);
}
invoice.setInvoiceitemCollection(invoiceItemList); 

我有通用的DAO

 public void insert(E entity) {
  try {
        EntityManager em = getEntityManager();
        em.persist(entity);
      } catch (Exception e) {
       rollbackTransaction();
        e.printStackTrace();
        }
      }

第2步 InvoiceService - 持久发票

public String save(Invoice invoice) {
InvoiceDAO.log("saving Invoice---> InvoiceItem instance", Level.INFO, null);
    try {
        inDAO.beginTransaction();
    inDAO.insert(invoice);

        for (int i = 0; i < invoice.getInvoiceitemCollection().size(); i++) {
            InvoiceitemPK inItmPK = new InvoiceitemPK();

            inItmPK.setItemID(++i);
            inItmPK.setInvoiceID(invoice.getInvoiceID());

            invoice.getInvoiceitemCollection().get(i).setInvoiceitemPK(inItmPK);

       for (int i = 0; i < invoice.getInvoiceitemCollection().size(); i++) {
            inDAO.insert(invoice);

            }
          inDAO.commitTransaction();
    } catch (Exception e) {
        InvoiceDAO.log("save failed", Level.SEVERE, e);
        return "Invoice  are't  saved";
    }
    inDAO.closeEntityManager();
    InvoiceDAO.log("save successful", Level.INFO, null);
    return "Invoice successfuly saved";
}

运行应用程序是通过错误获得的: 1. org.apache.openjpa.persistence.ArgumentException:尝试将id“domainopenjpa.Invoiceitem-null”分配给新实例“domainopenjpa.Invoiceitem@4fdf11”失败; 2. org.apache.openjpa.persistence.ArgumentException:cant-set-value

...

将Invoice保存到数据库的最佳方法是什么? 我犯了哪个错误,以及如何纠正?

嗨瑞克,

异常堆栈如下所示:

<openjpa-2.0.1-r422266:989424 fatal general error> org.apache.openjpa.persistence.PersistenceException: This operation failed for some instances.  See the nested exceptions array for details.
    at org.apache.openjpa.kernel.BrokerImpl.throwNestedExceptions(BrokerImpl.java:2493)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2179)
    at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2037)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1808)
    at org.apache.openjpa.kernel.StateManagerImpl.assignObjectId(StateManagerImpl.java:609)
    at org.apache.openjpa.kernel.StateManagerImpl.assignField(StateManagerImpl.java:696)
    at org.apache.openjpa.kernel.StateManagerImpl.beforeAccessField(StateManagerImpl.java:1608)
    at org.apache.openjpa.kernel.StateManagerImpl.accessingField(StateManagerImpl.java:1591)
    at domainopenjpa.Invoice.pcGetinvoiceID(Invoice.java)
...
Caused by: <openjpa-2.0.1-r422266:989424 fatal general error> org.apache.openjpa.persistence.PersistenceException: The transaction has been rolled back.  See the nested exceptions for details on the errors that occurred.
    at org.apache.openjpa.kernel.BrokerImpl.newFlushException(BrokerImpl.java:2302)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2139)
    ... 77 more
Caused by: <openjpa-2.0.1-r422266:989424 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: cant-set-value
    at org.apache.openjpa.jdbc.meta.strats.HandlerFieldStrategy.insert(HandlerFieldStrategy.java:132)
    at org.apache.openjpa.jdbc.meta.FieldMapping.insert(FieldMapping.java:623)
    at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.insert(AbstractUpdateManager.java:230)
    at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.populateRowManager(AbstractUpdateManager.java:162)
    at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:95)
    at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:76)
    at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.flush(JDBCStoreManager.java:731)
    at org.apache.openjpa.kernel.DelegatingStoreManager.flush(DelegatingStoreManager.java:131)
    ... 78 more
NestedThrowables:
<openjpa-2.0.1-r422266:989424 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: Attempt to assign id "domainopenjpa.Invoiceitem-null" to new instance "domainopenjpa.Invoiceitem@4fdf11" failed; there is already an object in the L1 cache with this id. You must delete this object (in a previous transaction or the current one) before reusing its id.  This error can also occur when a horizontally or vertically mapped classes uses auto-increment application identity and does not use a hierarchy of application identity classes.
FailedObject: domainopenjpa.Invoiceitem@4fdf11
at org.apache.openjpa.kernel.ManagedCache.assignObjectId(ManagedCache.java:193)
    at org.apache.openjpa.kernel.BrokerImpl.assignObjectId(BrokerImpl.java:4949)
    at org.apache.openjpa.kernel.BrokerImpl.setStateManager(BrokerImpl.java:4046)
    at org.apache.openjpa.kernel.StateManagerImpl.assertObjectIdAssigned(StateManagerImpl.java:636)
    at org.apache.openjpa.kernel.StateManagerImpl.afterFlush(StateManagerImpl.java:1084)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2162)
    at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2037)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1808)
    at org.apache.openjpa.kernel.StateManagerImpl.assignObjectId(StateManagerImpl.java:609)
    at org.apache.openjpa.kernel.StateManagerImpl.assignField(StateManagerImpl.java:696)
    at org.apache.openjpa.kernel.StateManagerImpl.beforeAccessField(StateManagerImpl.java:1608)
    at org.apache.openjpa.kernel.StateManagerImpl.accessingField(StateManagerImpl.java:1591)
    at domainopenjpa.Invoice.pcGetinvoiceID(Invoice.java)
    at domainopenjpa.Invoice.getInvoiceID(Invoice.java:64)

新的异常堆栈:

<openjpa-2.0.1-r422266:989424 fatal user error> org.apache.openjpa.persistence.InvalidStateException: Encountered unmanaged object in persistent field "domainopenjpa.Invoice.invoiceitemCollection<element:class domainopenjpa.Invoiceitem>" during flush.  However, this field does not allow cascade persist. Set the cascade attribute for this field to CascadeType.PERSIST or CascadeType.ALL (JPA annotations) or "persist" or "all" (JPA orm.xml), or enable cascade-persist globally, or manually persist the related field value prior to flushing. You cannot flush unmanaged objects or graphs that have persistent associations to unmanaged objects.
FailedObject: domainopenjpa.Invoiceitem@939bdb

2 个答案:

答案 0 :(得分:1)

打开Jpa ArgumentException:

<(Yosi Lev)

插入行时,openJpa遇到了同样的问题。 我收到了以下ArgumentException:

<openjpa-1.2.4-SNAPSHOT-r422266:1481680 nonfatal user error> 
org.apache.openjpa.persistence.ArgumentException: 
Field "com.server.beans.entities.BalanceDetail.id" of "BalanceDetail 
id=BalanceDetailPK [relevanceDate=........],
can not be set to "BalanceDetailPK [relevanceDate=........]" value.
               WHICH WAS THE SAME VALUE ..

问题是@Embeddable PK类BalanceDetailPK。 @Columns都是使用“insertable = false,updatable = false”生成的(使用eclipse)

@Column(name="RELEVANCE_DATE", insertable=false, updatable=false)
private java.util.Date relevanceDate;

@Column(name="FND_ID", insertable=false, updatable=false)
private Long fndId;

删除这些属性:

@Column(name="RELEVANCE_DATE")
private java.util.Date relevanceDate;

@Column(name="FND_ID")
private Long fndId;

刚刚解决了这个问题。

答案 1 :(得分:0)

问题出在您的public String save(Invoice invoice)方法中。当您致电持有发票的inDAO.insert(invoice);时,它会级联到所有InvoiceItems。 在将主键移交给OpenJPA后,您不应该修改主键。

在save方法中,您需要在调用inDAO.insert(invoice);之前设置InvoiceItemPK。此外,不要为每个invoceItem调用inDAO.insert(...),因为它们已经保留,因为您在关系@OneToMany(cascade = CascadeType.ALL, mappedBy = "invoice")上设置了Cascade属性