diff --git a/core/src/main/java/org/hibernate/cfg/HbmBinder.java b/core/src/main/java/org/hibernate/cfg/HbmBinder.java index c6658893cf..d14d91a990 100644 --- a/core/src/main/java/org/hibernate/cfg/HbmBinder.java +++ b/core/src/main/java/org/hibernate/cfg/HbmBinder.java @@ -1615,13 +1615,9 @@ public final class HbmBinder { Attribute fkNode = node.attribute( "foreign-key" ); if ( fkNode != null ) manyToOne.setForeignKeyName( fkNode.getValue() ); - validateCascade( node, path ); - } - - private static void validateCascade(Element node, String path) { String cascade = node.attributeValue( "cascade" ); if ( cascade != null && cascade.indexOf( "delete-orphan" ) >= 0 ) { - throw new MappingException( "single-valued associations do not support orphan delete: " + path ); + throw new MappingException( "many-to-one attributes do not support orphan delete: " + path ); } } @@ -1687,8 +1683,6 @@ public final class HbmBinder { oneToOne.setPropertyName( node.attributeValue( "name" ) ); oneToOne.setReferencedEntityName( getEntityName( node, mappings ) ); - - validateCascade( node, path ); } public static void bindOneToMany(Element node, OneToMany oneToMany, Mappings mappings) diff --git a/core/src/main/java/org/hibernate/engine/Cascade.java b/core/src/main/java/org/hibernate/engine/Cascade.java index 6d5ec26efa..6c01bc9fd0 100644 --- a/core/src/main/java/org/hibernate/engine/Cascade.java +++ b/core/src/main/java/org/hibernate/engine/Cascade.java @@ -109,6 +109,10 @@ public final class Cascade { this.action = action; } + private SessionFactoryImplementor getFactory() { + return eventSource.getFactory(); + } + /** * Cascade an action from the parent entity instance to all its children. * @@ -206,6 +210,54 @@ public final class Cascade { cascadeComponent( parent, child, (AbstractComponentType) type, anything ); } } + else { + // potentially we need to handle orphan deletes for one-to-ones here... + if ( isLogicalOneToOne( type ) ) { + // We have a physical or logical one-to-one and from previous checks we know we + // have a null value. See if the attribute cascade settings and action-type require + // orphan checking + if ( style.hasOrphanDelete() && action.deleteOrphans() ) { + // value is orphaned if loaded state for this property shows not null + // because it is currently null. + final EntityEntry entry = eventSource.getPersistenceContext().getEntry( parent ); + if ( entry != null ) { + final EntityType entityType = (EntityType) type; + final Object loadedValue = entry.getLoadedValue( entityType.getPropertyName() ); + if ( loadedValue != null ) { + final String entityName = entityType.getAssociatedEntityName(); + if ( log.isTraceEnabled() ) { + log.trace( "deleting orphaned entity instance: " + entityName ); + } + eventSource.delete( entityName, loadedValue, false, new HashSet() ); + } + } + } + } + } + } + + /** + * Check if the association is a one to one in the logical model (either a shared-pk + * or unique fk). + * + * @param type The type representing the attribute metadata + * + * @return True if the attribute represents a logical one to one association + */ + private boolean isLogicalOneToOne(Type type) { + if ( ! type.isEntityType() ) { + return false; + } + final EntityType entityType = (EntityType) type; + if ( entityType.isOneToOne() ) { + // physical one-to-one + return true; + } + // todo : still need to handle the many-to-one w/ property-ref + // actually there is a question about whether the constrained side + // can declare the orphan-delete. If not, then the side declaring + // the orphan-delete can only ever be a + return false; } private boolean cascadeAssociationNow(AssociationType associationType) { diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/DeleteOneToOneOrphansTest.java b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/DeleteOneToOneOrphansTest.java new file mode 100644 index 0000000000..a038699d77 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/DeleteOneToOneOrphansTest.java @@ -0,0 +1,92 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * 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, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.orphan.one2one.fk.bidirectional; + +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.junit.functional.FunctionalTestCase; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class DeleteOneToOneOrphansTest extends FunctionalTestCase { + public DeleteOneToOneOrphansTest(String string) { + super( string ); + } + + public String[] getMappings() { + return new String[] { "orphan/one2one/fk/bidirectional/Mapping.hbm.xml" }; + } + + private void createData() { + Session session = openSession(); + session.beginTransaction(); + Employee emp = new Employee(); + emp.setInfo( new EmployeeInfo( emp ) ); + session.save( emp ); + session.getTransaction().commit(); + session.close(); + } + + private void cleanupData() { + Session session = openSession(); + session.beginTransaction(); + session.createQuery( "delete EmployeeInfo" ).executeUpdate(); + session.createQuery( "delete Employee" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + public void testOrphanedWhileManaged() { + createData(); + + Session session = openSession(); + session.beginTransaction(); + List results = session.createQuery( "from EmployeeInfo" ).list(); + assertEquals( 1, results.size() ); + results = session.createQuery( "from Employee" ).list(); + assertEquals( 1, results.size() ); + Employee emp = ( Employee ) results.get( 0 ); + assertNotNull( emp.getInfo() ); + emp.setInfo( null ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + emp = ( Employee ) session.get( Employee.class, emp.getId() ); + assertNull( emp.getInfo() ); + results = session.createQuery( "from EmployeeInfo" ).list(); + assertEquals( 0, results.size() ); + results = session.createQuery( "from Employee" ).list(); + assertEquals( 1, results.size() ); + session.getTransaction().commit(); + session.close(); + + cleanupData(); + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/Employee.java b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/Employee.java new file mode 100644 index 0000000000..16cceebcf7 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/Employee.java @@ -0,0 +1,50 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * 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, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.orphan.one2one.fk.bidirectional; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class Employee { + private Long id; + private EmployeeInfo info; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public EmployeeInfo getInfo() { + return info; + } + + public void setInfo(EmployeeInfo info) { + this.info = info; + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/EmployeeInfo.java b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/EmployeeInfo.java new file mode 100644 index 0000000000..b5e4858f3c --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/EmployeeInfo.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * 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, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.orphan.one2one.fk.bidirectional; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class EmployeeInfo { + private Long id; + private Employee employee; + + public EmployeeInfo() { + } + + public EmployeeInfo(Employee employee) { + this.employee = employee; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml new file mode 100644 index 0000000000..6f853578de --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/bidirectional/Mapping.hbm.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/DeleteOneToOneOrphansTest.java b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/DeleteOneToOneOrphansTest.java new file mode 100644 index 0000000000..8e53f1ddf7 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/DeleteOneToOneOrphansTest.java @@ -0,0 +1,92 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * 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, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.orphan.one2one.pk.bidirectional; + +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.junit.functional.FunctionalTestCase; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class DeleteOneToOneOrphansTest extends FunctionalTestCase { + public DeleteOneToOneOrphansTest(String string) { + super( string ); + } + + public String[] getMappings() { + return new String[] { "orphan/one2one/pk/bidirectional/Mapping.hbm.xml" }; + } + + private void createData() { + Session session = openSession(); + session.beginTransaction(); + Employee emp = new Employee(); + emp.setInfo( new EmployeeInfo( emp ) ); + session.save( emp ); + session.getTransaction().commit(); + session.close(); + } + + private void cleanupData() { + Session session = openSession(); + session.beginTransaction(); + session.createQuery( "delete EmployeeInfo" ).executeUpdate(); + session.createQuery( "delete Employee" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + public void testOrphanedWhileManaged() { + createData(); + + Session session = openSession(); + session.beginTransaction(); + List results = session.createQuery( "from EmployeeInfo" ).list(); + assertEquals( 1, results.size() ); + results = session.createQuery( "from Employee" ).list(); + assertEquals( 1, results.size() ); + Employee emp = ( Employee ) results.get( 0 ); + assertNotNull( emp.getInfo() ); + emp.setInfo( null ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + emp = ( Employee ) session.get( Employee.class, emp.getId() ); + assertNull( emp.getInfo() ); + results = session.createQuery( "from EmployeeInfo" ).list(); + assertEquals( 0, results.size() ); + results = session.createQuery( "from Employee" ).list(); + assertEquals( 1, results.size() ); + session.getTransaction().commit(); + session.close(); + + cleanupData(); + } +} diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/Employee.java b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/Employee.java new file mode 100644 index 0000000000..e11e55d410 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/Employee.java @@ -0,0 +1,50 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * 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, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.orphan.one2one.pk.bidirectional; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class Employee { + private Long id; + private EmployeeInfo info; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public EmployeeInfo getInfo() { + return info; + } + + public void setInfo(EmployeeInfo info) { + this.info = info; + } +} diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/EmployeeInfo.java b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/EmployeeInfo.java new file mode 100644 index 0000000000..cc3cd68fd7 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/EmployeeInfo.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * 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, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY 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 + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.orphan.one2one.pk.bidirectional; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class EmployeeInfo { + private Long id; + private Employee employee; + + public EmployeeInfo() { + } + + public EmployeeInfo(Employee employee) { + this.employee = employee; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } +} diff --git a/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml new file mode 100644 index 0000000000..93547539aa --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/orphan/one2one/pk/bidirectional/Mapping.hbm.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + employee + + + + + +