Author: hthomann
Date: Sat Oct 17 18:29:11 2015
New Revision: 1709201
URL: http://svn.apache.org/viewvc?rev=1709201&view=rev
Log:
OPENJPA-2603: Merging an unmanaged entity multiple (3) times leads to an
exception.
Added:
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java
Modified:
openjpa/branches/2.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java
Modified:
openjpa/branches/2.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java?rev=1709201&r1=1709200&r2=1709201&view=diff
==============================================================================
---
openjpa/branches/2.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java
(original)
+++
openjpa/branches/2.1.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/RelationToManyInverseKeyFieldStrategy.java
Sat Oct 17 18:29:11 2015
@@ -268,7 +268,15 @@ public abstract class RelationToManyInve
ValueMapping elem = field.getElementMapping();
ColumnIO io = elem.getColumnIO();
ForeignKey fk = elem.getForeignKey();
- if (!elem.getUseClassCriteria() && io.isAnyUpdatable(fk, true)) {
+
+ //OJ-2603: Don't null an FK which is also a PK in the referencing
object.
+ boolean containsPK = false;
+ Column[] cols = fk.getColumns();
+ for (int i = 0; i < cols.length && !containsPK; i++){
+ containsPK= cols[i].isPrimaryKey();
+ }
+
+ if (!elem.getUseClassCriteria() && io.isAnyUpdatable(fk, true) &&
!containsPK) {
assertInversable();
Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE);
row.setForeignKey(fk, io, null);
Added:
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java?rev=1709201&view=auto
==============================================================================
---
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java
(added)
+++
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java
Sat Oct 17 18:29:11 2015
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.merge;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.Table;
+
+@Entity
+@Table( name = "ITEM_TABLE" )
+// Remove this @IdClass, and one of the @Id (i.e. use a single PK, not a
compound PK
+// and the test will work fine!!!!!
+@IdClass( LineItemPK.class )
+public class LineItem {
+ @Id
+ @Column( name = "ORDER_ID", nullable = false )
+ private Long orderId;
+
+ @Id
+ @Column( name = "ITEM_ID", nullable = false )
+ private Long itemId;
+
+ @Column( name = "PRODUCT_NAME", nullable = false )
+ private String productName;
+
+ @Column( name = "QUANTITY", nullable = false )
+ private int quantity;
+
+ @Column( name = "PRICE", nullable = false )
+ private float price;
+
+ public LineItem() {
+ }
+
+ public LineItem( String productName, int quantity, float price ) {
+ this();
+ this.productName = productName;
+ this.quantity = quantity;
+ this.price = price;
+ }
+
+ public String getProductName() {
+ return productName;
+ }
+
+ public void setProductName(String productName) {
+ this.productName = productName;
+ }
+
+ public int getQuantity() {
+ return quantity;
+ }
+
+ public void setQuantity(int quantity) {
+ this.quantity = quantity;
+ }
+
+ public float getPrice() {
+ return price;
+ }
+
+ public void setPrice(float price) {
+ this.price = price;
+ }
+
+ public Long getOrderId() {
+ return orderId;
+ }
+
+ public void setOrderId(Long orderId) {
+ this.orderId = orderId;
+ }
+
+ public Long getItemId() {
+ return itemId;
+ }
+
+ public void setItemId(Long itemId) {
+ this.itemId = itemId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((itemId == null) ? 0 : itemId.hashCode());
+ result = prime * result + ((orderId == null) ? 0 : orderId.hashCode());
+ result = prime * result + Float.floatToIntBits(price);
+ result = prime * result + ((productName == null) ? 0 :
productName.hashCode());
+ result = prime * result + quantity;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ LineItem other = (LineItem) obj;
+ if (itemId == null) {
+ if (other.itemId != null)
+ return false;
+ } else if (!itemId.equals(other.itemId))
+ return false;
+ if (orderId == null) {
+ if (other.orderId != null)
+ return false;
+ } else if (!orderId.equals(other.orderId))
+ return false;
+ if (Float.floatToIntBits(price) != Float.floatToIntBits(other.price))
+ return false;
+ if (productName == null) {
+ if (other.productName != null)
+ return false;
+ } else if (!productName.equals(other.productName))
+ return false;
+ if (quantity != other.quantity)
+ return false;
+ return true;
+ }
+}
Added:
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java?rev=1709201&view=auto
==============================================================================
---
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java
(added)
+++
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java
Sat Oct 17 18:29:11 2015
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.merge;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+
+@Embeddable
+public class LineItemPK implements Serializable {
+ private static final long serialVersionUID = -8657894635702714413L;
+
+ @Column( name = "ORDER_ID", nullable = false )
+ private Long orderId;
+
+ @Column( name = "ITEM_ID", nullable = false )
+ private Long itemId;
+
+ public LineItemPK() {
+ }
+
+ public Long getOrderId() {
+ return orderId;
+ }
+
+ public void setOrderId(Long orderId) {
+ this.orderId = orderId;
+ }
+
+ public Long getItemId() {
+ return itemId;
+ }
+
+ public void setItemId(Long itemId) {
+ this.itemId = itemId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((itemId == null) ? 0 : itemId.hashCode());
+ result = prime * result + ((orderId == null) ? 0 : orderId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ LineItemPK other = (LineItemPK) obj;
+ if (itemId == null) {
+ if (other.itemId != null)
+ return false;
+ } else if (!itemId.equals(other.itemId))
+ return false;
+ if (orderId == null) {
+ if (other.orderId != null)
+ return false;
+ } else if (!orderId.equals(other.orderId))
+ return false;
+ return true;
+ }
+}
Added:
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java?rev=1709201&view=auto
==============================================================================
---
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java
(added)
+++
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java
Sat Oct 17 18:29:11 2015
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.merge;
+
+import java.sql.Date;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+@Entity
+@Table( name = "ORDER_TABLE" )
+public class Order {
+ @Id
+ @Column( name = "ID", nullable = false )
+ private Long id;
+
+ @Column( name = "ENTRY_DATE", nullable = false )
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date orderEntry;
+
+ // When using a List, things fails...using a Set all works fine.
+ @OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL )
+ @JoinColumn( name = "ORDER_ID", referencedColumnName = "ID" )
+ private List<LineItem> items;
+
+ public Order() {
+ orderEntry = new Date( System.currentTimeMillis() );
+ items = new ArrayList<LineItem>();
+ }
+
+ public Order( long id ) {
+ this();
+ this.id = id;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getOrderEntry() {
+ return orderEntry;
+ }
+
+ public void setOrderEntry(Date orderEntry) {
+ this.orderEntry = orderEntry;
+ }
+
+ public void addItem( LineItem item ) {
+ items.add(item);
+ item.setOrderId(id);
+ item.setItemId((long)items.size() );
+ }
+
+ public List<LineItem> getItems() {
+ return items;
+ }
+
+ public void setItems(List<LineItem> items) {
+ this.items = items;
+ }
+}
Added:
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java
URL:
http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java?rev=1709201&view=auto
==============================================================================
---
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java
(added)
+++
openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java
Sat Oct 17 18:29:11 2015
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.merge;
+
+import java.sql.Date;
+
+import javax.persistence.EntityManager;
+
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/*
+ * See OPENJPA-2603 for details.
+ */
+public class TestMultipleMerge extends SingleEMFTestCase {
+
+ public void setUp() {
+ //Since a JPA 1.0 p.xml file is used the property
"NonDefaultMappingAllowed=true" is
+ //needed for this test. This is needed since Order uses an
@JoinColumn; something
+ //not allowed in 1.0 (or at least a grey area in the spec) on an
@OneToMany.
+ setUp("openjpa.Compatibility", "NonDefaultMappingAllowed=true",
+ CLEAR_TABLES, Order.class, LineItemPK.class, LineItem.class);
+ }
+
+ public void testMultiMerge() {
+ EntityManager em = emf.createEntityManager();
+ em.getTransaction().begin();
+ Order order = new Order( 1l );
+
+ LineItem item = new LineItem( "my product", 44, 4.99f );
+ order.addItem(item);
+
+ //NOTE: Notice that throughout the rest of the test the unmanaged
order is merged.
+ //Throughout the rest of the test we should do a 'order =
em.merge(order)', or
+ //something to that effect (i.e. use the 'managed' order). However,
technically
+ //speaking merging the unmanaged order is not wrong, albeit odd and
potentially
+ //error prone.
+ em.merge(order);
+ em.getTransaction().commit();
+
+ em.getTransaction().begin();
+ LineItem additional = new LineItem( "My second product", 1, 999.95f );
+ order.addItem(additional);
+ order.setOrderEntry( new Date( System.currentTimeMillis() ) );
+ em.merge(order);
+ //NOTE: do a flush here and all works fine:
+ //em.flush();
+ em.merge(order);
+ //Prior to fix, an exception occurred on the commit.
+ em.getTransaction().commit();
+
+ em.clear();
+
+ //OK, good, we no longer get an exception, to be double certain
+ //all is well, lets verify that the expected LineItems are in the DB.
+ LineItemPK liPK = new LineItemPK();
+ liPK.setItemId(1l);
+ liPK.setOrderId(1l);
+ LineItem li = em.find(LineItem.class, liPK);
+
+ assertNotNull(li);
+ assertEquals(item.getProductName(), li.getProductName());
+
+ liPK.setItemId(2l);
+ liPK.setOrderId(1l);
+ li = em.find(LineItem.class, liPK);
+ assertNotNull(li);
+ assertEquals(additional.getProductName(), li.getProductName());
+
+ em.close();
+ }
+}