mirror of https://github.com/apache/openjpa.git
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:
parent
1eba6fd556
commit
42855b69f4
|
@ -268,7 +268,15 @@ public abstract class RelationToManyInverseKeyFieldStrategy
|
||||||
ValueMapping elem = field.getElementMapping();
|
ValueMapping elem = field.getElementMapping();
|
||||||
ColumnIO io = elem.getColumnIO();
|
ColumnIO io = elem.getColumnIO();
|
||||||
ForeignKey fk = elem.getForeignKey();
|
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();
|
assertInversable();
|
||||||
Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE);
|
Row row = rm.getAllRows(fk.getTable(), Row.ACTION_UPDATE);
|
||||||
row.setForeignKey(fk, io, null);
|
row.setForeignKey(fk, io, null);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue