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