mirror of https://github.com/apache/openjpa.git
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:
parent
5f824a2c5f
commit
f0fe24872b
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -897,6 +897,7 @@ public class RowImpl
|
|||
idx++;
|
||||
}
|
||||
}
|
||||
setFlushed(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3402,4 +3402,8 @@ public class StateManagerImpl
|
|||
public void setPc(PersistenceCapable pc) {
|
||||
_pc = pc;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "SM[" + _meta.getDescribedType().getSimpleName() + "]:" + getObjectId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue