Каскад OpenJPA сохраняется один-ко-многим с @EmbeddedId - PullRequest
1 голос
/ 03 июня 2011

У меня есть следующие объекты:

Счет

@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
}

Это трехуровневое автономное приложение, и мне нужно сделать следующее: Тест 1. Для каждого 10-инвойса добавьте 10-инвойс .... ....

ШАГ 1 , заполнить Счет - Счет-фактура:

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: попытка присвоить идентификатор «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 ]

1 голос
/ 04 февраля 2014

Открыть Jpa ArgumentException:

(Йоси Лев)

У меня была такая же проблема с 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 все были сгенерированы (используя eclipse) с "inserttable = false, updatable = false"

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

только что решил проблему.

0 голосов
/ 06 июня 2011

Проблема в вашем public String save(Invoice invoice) методе.Когда вы вызываете inDAO.insert(invoice);, это сохраняет ваш Счет и каскадно относится ко всем InvoiceItems. Вы не должны изменять первичный ключ после его передачи в OpenJPA.

В вашем методе сохранения вам нужно установить InvoiceItemPK перед вызовом inDAO.insert(invoice);.Кроме того, не вызывайте inDAO.insert(...) для каждого invoceItem, поскольку они уже сохранены из-за свойства Cascade, установленного для отношения @OneToMany(cascade = CascadeType.ALL, mappedBy = "invoice")

...