OPENJPA-2603: Merging an unmanaged entity multiple (3) times leads to an exception.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.1.x@1709201 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
asf-sync-process 2015-10-17 18:29:11 +00:00
parent 1eba6fd556
commit 42855b69f4
5 changed files with 414 additions and 1 deletions

View File

@ -268,7 +268,15 @@ public abstract class RelationToManyInverseKeyFieldStrategy
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);

View File

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

View File

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

View File

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

View File

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