OpenJPA 级联使用 @EmbeddedId 保持一对多
我有以下实体:
Invoice
@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 张发票项目 .... ....
STEP 1,填充发票 - 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();
}
}
STEP 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
...
将发票保存到数据库的最佳方法是什么? 我哪里做错了,如何改正?
嗨,Rick,
异常堆栈如下所示:
<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
I have the following entities:
Invoice
@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
}
It's 3-tier stand alone app, and I need to do following:
test 1. for each of 10-invoice add 10-invoiceitem
....
....
STEP 1, populate 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);
}
method 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);
I have generic DAO
public void insert(E entity) {
try {
EntityManager em = getEntityManager();
em.persist(entity);
} catch (Exception e) {
rollbackTransaction();
e.printStackTrace();
}
}
STEP 2, InvoiceService - persit invoice
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";
}
Running applications are obtained by errors:
1. org.apache.openjpa.persistence.ArgumentException: Attempt to assign id "domainopenjpa.Invoiceitem-null" to new instance "domainopenjpa.Invoiceitem@4fdf11" failed;
2. org.apache.openjpa.persistence.ArgumentException: cant-set-value
...
What is the best way to persist Invoice into DB?
Where I made a mistake, and how to correct?
Hi Rick,
Exception stack looks like this:
<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)
New Exception stack:
<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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Open Jpa ArgumentException:
插入行时我遇到了与 openJpa 相同的问题。
我收到以下 ArgumentException :
问题是 @Embeddable PK 类 BalanceDetailPK。
@Columns 都是用“insertable=false, updatable=false”生成的(使用 eclipse)
删除这些属性:
刚刚解决了问题。
Open Jpa ArgumentException:
I had the same problem with openJpa when inserting rows.
I got the following ArgumentException :
The problem was the @Embeddable PK class BalanceDetailPK.
The @Columns were all generated (using eclipse) with "insertable=false, updatable=false"
Removing these attributes :
just solved the problem.
问题出在您的
public String save(Invoice Invoice)
方法中。当您调用inDAO.insert(invoice);
时,会保留您的发票,并且它会级联到所有 InvoiceItems。 在将主键移交给 OpenJPA 后,您不应对其进行修改。在您的保存方法中,您需要在调用
inDAO.insert(invoice); 之前设置 InvoiceItemPK;代码>.另外,不要为每个 invoceItem 调用
inDAO.insert(...)
,因为由于您在关系@OneToMany(cascade = CascadeType.全部,mappedBy =“发票”)
The problem is in your
public String save(Invoice invoice)
method. When you callinDAO.insert(invoice);
that is persisting your Invoice and it is cascading to all InvoiceItems. You shouldn't be modifying a primary key after you hand it over to OpenJPA.In your save method, you need to set the InvoiceItemPK prior to calling
inDAO.insert(invoice);
. Also, don't callinDAO.insert(...)
for each invoceItem as they are already persisted due to the Cascade property that you set on the relationship@OneToMany(cascade = CascadeType.ALL, mappedBy = "invoice")