HHH-2711 : composite-map-key + unidir map can lead to PropertyAccessException

git-svn-id: https://svn.jboss.org/repos/hibernate/core/branches/Branch_3_2@12860 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2007-07-31 13:39:45 +00:00
parent c23b920ff8
commit 5f392deeae
8 changed files with 508 additions and 11 deletions

View File

@ -99,6 +99,9 @@
<exclude name="**/*.hbm.xml" /> <exclude name="**/*.hbm.xml" />
</patternset> </patternset>
<!-- Define default test suite parameters -->
<property name="hibernate.junit.forkmode" value="perBatch"/>
<property name="hibernate.junit.timeout" value="2147483647"/>
<!-- ################################################################## --> <!-- ################################################################## -->
<!-- ############################# Tasks ############################## --> <!-- ############################# Tasks ############################## -->
@ -283,7 +286,7 @@
<pathelement path="${dir.props}"/> <pathelement path="${dir.props}"/>
</path> </path>
<junit printsummary="yes" dir="${basedir}" maxmemory="256M" fork="yes" forkmode="perBatch"> <junit printsummary="yes" dir="${basedir}" maxmemory="256M" fork="yes" forkmode="${hibernate.junit.forkmode}" timeout="${hibernate.junit.timeout}">
<jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/> <jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/>
<classpath> <classpath>
<fileset refid="fs.lib" /> <fileset refid="fs.lib" />
@ -311,7 +314,7 @@
<target name="junit.instrument.cglib" depends="instrument.cglib"> <target name="junit.instrument.cglib" depends="instrument.cglib">
<mkdir dir="${dir.out.junit}"/> <mkdir dir="${dir.out.junit}"/>
<junit printsummary="yes" maxmemory="256M" fork="yes"> <junit printsummary="yes" maxmemory="256M" fork="yes" timeout="${hibernate.junit.timeout}">
<jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/> <jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/>
<classpath> <classpath>
<fileset refid="fs.lib" /> <fileset refid="fs.lib" />
@ -333,7 +336,7 @@
<target name="junit.instrument.javassist" depends="instrument.javassist"> <target name="junit.instrument.javassist" depends="instrument.javassist">
<mkdir dir="${dir.out.junit}"/> <mkdir dir="${dir.out.junit}"/>
<junit printsummary="yes" maxmemory="256M" fork="yes"> <junit printsummary="yes" maxmemory="256M" fork="yes" timeout="${hibernate.junit.timeout}">
<jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/> <jvmarg value="-Dhibernate.test.validatefailureexpected=${hibernate.test.validatefailureexpected}"/>
<classpath> <classpath>
<fileset refid="fs.lib" /> <fileset refid="fs.lib" />
@ -357,7 +360,7 @@
<target name="junitsingle" depends="cleantestdb,compiletest" description="Run a single test suite (requires testname and jar.driver properties)"> <target name="junitsingle" depends="cleantestdb,compiletest" description="Run a single test suite (requires testname and jar.driver properties)">
<delete dir="${dir.out.junit}"/> <delete dir="${dir.out.junit}"/>
<mkdir dir="${dir.out.junit}"/> <mkdir dir="${dir.out.junit}"/>
<junit printsummary="yes" fork="yes" haltonfailure="yes" dir="${basedir}"> <junit printsummary="yes" fork="yes" haltonfailure="yes" dir="${basedir}" timeout="${hibernate.junit.timeout}">
<classpath> <classpath>
<fileset refid="fs.lib" /> <fileset refid="fs.lib" />
<path refid="path.jdbc" /> <path refid="path.jdbc" />

View File

@ -1,25 +1,25 @@
//$Id: PojoComponentTuplizer.java 9619 2006-03-15 00:12:47Z steve.ebersole@jboss.com $ //$Id: PojoComponentTuplizer.java 9619 2006-03-15 00:12:47Z steve.ebersole@jboss.com $
package org.hibernate.tuple.component; package org.hibernate.tuple.component;
import java.lang.reflect.Method;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method;
import org.hibernate.HibernateException;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.tuple.component.AbstractComponentTuplizer; import org.hibernate.HibernateException;
import org.hibernate.tuple.Instantiator;
import org.hibernate.tuple.PojoInstantiator;
import org.hibernate.util.ReflectHelper;
import org.hibernate.bytecode.ReflectionOptimizer;
import org.hibernate.bytecode.BasicProxyFactory; import org.hibernate.bytecode.BasicProxyFactory;
import org.hibernate.bytecode.ReflectionOptimizer;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.property.BackrefPropertyAccessor;
import org.hibernate.property.Getter; import org.hibernate.property.Getter;
import org.hibernate.property.PropertyAccessor; import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory; import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter; import org.hibernate.property.Setter;
import org.hibernate.tuple.Instantiator;
import org.hibernate.tuple.PojoInstantiator;
import org.hibernate.util.ReflectHelper;
/** /**
* A {@link ComponentTuplizer} specific to the pojo entity mode. * A {@link ComponentTuplizer} specific to the pojo entity mode.
@ -76,6 +76,9 @@ public class PojoComponentTuplizer extends AbstractComponentTuplizer {
} }
public Object[] getPropertyValues(Object component) throws HibernateException { public Object[] getPropertyValues(Object component) throws HibernateException {
if ( component == BackrefPropertyAccessor.UNKNOWN ) {
return new Object[ propertySpan ];
}
if ( optimizer != null && optimizer.getAccessOptimizer() != null ) { if ( optimizer != null && optimizer.getAccessOptimizer() != null ) {
return optimizer.getAccessOptimizer().getPropertyValues( component ); return optimizer.getAccessOptimizer().getPropertyValues( component );
} }

View File

@ -3,6 +3,7 @@ package org.hibernate.test.collection;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
import org.hibernate.test.collection.backref.map.compkey.BackrefCompositeMapKeyTest;
import org.hibernate.test.collection.bag.PersistentBagTest; import org.hibernate.test.collection.bag.PersistentBagTest;
import org.hibernate.test.collection.idbag.PersistentIdBagTest; import org.hibernate.test.collection.idbag.PersistentIdBagTest;
import org.hibernate.test.collection.list.PersistentListTest; import org.hibernate.test.collection.list.PersistentListTest;
@ -25,6 +26,7 @@ public class CollectionSuite {
suite.addTest( PersistentMapTest.suite() ); suite.addTest( PersistentMapTest.suite() );
suite.addTest( CollectionTest.suite() ); suite.addTest( CollectionTest.suite() );
suite.addTest( PersistentSetTest.suite() ); suite.addTest( PersistentSetTest.suite() );
suite.addTest( BackrefCompositeMapKeyTest.suite() );
return suite; return suite;
} }

View File

@ -0,0 +1,284 @@
/*
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, v. 2.1. This program is distributed in the
* hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received a
* copy of the GNU Lesser General Public License, v.2.1 along with this
* distribution; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Red Hat Author(s): Steve Ebersole
*/
package org.hibernate.test.collection.backref.map.compkey;
import junit.framework.Test;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.LockMode;
import org.hibernate.Hibernate;
import org.hibernate.util.SerializationHelper;
/**
* BackrefCompositeMapKeyTest implementation. Test access to a composite map-key
* backref via a number of different access methods.
*
* @author Steve Ebersole
*/
public class BackrefCompositeMapKeyTest extends FunctionalTestCase {
public BackrefCompositeMapKeyTest(String string) {
super( string );
}
public String[] getMappings() {
return new String[] { "collection/backref/map/compkey/Mappings.hbm.xml" };
}
public static Test suite() {
return new FunctionalTestClassTestSuite( BackrefCompositeMapKeyTest.class );
}
public void testOrphanDeleteOnDelete() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey( "Bottom" ), part2 );
session.persist( prod );
session.flush();
prod.getParts().remove( mapKey );
session.delete( prod );
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
assertNull( "Orphan 'Widge' was not deleted", session.get(Part.class, "Widge") );
assertNull( "Orphan 'Get' was not deleted", session.get(Part.class, "Get") );
assertNull( "Orphan 'Widget' was not deleted", session.get(Product.class, "Widget") );
t.commit();
session.close();
}
public void testOrphanDeleteAfterPersist() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey( "Bottom" ), part2 );
session.persist( prod );
prod.getParts().remove( mapKey );
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
session.delete( session.get(Product.class, "Widget") );
t.commit();
session.close();
}
public void testOrphanDeleteAfterPersistAndFlush() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey( "Bottom" ), part2 );
session.persist( prod );
session.flush();
prod.getParts().remove( mapKey );
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
assertNull( session.get(Part.class, "Widge") );
assertNotNull( session.get(Part.class, "Get") );
session.delete( session.get(Product.class, "Widget") );
t.commit();
session.close();
}
public void testOrphanDeleteAfterLock() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey( "Bottom" ),part2 );
session.persist( prod );
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
session.lock(prod, LockMode.READ);
prod.getParts().remove(mapKey);
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
assertNull( session.get(Part.class, "Widge") );
assertNotNull( session.get(Part.class, "Get") );
session.delete( session.get(Product.class, "Widget") );
t.commit();
session.close();
}
public void testOrphanDeleteOnSaveOrUpdate() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey( "Bottom" ), part2 );
session.persist( prod );
t.commit();
session.close();
prod.getParts().remove( mapKey );
session = openSession();
t = session.beginTransaction();
session.saveOrUpdate(prod);
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
assertNull( session.get(Part.class, "Widge") );
assertNotNull( session.get(Part.class, "Get") );
session.delete( session.get(Product.class, "Widget") );
t.commit();
session.close();
}
public void testOrphanDeleteOnSaveOrUpdateAfterSerialization() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey( "Bottom" ), part2 );
session.persist( prod );
t.commit();
session.close();
prod.getParts().remove( mapKey );
prod = (Product) SerializationHelper.clone( prod );
session = openSession();
t = session.beginTransaction();
session.saveOrUpdate(prod);
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
assertNull( session.get(Part.class, "Widge") );
assertNotNull( session.get(Part.class, "Get") );
session.delete( session.get(Product.class, "Widget") );
t.commit();
session.close();
}
public void testOrphanDelete() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey( "Bottom" ), part2 );
session.persist( prod );
t.commit();
session.close();
getSessions().evict(Product.class);
getSessions().evict(Part.class);
session = openSession();
t = session.beginTransaction();
prod = (Product) session.get(Product.class, "Widget");
assertTrue( Hibernate.isInitialized( prod.getParts() ) );
part = (Part) session.get(Part.class, "Widge");
prod.getParts().remove(mapKey);
t.commit();
session.close();
getSessions().evict(Product.class);
getSessions().evict(Part.class);
session = openSession();
t = session.beginTransaction();
prod = (Product) session.get(Product.class, "Widget");
assertTrue( Hibernate.isInitialized( prod.getParts() ) );
assertNull( prod.getParts().get(new MapKey("Top")));
assertNotNull( session.get(Part.class, "Get") );
session.delete( session.get(Product.class, "Widget") );
t.commit();
session.close();
}
public void testOrphanDeleteOnMerge() {
Session session = openSession();
Transaction t = session.beginTransaction();
Product prod = new Product( "Widget" );
Part part = new Part( "Widge", "part if a Widget" );
MapKey mapKey = new MapKey( "Top" );
prod.getParts().put( mapKey, part );
Part part2 = new Part( "Get", "another part if a Widget" );
prod.getParts().put( new MapKey("Bottom"), part2 );
session.persist( prod );
t.commit();
session.close();
prod.getParts().remove( mapKey );
session = openSession();
t = session.beginTransaction();
session.merge(prod);
t.commit();
session.close();
session = openSession();
t = session.beginTransaction();
assertNull( session.get(Part.class, "Widge") );
assertNotNull( session.get(Part.class, "Get") );
session.delete( session.get(Product.class, "Widget") );
t.commit();
session.close();
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, v. 2.1. This program is distributed in the
* hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received a
* copy of the GNU Lesser General Public License, v.2.1 along with this
* distribution; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Red Hat Author(s): Steve Ebersole
*/
package org.hibernate.test.collection.backref.map.compkey;
import java.io.Serializable;
/**
* A composite map key.
*
* @author Steve Ebersole
*/
public class MapKey implements Serializable {
private String role;
public MapKey() {
}
public MapKey(String role) {
this.role = role;
}
public String getRole() {
return role;
}
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
MapKey mapKey = ( MapKey ) o;
if ( !role.equals( mapKey.role ) ) {
return false;
}
return true;
}
public int hashCode() {
return role.hashCode();
}
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0"?>
<!--
~ Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, v. 2.1. This program is distributed in the
~ hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
~ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details. You should have received a
~ copy of the GNU Lesser General Public License, v.2.1 along with this
~ distribution; if not, write to the Free Software Foundation, Inc.,
~ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
~
~ Red Hat Author(s): Steve Ebersole
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Demonstrates a unidirectional map mapping where the map key is a
composite. The unidirectional collection forces Hibernate to use
a backref. We want to make sure the backref works properly with
the component.
-->
<hibernate-mapping package="org.hibernate.test.collection.backref.map.compkey" default-access="field">
<class name="Product" table="t_product">
<id name="name"/>
<map name="parts" table="Parts" cascade="all,delete-orphan" fetch="join">
<key column="productName" not-null="true"/>
<composite-map-key class="MapKey">
<key-property name="role"/>
</composite-map-key>
<one-to-many class="Part"/>
</map>
</class>
<class name="Part" table="t_part">
<id name="name"/>
<property name="description" not-null="true"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, v. 2.1. This program is distributed in the
* hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received a
* copy of the GNU Lesser General Public License, v.2.1 along with this
* distribution; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Red Hat Author(s): Steve Ebersole
*/
package org.hibernate.test.collection.backref.map.compkey;
import java.io.Serializable;
/**
* Part implementation
*
* @author Steve Ebersole
*/
public class Part implements Serializable {
private String name;
private String description;
public Part() {
}
public Part(String name) {
this.name = name;
}
public Part(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, v. 2.1. This program is distributed in the
* hope that it will be useful, but WITHOUT A WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received a
* copy of the GNU Lesser General Public License, v.2.1 along with this
* distribution; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Red Hat Author(s): Steve Ebersole
*/
package org.hibernate.test.collection.backref.map.compkey;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* Product implementation
*
* @author Steve Ebersole
*/
public class Product implements Serializable {
private String name;
private Map parts = new HashMap();
public Product() {
}
public Product(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Map getParts() {
return parts;
}
public void setParts(Map parts) {
this.parts = parts;
}
}