OPENJPA-2373: MapsId in a Child entity to a Parent entity using auto-assigned identity fails with duplicte INSERT SQL - back ported to 2.1.x Pinaki Poddar's trunk changes.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.1.x@1484300 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Heath Thomann 2013-05-19 15:44:19 +00:00
parent 5f824a2c5f
commit f0fe24872b
11 changed files with 530 additions and 3 deletions

View File

@ -57,6 +57,7 @@ import org.apache.openjpa.jdbc.sql.SelectExecutor;
import org.apache.openjpa.jdbc.sql.Union;
import org.apache.openjpa.kernel.LockManager;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StateManagerImpl;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
@ -268,13 +269,26 @@ public class RelationFieldStrategy
throws SQLException {
if (field.getMappedBy() != null)
return;
Row row = null;
OpenJPAStateManager rel = RelationStrategies.getStateManager
(sm.fetchObjectField(field.getIndex()), store.getContext());
if (field.getJoinDirection() == field.JOIN_INVERSE)
// Checks if the field being inserted is a MapsId field and
// the related object is using auto-assigned identity
// If the above conditions are satisfied and the related instance has
// already been inserted in the RowManger, then returns without further
// processing
if (sm instanceof StateManagerImpl) {
List<FieldMetaData> mappedByIdFields = ((StateManagerImpl)sm).getMappedByIdFields();
if (rel != null && ((ClassMapping)rel.getMetaData()).getTable().getAutoAssignedColumns().length > 0
&& mappedByIdFields!= null && mappedByIdFields.contains(field)) {
row = rm.getRow(((ClassMapping)rel.getMetaData()).getTable(), Row.ACTION_INSERT, rel, false);
if (row != null) return;
}
}
if (field.getJoinDirection() == FieldMapping.JOIN_INVERSE)
updateInverse(sm, rel, store, rm);
else {
Row row = field.getRow(sm, store, rm, Row.ACTION_INSERT);
if (row == null) row = field.getRow(sm, store, rm, Row.ACTION_INSERT);
if (row != null && !field.isBiMTo1JT()) {
field.setForeignKey(row, rel);
// this is for bi-directional maps, the key and value of the

View File

@ -459,4 +459,18 @@ public class PrimaryRow
_callbacks.length);
}
}
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("PrimaryRow[");
switch (getAction()) {
case ACTION_UPDATE: buf.append("UPDATE"); break;
case ACTION_INSERT: buf.append("INSERT"); break;
case ACTION_DELETE: buf.append("DELETE"); break;
default: buf.append("UNKNOWN");
}
buf.append(" ").append(getTable().getName()).append("]: ");
buf.append(_pk);
return buf.toString();
}
}

View File

@ -897,6 +897,7 @@ public class RowImpl
idx++;
}
}
setFlushed(true);
}
/**

View File

@ -3402,4 +3402,8 @@ public class StateManagerImpl
public void setPc(PersistenceCapable pc) {
_pc = pc;
}
public String toString() {
return "SM[" + _meta.getDescribedType().getSimpleName() + "]:" + getObjectId();
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.common.apps;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.MapsId;
import javax.persistence.Table;
import org.apache.openjpa.persistence.jdbc.ForeignKey;
import org.apache.openjpa.persistence.jdbc.ForeignKeyAction;
@Entity
@Table(name="MPTZZS")
public class Part implements Serializable {
@Column(name="NA_PT", length=20)
private String partName;
@EmbeddedId
private PartPK id = new PartPK();
@ManyToOne
@MapsId("textileId")
Shirt shirt;
public PartPK getId() {
return id;
}
public String getPartName() {
return partName;
}
public void setPartName(String aPartName) {
partName = aPartName;
}
public Shirt getShirt() {
return shirt;
}
public void setShirt(Shirt shirt) {
this.shirt = shirt;
}
}

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.common.apps;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class PartPK implements Serializable {
/*Textile Id*/
@Column(name="ID_TXE", length=4)
private Integer textileId;
/*Part Number*/
@Column(name="NU_PT", length=4)
private Integer partNumber;
public PartPK() {
}
public Integer getTextileId() {
return textileId;
}
public void setTextileId(Integer aTextileId) {
textileId = aTextileId;
}
public Integer getPartNumber() {
return partNumber;
}
public void setPartNumber(Integer aPartNumber) {
partNumber = aPartNumber;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PartPK other = (PartPK) obj;
if (partNumber == null) {
if (other.partNumber != null) {
return false;
}
} else if (!partNumber.equals(other.partNumber)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((partNumber == null) ? 0 : partNumber.hashCode());
return result;
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.common.apps;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
*
* @author Pinaki Poddar
*
*/
@Entity
@Table(name="MPTZZV")
public class Shirt extends Textile implements Serializable {
@Column(name="ID_SZE", length=1)
private String szeId;
public String getSzeId() {
return szeId;
}
public void setSzeId(String aSzeId) {
szeId = aSzeId;
}
@OneToMany(cascade=CascadeType.ALL, mappedBy="shirt", fetch=FetchType.EAGER, orphanRemoval=true)
Collection<Part> parts = new ArrayList<Part>();
public Collection<Part> getParts() {
return parts;
}
public void setParts(Collection<Part> parts) {
this.parts = parts;
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.common.apps;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import java.io.Serializable;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="MPTZZT")
public class Textile implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ID_TXE", length=4)
private Integer textileId;
@Column(name="NA_TXE", length=20)
private String txeName;
public Integer getTextileId() {
return textileId;
}
public void setTextileId(Integer aTextileId) {
textileId = aTextileId;
}
public String getTxeName() {
return txeName;
}
public void setTxeName(String aTxeName) {
txeName = aTxeName;
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.common.apps;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
@Embeddable
public class TextilePK implements Serializable {
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ID_TXE", length=4)
private Integer textileId;
public TextilePK() {
}
public Integer getTextileId() {
return textileId;
}
public void setTextileId(Integer aTextileId) {
textileId = aTextileId;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TextilePK other = (TextilePK) obj;
if (textileId == null) {
if (other.textileId != null) {
return false;
}
} else if (!textileId.equals(other.textileId)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((textileId == null) ? 0 : textileId.hashCode());
return result;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.common.apps;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.io.Serializable;
@Entity
@Table(name="MPTZZU")
public class Trousers extends Textile implements Serializable {
@Column(name="NU_LNH", length=4)
private Integer lnhNumber;
public Integer getLnhNumber() {
return lnhNumber;
}
public void setLnhNumber(Integer aLnhNumber) {
lnhNumber = aLnhNumber;
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.inheritance.jointable.onetomany;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import org.apache.openjpa.persistence.common.apps.Part;
import org.apache.openjpa.persistence.common.apps.PartPK;
import org.apache.openjpa.persistence.common.apps.Shirt;
import org.apache.openjpa.persistence.common.apps.Textile;
import org.apache.openjpa.persistence.common.apps.TextilePK;
import org.apache.openjpa.persistence.common.apps.Trousers;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/**
* Tests persisting a domain model where {@code MapsId} is used for a
* entity that uses auto-generated identity.
* <br>
* The test is created with a reported error with following domain model:
* <ol>
* <LI> The domain model used a Joined Inheritance of Textile->(Shirt, Trousers)
* <LI> Textile used auto-assigned primary key
* <LI> A Shirt has Parts.
* <LI> Part used @Maps id annotation to refer the Shirt it belongs to.
* </ol>
* and following configuration
* <ol>
* <li> the schema was defined with SQL DDL script and included foreign
* key constraints.
* <li> {@code openjpa.jdbc.MappingDefaults} was not configured
* </ol>
* <p>
* Under the above conditions, the {@code INSERT} SQL for Shirt was
* generated twice during flush: once to obtain the primary key from
* the database and (erroneously) second time while flushing a Part
* via its @MapsId relation.
*
* @see Shirt
* @see TextTile
* @see and other classes of the domain model
*
* @author Pinaki Poddar
*
*/
public class TestMapsIdWithAutoGeneratedKey extends SingleEMFTestCase {
public void setUp() {
super.setUp(DROP_TABLES,
Textile.class, TextilePK.class,
Shirt.class, Trousers.class,
Part.class, PartPK.class);
}
public void testPersistShirtWithPart() throws NamingException {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
int nPart = 3;
tx.begin();
Shirt shirt = new Shirt();
String name = "Shirt: " + System.currentTimeMillis();
shirt.setTxeName(name);
String size = "L";
shirt.setSzeId(size);
int pid = (int) System.currentTimeMillis();
for (int i = 0; i < nPart; i++) {
Part part = new Part();
part.setPartName("Part");
part.getId().setPartNumber(pid++);
part.setShirt(shirt);
shirt.getParts().add(part);
}
em.persist(shirt);
tx.commit();
em.close();
int sid = shirt.getTextileId();
em = emf.createEntityManager();
shirt = em.find(Shirt.class, sid);
assertNotNull(shirt);
assertNotNull(shirt.getParts());
assertEquals(nPart, shirt.getParts().size());
}
}