OPENJPA-679 java.lang.ArrayIndexOutOfBoundsException may occur when a relation field is annotated as a primary key and a foreign key

Commit patch provided by Fay Wang

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@685042 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Catalina Wei 2008-08-12 05:10:54 +00:00
parent ffb59c35fc
commit f4845dae9b
8 changed files with 955 additions and 15 deletions

View File

@ -18,8 +18,6 @@
*/
package org.apache.openjpa.jdbc.meta;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
@ -32,12 +30,12 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.Reflection;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
import org.apache.openjpa.jdbc.kernel.JDBCStore;
import org.apache.openjpa.jdbc.meta.strats.NoneClassStrategy;
import org.apache.openjpa.jdbc.meta.strats.RelationFieldStrategy;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.ColumnIO;
import org.apache.openjpa.jdbc.schema.ForeignKey;
@ -91,6 +89,7 @@ public class ClassMapping
// maps columns to joinables
private final Map _joinables = Collections.synchronizedMap(new HashMap());
private boolean redoPrimaryKeyColumns = false;
/**
* Constructor. Supply described type and owning repository.
@ -418,17 +417,25 @@ public class ClassMapping
* class uses to link to its superclass table.
*/
public Column[] getPrimaryKeyColumns() {
if (_cols.length == 0 && getIdentityType() == ID_APPLICATION
&& isMapped()) {
FieldMapping[] pks = getPrimaryKeyFieldMappings();
Collection cols = new ArrayList(pks.length);
Column[] fieldCols;
for (int i = 0; i < pks.length; i++) {
fieldCols = pks[i].getColumns();
for (int j = 0; j < fieldCols.length; j++)
cols.add(fieldCols[j]);
if (getIdentityType() == ID_APPLICATION && isMapped()) {
if (_cols.length == 0 || redoPrimaryKeyColumns) {
FieldMapping[] pks = getPrimaryKeyFieldMappings();
Collection cols = new ArrayList(pks.length);
Column[] fieldCols;
for (int i = 0; i < pks.length; i++) {
fieldCols = pks[i].getColumns();
if (fieldCols.length == 0) {
// some pk columns depends on fk. At this moment,
// the fk may not contain complete information.
// need to redo the primary key again later on
redoPrimaryKeyColumns = true;
continue;
}
for (int j = 0; j < fieldCols.length; j++)
cols.add(fieldCols[j]);
}
_cols = (Column[]) cols.toArray(new Column[cols.size()]);
}
_cols = (Column[]) cols.toArray(new Column[cols.size()]);
}
return _cols;
}
@ -826,9 +833,27 @@ public class ClassMapping
// recursion, then resolve all fields
resolveNonRelationMappings();
FieldMapping[] fms = getFieldMappings();
for (int i = 0; i < fms.length; i++)
if (fms[i].getDefiningMetaData() == this)
for (int i = 0; i < fms.length; i++) {
if (fms[i].getDefiningMetaData() == this) {
if (fms[i].getForeignKey() != null &&
fms[i].getStrategy() instanceof RelationFieldStrategy) {
// set resolve mode to force this field mapping to be
// resolved again. The need to resolve again occurs when
// a primary key is a relation field with the foreign key
// annotation. In this situation, this primary key field
// mapping is resolved during the call to
// resolveNonRelationMapping. Since it is a relation
// field, the foreign key will be constructed. However,
// the primary key of the parent entity may not have been
// resolved yet, resulting in missing informaiton in the fk
fms[i].setResolve(MODE_META);
// set strategy to null to force fk to be re-constructed
fms[i].setStrategy(null, false);
}
fms[i].resolve(MODE_MAPPING);
}
}
fms = getDeclaredUnmanagedFieldMappings();
for (int i = 0; i < fms.length; i++)
fms[i].resolve(MODE_MAPPING);

View File

@ -0,0 +1,114 @@
/*
* 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.relations;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.openjpa.persistence.jdbc.ForeignKey;
@Entity
@IdClass(C.CId.class)
@Table(name="C4")
public class C {
@Id
private String cId;
@ManyToOne
@ForeignKey
@Id
private CM cm;
@OneToMany(mappedBy="c")
private Set<D> ds = new HashSet<D>();
public C() {
}
public String getCId() {
return cId;
}
public void setCId(String cId) {
this.cId = cId;
}
public CM getCm() {
return cm;
}
public void setCm(CM cm) {
this.cm = cm;
}
public Set<D> getDs() {
return ds;
}
public void setDs(Set<D> ds) {
this.ds = ds;
}
public static class CId{
String cId;
CM.CMId cm;
public String getCId() {
return cId;
}
public void setCId(String id) {
cId = id;
}
public CM.CMId getCm() {
return cm;
}
public void setCm(CM.CMId cm) {
this.cm = cm;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || !(obj instanceof CId))
return false;
CId id = (CId) obj;
return (this.getCId() == id.getCId() || (this.getCId() != null &&
this.getCId().equals(id.getCId())))
&& (this.getCm() == id.getCm() || (this.getCm() != null &&
this.getCm().equals(id.getCm())));
}
@Override
public int hashCode() {
return ((this.getCId() != null) ? this.getCId().hashCode():0)
^ ((this.getCm() != null)? this.getCm().hashCode():0);
}
}
}

View File

@ -0,0 +1,120 @@
/*
* 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.relations;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.openjpa.persistence.jdbc.ForeignKey;
@Entity
@IdClass(CM.CMId.class)
@Table(name="CM4")
public class CM {
@Id
private String cmId;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="cm")
private Set<C> cs = new HashSet<C>();
@ManyToOne
@ForeignKey
@Id
private E e;
public CM() {
}
public String getCmId() {
return cmId;
}
public void setCmId(String cmId) {
this.cmId = cmId;
}
public Set<C> getCs() {
return cs;
}
public void setCs(Set<C> cs) {
this.cs = cs;
}
public void addC(C c){
cs.add(c);
c.setCm(this);
}
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
public static class CMId{
private String cmId;
private String e;
public String getCmId() {
return cmId;
}
public void setCmId(String id) {
cmId = id;
}
public String getE() {
return e;
}
public void setE(String e) {
this.e = e;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null && ! (obj instanceof CMId))
return false;
CMId id = (CMId) obj;
return ( this.getCmId() == id.getCmId() ||
(this.getCmId() != null && this.getCmId().equals(id.getCmId())))
&& ( this.getE() == id.getE() || (this.getE() != null &&
this.getE().equals(id.getE())));
}
@Override
public int hashCode() {
return (this.getCmId() != null? this.getCmId().hashCode():0)
^ (this.getE()!= null ? this.getE().hashCode():0);
}
}
}

View File

@ -0,0 +1,124 @@
/*
* 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.relations;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.openjpa.persistence.jdbc.ForeignKey;
@Entity
@Table(name="D4")
@IdClass(D.CId.class)
public class D {
@ManyToOne
@ForeignKey
private C c;
@Id
private String id;
private String a;
@ManyToOne
@ForeignKey
@Id
private VC vc;
public D() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public C getC() {
return c;
}
public void setC(C c) {
this.c = c;
}
public VC getVc() {
return vc;
}
public void setVc(VC vc) {
this.vc = vc;
}
public static class CId{
private String id;
private VC.VCId vc;
public VC.VCId getVc() {
return vc;
}
public void setVc(VC.VCId vc) {
this.vc = vc;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || ! (obj instanceof CId))
return false;
CId id = (CId) obj;
return (this.getId() == id.getId() || (this.getId() != null &&
this.getId().equals(id.getId())))
&& (this.getVc() == id.getVc() || (this.getVc() != null &&
this.getVc().equals(id.getVc())));
}
@Override
public int hashCode() {
return (this.getId() != null ? this.getId().hashCode():0)
^ (this.getVc() != null ? this.getVc().hashCode():0);
}
}
}

View File

@ -0,0 +1,79 @@
/*
* 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.relations;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="E4")
public class E {
@Id
private String eId;
private String name;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "e")
private Set<VCS> vcss = new HashSet<VCS>();
@OneToMany(cascade = CascadeType.ALL, mappedBy = "e")
private Set<CM> cms = new HashSet<CM>();
public E() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<CM> getCms() {
return cms;
}
public void setCms(Set<CM> cms) {
this.cms = cms;
}
public String getEId() {
return eId;
}
public void setEId(String id) {
eId = id;
}
public Set<VCS> getVcss() {
return vcss;
}
public void setVcss(Set<VCS> vcss) {
this.vcss = vcss;
}
}

View File

@ -0,0 +1,208 @@
/*
* 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.relations;
import java.util.List;
import javax.persistence.EntityManager;
import junit.framework.Assert;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestRelationFieldAsPrimaryKeyAndForeignKey
extends SingleEMFTestCase {
public void setUp() {
setUp(C.class, CM.class, D.class, E.class, VC.class, VCS.class);
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
List<E> es = (List<E>) em.createQuery(
"Select e from E e").getResultList();
for (E e : es)
em.remove(e);
em.getTransaction().commit();
em.close();
} catch (Exception e) {
}
em = emf.createEntityManager();
em.getTransaction().begin();
E e = new E();
e.setEId("E1");
e.setName("E1");
VC vc = new VC();
vc.setVcId("VC1");
VCS vcset = new VCS();
vcset.setVcsId("VCS1");
vcset.setName("VCSET1");
vcset.addVC(vc);
vcset.setE(e);
C c = new C();
c.setCId("C1");
CM cm = new CM();
cm.setCmId("CM1");
cm.setE(e);
cm.addC(c);
D d = new D();
d.setA("addr");
d.setVc(vc);
d.setId("IM1");
em.persist(e);
em.persist(vc);
em.persist(vcset);
em.persist(c);
em.persist(cm);
em.persist(d);
em.getTransaction().commit();
em.close();
}
public void testUnboundEntities() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
VCS vcSet = new VCS();
vcSet.setVcsId("VCSET2");
vcSet.setName("VCSET2");
try {
em.persist(vcSet);
em.getTransaction().commit();
Assert.fail("didn't throw expected PersistenceException");
} catch (Exception e) {
// test pass
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
em.getTransaction().begin();
VC vc = new VC();
vc.setVcId("VC2");
try {
em.persist(vc);
em.getTransaction().commit();
Assert.fail("didn't throw expected PersistenceException");
} catch (Exception e) {
// test pass
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
em.getTransaction().begin();
CM cm = new CM();
cm.setCmId("CMID2");
try {
em.persist(cm);
em.getTransaction().commit();
Assert.fail("didn't throw expected PersistenceException");
} catch (Exception e) {
// test pass
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
em.getTransaction().begin();
C c = new C();
c.setCId("CID2");
try {
em.persist(c);
em.getTransaction().commit();
Assert.fail("didn't throw expected PersistenceException");
} catch (Exception e) {
// test pass
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
em.close();
}
public void testQuery() {
EntityManager em = emf.createEntityManager();
List<E> es = (List<E>) em.createQuery(
"Select e from E e where e.name='E1'").getResultList();
Assert.assertEquals(1, es.size());
E e = (E) es.get(0);
Assert.assertEquals("E1", e.getName());
Assert.assertEquals(1, e.getVcss().size());
Assert.assertEquals(1, e.getCms().size());
Assert.assertEquals(1, e.getVcss().size());
// Get virtual container set and check that it has a reference to the
// ensemble
List<VCS> vcss = (List<VCS>) em.createQuery(
"Select vcset from VCS vcset where vcset.vcsId='VCS1'")
.getResultList();
Assert.assertEquals(1, vcss.size());
Assert.assertEquals(e, ((VCS) vcss.get(0)).getE());
em.close();
}
public void testDeletes() {
// Remove VC set and check that all VCs belonging to that set are
// deleted but not the ensemble itself
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
VCS vcset = (VCS) em.createQuery(
"Select vcset from VCS vcset where vcset.vcsId='VCS1'")
.getSingleResult();
em.remove(vcset);
em.getTransaction().commit();
// Get virtualContainer
List<VC> vcs = (List<VC>) em.createQuery(
"Select vc from VC vc where vc.vcId='VC1'")
.getResultList();
Assert.assertEquals(0, vcs.size());
// Make sure E and I are still there
List<E> es = (List<E>) em.createQuery(
"Select e from E e").getResultList();
Assert.assertEquals(1, es.size());
}
public void tearDown() throws Exception {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
List<E> es = (List<E>) em.createQuery(
"Select e from E e").getResultList();
for (E e : es) {
em.remove(e);
}
em.getTransaction().commit();
em.close();
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.relations;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.openjpa.persistence.relations.VCS.VCSId;
import org.apache.openjpa.persistence.jdbc.ForeignKey;
@Entity
@Table(name="VC4")
@IdClass(VC.VCId.class)
public class VC {
@Id
private String vcId;
// @ManyToOne
// @ForeignKey
// private I i;
@ManyToOne
@ForeignKey
@Id
private VCS vcs;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "vc")
private Set<D> ds = new HashSet<D>();
public VC() {
}
public String getVcId() {
return vcId;
}
public void setVcId(String vcId) {
this.vcId = vcId;
}
public Set<D> getDs() {
return ds;
}
public void setDs(Set<D> ds) {
this.ds = ds;
}
public VCS getVcs() {
return vcs;
}
public void setVcs(VCS vcs) {
this.vcs = vcs;
}
public static class VCId {
private String vcId;
private VCSId vcs;
public String getVcId() {
return vcId;
}
public void setVcId(String vcId) {
this.vcId = vcId;
}
public VCSId getVcs() {
return vcs;
}
public void setVcs(VCSId vcs) {
this.vcs = vcs;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || !(obj instanceof VCId))
return false;
VCId id = (VCId) obj;
return (this.getVcId() == id.getVcId() ||
(this.getVcId() == null &&
this.getVcId().equals(id.getVcId()))) &&
(this.getVcs() == id.getVcs() ||
(this.getVcs() == null && this
.getVcs().equals(id.getVcs())));
}
@Override
public int hashCode() {
return (this.getVcId() != null ? this.getVcId().hashCode() : 0)
^ (this.getVcs() != null ? this.getVcs().hashCode() : 0);
}
}
}

View File

@ -0,0 +1,143 @@
/*
* 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.relations;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.openjpa.persistence.jdbc.ForeignKey;
@Entity
@Table(name="VCS4")
@IdClass(VCS.VCSId.class)
public class VCS {
@Id
private String vcsId;
@ManyToOne
@ForeignKey
@Id
private E e;
@Basic
private String name;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy = "vcs")
private Set<VC> vcs = new HashSet<VC>();
public VCS() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVcsId() {
return vcsId;
}
public void setVcsId(String vcsId) {
this.vcsId = vcsId;
}
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
public Set<VC> getVcs() {
return vcs;
}
public void setVcs(Set<VC> vcs) {
this.vcs = vcs;
}
public void addVC(VC vc){
vcs.add(vc);
vc.setVcs(this);
}
public static class VCSId{
private String vcsId;
private String e;
public String getE() {
return e;
}
public void setE(String e) {
this.e = e;
}
public String getVcsId() {
return vcsId;
}
public void setVcsId(String vcsId) {
this.vcsId = vcsId;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || ! (obj instanceof VCSId))
return false;
VCSId id = (VCSId) obj;
return (this.getVcsId() == id.getVcsId() ||
(this.getVcsId() != null &&
this.getVcsId().equals(id.getVcsId())))
&& (this.getE() == id.getE() || (this.getE() != null
&& this.getE().equals(id.getE())));
}
@Override
public int hashCode() {
return (this.getVcsId() != null ?this.getVcsId().hashCode():0)
^ (this.getE() != null ? this.getE().hashCode():0);
}
}
}