HHH-4726 - Add support for delete-orphan cascading to <one-to-one/>

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18568 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-01-16 20:27:04 +00:00
parent 7fa50f7a18
commit 29152a8390
17 changed files with 726 additions and 110 deletions

View File

@ -1064,10 +1064,10 @@ public final class HbmBinder {
// COLUMN(S)
Attribute columnAttribute = node.attribute( "column" );
if ( columnAttribute == null ) {
Iterator iter = node.elementIterator();
Iterator itr = node.elementIterator();
int count = 0;
while ( iter.hasNext() ) {
Element columnElement = (Element) iter.next();
while ( itr.hasNext() ) {
Element columnElement = (Element) itr.next();
if ( columnElement.getName().equals( "column" ) ) {
Column column = new Column();
column.setValue( simpleValue );
@ -1115,6 +1115,9 @@ public final class HbmBinder {
Column column = new Column();
column.setValue( simpleValue );
bindColumn( node, column, isNullable );
if ( column.isUnique() && ManyToOne.class.isInstance( simpleValue ) ) {
( (ManyToOne) simpleValue ).markAsLogicalOneToOne();
}
final String columnName = columnAttribute.getValue();
String logicalColumnName = mappings.getNamingStrategy().logicalColumnName(
columnName, propertyPath
@ -1617,7 +1620,9 @@ public final class HbmBinder {
String cascade = node.attributeValue( "cascade" );
if ( cascade != null && cascade.indexOf( "delete-orphan" ) >= 0 ) {
throw new MappingException( "many-to-one attributes do not support orphan delete: " + path );
if ( !manyToOne.isLogicalOneToOne() ) {
throw new MappingException( "many-to-one attributes do not support orphan delete: " + path );
}
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* 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 Middleware LLC.
* 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
@ -20,13 +20,14 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.engine;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -36,6 +37,7 @@ import org.hibernate.collection.PersistentCollection;
import org.hibernate.event.EventSource;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.AbstractComponentType;
import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
@ -148,7 +150,8 @@ public final class Cascade {
EntityMode entityMode = eventSource.getEntityMode();
boolean hasUninitializedLazyProperties = persister.hasUninitializedLazyProperties( parent, entityMode );
for ( int i=0; i<types.length; i++) {
CascadeStyle style = cascadeStyles[i];
final CascadeStyle style = cascadeStyles[i];
final String propertyName = persister.getPropertyNames()[i];
if ( hasUninitializedLazyProperties && persister.getPropertyLaziness()[i] && ! action.performOnLazyProperty() ) {
//do nothing to avoid a lazy property initialization
continue;
@ -160,6 +163,7 @@ public final class Cascade {
persister.getPropertyValue( parent, i, entityMode ),
types[i],
style,
propertyName,
anything,
false
);
@ -189,6 +193,7 @@ public final class Cascade {
final Object child,
final Type type,
final CascadeStyle style,
final String propertyName,
final Object anything,
final boolean isCascadeDeleteEnabled) throws HibernateException {
@ -207,7 +212,7 @@ public final class Cascade {
}
}
else if ( type.isComponentType() ) {
cascadeComponent( parent, child, (AbstractComponentType) type, anything );
cascadeComponent( parent, child, (AbstractComponentType) type, propertyName, anything );
}
}
else {
@ -222,11 +227,34 @@ public final class Cascade {
final EntityEntry entry = eventSource.getPersistenceContext().getEntry( parent );
if ( entry != null ) {
final EntityType entityType = (EntityType) type;
final Object loadedValue = entry.getLoadedValue( entityType.getPropertyName() );
final Object loadedValue;
if ( componentPathStack.isEmpty() ) {
// association defined on entity
loadedValue = entry.getLoadedValue( propertyName );
}
else {
// association defined on component
// todo : this is currently unsupported because of the fact that
// we do not know the loaded state of this value properly
// and doing so would be very difficult given how components and
// entities are loaded (and how 'loaded state' is put into the
// EntityEntry). Solutions here are to either:
// 1) properly account for components as a 2-phase load construct
// 2) just assume the association was just now orphaned and
// issue the orphan delete. This would require a special
// set of SQL statements though since we do not know the
// orphaned value, something a delete with a subquery to
// match the owner.
// final String propertyPath = composePropertyPath( entityType.getPropertyName() );
loadedValue = null;
}
if ( loadedValue != null ) {
final String entityName = entityType.getAssociatedEntityName();
final String entityName = entry.getPersister().getEntityName();
if ( log.isTraceEnabled() ) {
log.trace( "deleting orphaned entity instance: " + entityName );
final Serializable id = entry.getPersister()
.getIdentifier( loadedValue, eventSource.getEntityMode() );
final String description = MessageHelper.infoString( entityName, id );
log.trace( "deleting orphaned entity instance: " + description );
}
eventSource.delete( entityName, loadedValue, false, new HashSet() );
}
@ -245,21 +273,26 @@ public final class Cascade {
* @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 <one-to-one/>
return false;
return type.isEntityType() && ( (EntityType) type ).isLogicalOneToOne();
}
private String composePropertyPath(String propertyName) {
if ( componentPathStack.isEmpty() ) {
return propertyName;
}
else {
StringBuffer buffer = new StringBuffer();
Iterator itr = componentPathStack.iterator();
while ( itr.hasNext() ) {
buffer.append( itr.next() ).append( '.' );
}
buffer.append( propertyName );
return buffer.toString();
}
}
private Stack componentPathStack = new Stack();
private boolean cascadeAssociationNow(AssociationType associationType) {
return associationType.getForeignKeyDirection().cascadeNow(cascadeTo) &&
( eventSource.getEntityMode()!=EntityMode.DOM4J || associationType.isEmbeddedInXML() );
@ -269,22 +302,27 @@ public final class Cascade {
final Object parent,
final Object child,
final AbstractComponentType componentType,
final String componentPropertyName,
final Object anything) {
Object[] children = componentType.getPropertyValues(child, eventSource);
componentPathStack.push( componentPropertyName );
Object[] children = componentType.getPropertyValues( child, eventSource );
Type[] types = componentType.getSubtypes();
for ( int i=0; i<types.length; i++ ) {
CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i);
final CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i);
final String subPropertyName = componentType.getPropertyNames()[i];
if ( componentPropertyStyle.doCascade(action) ) {
cascadeProperty(
parent,
children[i],
types[i],
componentPropertyStyle,
subPropertyName,
anything,
false
);
}
}
componentPathStack.pop();
}
private void cascadeAssociation(
@ -389,7 +427,8 @@ public final class Cascade {
parent,
iter.next(),
elemType,
style,
style,
null,
anything,
isCascadeDeleteEnabled
);

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* 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 Middleware LLC.
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.engine;
@ -29,7 +28,6 @@ import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
@ -84,13 +82,9 @@ public final class EntityEntry implements Serializable {
this.loadedWithLazyPropertiesUnfetched = lazyPropertiesAreUnfetched;
this.persister=persister;
this.entityMode = entityMode;
this.entityName = persister == null ?
null : persister.getEntityName();
this.entityName = persister == null ? null : persister.getEntityName();
}
/**
* Used during custom deserialization
*/
private EntityEntry(
final SessionFactoryImplementor factory,
final String entityName,
@ -104,6 +98,7 @@ public final class EntityEntry implements Serializable {
final boolean existsInDatabase,
final boolean isBeingReplicated,
final boolean loadedWithLazyPropertiesUnfetched) {
// Used during custom deserialization
this.entityName = entityName;
this.persister = ( factory == null ? null : factory.getEntityPersister( entityName ) );
this.id = id;
@ -181,10 +176,6 @@ public final class EntityEntry implements Serializable {
return cachedEntityKey;
}
void afterDeserialize(SessionFactoryImplementor factory) {
persister = factory.getEntityPersister( entityName );
}
public String getEntityName() {
return entityName;
}
@ -198,24 +189,29 @@ public final class EntityEntry implements Serializable {
}
/**
* After actually updating the database, update the snapshot information,
* and escalate the lock mode
* Handle updating the internal state of the entry after actually performing
* the database update. Specifically we update the snapshot information and
* escalate the lock mode
*
* @param entity The entity instance
* @param updatedState The state calculated after the update (becomes the
* new {@link #getLoadedState() loaded state}.
* @param nextVersion The new version.
*/
public void postUpdate(Object entity, Object[] updatedState, Object nextVersion) {
this.loadedState = updatedState;
setLockMode(LockMode.WRITE);
if ( getPersister().isVersioned() ) {
this.version = nextVersion;
getPersister().setPropertyValue(
entity,
getPersister().setPropertyValue(
entity,
getPersister().getVersionProperty(),
nextVersion,
entityMode
);
);
}
FieldInterceptionHelper.clearDirty( entity );
}
@ -249,8 +245,7 @@ public final class EntityEntry implements Serializable {
int propertyIndex = ( (UniqueKeyLoadable) persister ).getPropertyIndex(propertyName);
return loadedState[propertyIndex];
}
public boolean requiresDirtyCheck(Object entity) {
boolean isMutableInstance =
@ -268,6 +263,7 @@ public final class EntityEntry implements Serializable {
public void forceLocked(Object entity, Object nextVersion) {
version = nextVersion;
loadedState[ persister.getVersionProperty() ] = version;
//noinspection deprecation
setLockMode( LockMode.FORCE ); // TODO: use LockMode.PESSIMISTIC_FORCE_INCREMENT
persister.setPropertyValue(
entity,
@ -309,13 +305,13 @@ public final class EntityEntry implements Serializable {
return loadedWithLazyPropertiesUnfetched;
}
/**
* Custom serialization routine used during serialization of a
* Session/PersistenceContext for increased performance.
*
* @param oos The stream to which we should write the serial data.
* @throws java.io.IOException
*
* @throws IOException If a stream error occurs
*/
void serialize(ObjectOutputStream oos) throws IOException {
oos.writeObject( entityName );
@ -338,9 +334,12 @@ public final class EntityEntry implements Serializable {
*
* @param ois The stream from which to read the entry.
* @param session The session being deserialized.
*
* @return The deserialized EntityEntry
* @throws IOException
* @throws ClassNotFoundException
*
* @throws IOException If a stream error occurs
* @throws ClassNotFoundException If any of the classes declared in the stream
* cannot be found
*/
static EntityEntry deserialize(
ObjectInputStream ois,
@ -353,7 +352,7 @@ public final class EntityEntry implements Serializable {
Status.parse( ( String ) ois.readObject() ),
( Object[] ) ois.readObject(),
( Object[] ) ois.readObject(),
( Object ) ois.readObject(),
ois.readObject(),
LockMode.parse( ( String ) ois.readObject() ),
ois.readBoolean(),
ois.readBoolean(),

View File

@ -38,8 +38,8 @@ import org.hibernate.type.TypeFactory;
* @author Gavin King
*/
public class ManyToOne extends ToOne {
private boolean ignoreNotFound;
private boolean isLogicalOneToOne;
public ManyToOne(Table table) {
super(table);
@ -52,8 +52,9 @@ public class ManyToOne extends ToOne {
isLazy(),
isUnwrapProxy(),
isEmbedded(),
isIgnoreNotFound()
);
isIgnoreNotFound(),
isLogicalOneToOne
);
}
public void createForeignKey() throws MappingException {
@ -110,5 +111,11 @@ public class ManyToOne extends ToOne {
this.ignoreNotFound = ignoreNotFound;
}
public void markAsLogicalOneToOne() {
this.isLogicalOneToOne = true;
}
public boolean isLogicalOneToOne() {
return isLogicalOneToOne;
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* 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 Middleware LLC.
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.tuple.entity;
@ -29,7 +28,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.tuple.Instantiator;
@ -132,12 +130,12 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
getters = new Getter[propertySpan];
setters = new Setter[propertySpan];
Iterator iter = mappingInfo.getPropertyClosureIterator();
Iterator itr = mappingInfo.getPropertyClosureIterator();
boolean foundCustomAccessor=false;
int i=0;
while ( iter.hasNext() ) {
while ( itr.hasNext() ) {
//TODO: redesign how PropertyAccessors are acquired...
Property property = (Property) iter.next();
Property property = (Property) itr.next();
getters[i] = buildPropertyGetter(property, mappingInfo);
setters[i] = buildPropertySetter(property, mappingInfo);
if ( !property.isBasicPropertyAccessor() ) {
@ -172,7 +170,7 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
}
/**
* Retreives the defined entity-names for any subclasses defined for this
* Retrieves the defined entity-names for any subclasses defined for this
* entity.
*
* @return Any subclass entity-names.
@ -208,7 +206,7 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
catch ( ClassCastException cce ) {
StringBuffer msg = new StringBuffer( "Identifier classes must be serializable. " );
if ( id != null ) {
msg.append( id.getClass().getName() + " is not serializable. " );
msg.append( id.getClass().getName() ).append( " is not serializable. " );
}
if ( cce.getMessage() != null ) {
msg.append( cce.getMessage() );
@ -296,16 +294,21 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
}
public Object getPropertyValue(Object entity, String propertyPath) throws HibernateException {
int loc = propertyPath.indexOf('.');
String basePropertyName = loc>0 ?
propertyPath.substring(0, loc) : propertyPath;
int index = entityMetamodel.getPropertyIndex( basePropertyName );
Object baseValue = getPropertyValue( entity, index );
if ( loc>0 ) {
ComponentType type = (ComponentType) entityMetamodel.getPropertyTypes()[index];
return getComponentValue( type, baseValue, propertyPath.substring(loc+1) );
final int loc = propertyPath.indexOf('.');
final String basePropertyName = loc > 0
? propertyPath.substring( 0, loc )
: propertyPath;
final int index = entityMetamodel.getPropertyIndex( basePropertyName );
final Object baseValue = getPropertyValue( entity, index );
if ( loc > 0 ) {
if ( baseValue == null ) {
return null;
}
return getComponentValue(
(ComponentType) entityMetamodel.getPropertyTypes()[index],
baseValue,
propertyPath.substring(loc+1)
);
}
else {
return baseValue;
@ -321,25 +324,21 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
* @return The property value extracted.
*/
protected Object getComponentValue(ComponentType type, Object component, String propertyPath) {
int loc = propertyPath.indexOf('.');
String basePropertyName = loc>0 ?
propertyPath.substring(0, loc) : propertyPath;
String[] propertyNames = type.getPropertyNames();
int index=0;
for ( ; index<propertyNames.length; index++ ) {
if ( basePropertyName.equals( propertyNames[index] ) ) break;
}
if (index==propertyNames.length) {
throw new MappingException( "component property not found: " + basePropertyName );
}
Object baseValue = type.getPropertyValue( component, index, getEntityMode() );
if ( loc>0 ) {
ComponentType subtype = (ComponentType) type.getSubtypes()[index];
return getComponentValue( subtype, baseValue, propertyPath.substring(loc+1) );
final int loc = propertyPath.indexOf( '.' );
final String basePropertyName = loc > 0
? propertyPath.substring( 0, loc )
: propertyPath;
final int index = findSubPropertyIndex( type, basePropertyName );
final Object baseValue = type.getPropertyValue( component, index, getEntityMode() );
if ( loc > 0 ) {
if ( baseValue == null ) {
return null;
}
return getComponentValue(
(ComponentType) type.getSubtypes()[index],
baseValue,
propertyPath.substring(loc+1)
);
}
else {
return baseValue;
@ -347,6 +346,16 @@ public abstract class AbstractEntityTuplizer implements EntityTuplizer {
}
private int findSubPropertyIndex(ComponentType type, String subPropertyName) {
final String[] propertyNames = type.getPropertyNames();
for ( int index = 0; index<propertyNames.length; index++ ) {
if ( subPropertyName.equals( propertyNames[index] ) ) {
return index;
}
}
throw new MappingException( "component property not found: " + subPropertyName );
}
public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
boolean setAll = !entityMetamodel.hasLazyProperties();

View File

@ -508,8 +508,22 @@ public abstract class EntityType extends AbstractType implements AssociationType
return result.toString();
}
/**
* Is the association modeled here defined as a 1-1 in the database (physical model)?
*
* @return True if a 1-1 in the database; false otherwise.
*/
public abstract boolean isOneToOne();
/**
* Is the association modeled here a 1-1 according to the logical moidel?
*
* @return True if a 1-1 in the logical model; false otherwise.
*/
public boolean isLogicalOneToOne() {
return isOneToOne();
}
/**
* Convenience method to locate the identifier type of the associated entity.
*

View File

@ -45,27 +45,54 @@ import org.hibernate.persister.entity.EntityPersister;
* @author Gavin King
*/
public class ManyToOneType extends EntityType {
private final boolean ignoreNotFound;
private boolean isLogicalOneToOne;
public ManyToOneType(String className) {
this( className, false );
/**
* Creates a many-to-one association type with the given referenced entity.
*
* @param referencedEntityName The name iof the referenced entity
*/
public ManyToOneType(String referencedEntityName) {
this( referencedEntityName, false );
}
public ManyToOneType(String className, boolean lazy) {
super( className, null, !lazy, true, false );
this.ignoreNotFound = false;
/**
* Creates a many-to-one association type with the given referenced entity and the
* given laziness characteristic
*
* @param referencedEntityName The name iof the referenced entity
* @param lazy Should the association be handled lazily
*/
public ManyToOneType(String referencedEntityName, boolean lazy) {
this( referencedEntityName, null, !lazy, true, false, false );
}
/**
* @deprecated use {@link #ManyToOneType(String, String, boolean, boolean, boolean, boolean, boolean)}
* @noinspection JavaDoc
*/
public ManyToOneType(
String entityName,
String referencedEntityName,
String uniqueKeyPropertyName,
boolean lazy,
boolean unwrapProxy,
boolean isEmbeddedInXML,
boolean ignoreNotFound) {
super( entityName, uniqueKeyPropertyName, !lazy, isEmbeddedInXML, unwrapProxy );
this( referencedEntityName, uniqueKeyPropertyName, !lazy, isEmbeddedInXML, unwrapProxy, ignoreNotFound, false );
}
public ManyToOneType(
String referencedEntityName,
String uniqueKeyPropertyName,
boolean lazy,
boolean unwrapProxy,
boolean isEmbeddedInXML,
boolean ignoreNotFound,
boolean isLogicalOneToOne) {
super( referencedEntityName, uniqueKeyPropertyName, !lazy, isEmbeddedInXML, unwrapProxy );
this.ignoreNotFound = ignoreNotFound;
this.isLogicalOneToOne = isLogicalOneToOne;
}
protected boolean isNullable() {
@ -82,7 +109,11 @@ public class ManyToOneType extends EntityType {
public boolean isOneToOne() {
return false;
}
public boolean isLogicalOneToOne() {
return isLogicalOneToOne;
}
public int getColumnSpan(Mapping mapping) throws MappingException {
// our column span is the number of columns in the PK
return getIdentifierOrUniqueKeyType( mapping ).getColumnSpan( mapping );

View File

@ -201,6 +201,8 @@ public final class TypeFactory {
/**
* A many-to-one association type for the given class
*
* @deprecated Use {@link #manyToOne(String, String, boolean, boolean, boolean, boolean, boolean)}
*/
public static EntityType manyToOne(
String persistentClass,
@ -208,8 +210,8 @@ public final class TypeFactory {
boolean lazy,
boolean unwrapProxy,
boolean isEmbeddedInXML,
boolean ignoreNotFound
) {
boolean ignoreNotFound) {
//noinspection deprecation
return new ManyToOneType(
persistentClass,
uniqueKeyPropertyName,
@ -217,7 +219,29 @@ public final class TypeFactory {
unwrapProxy,
isEmbeddedInXML,
ignoreNotFound
);
);
}
/**
* A many-to-one association type for the given class
*/
public static EntityType manyToOne(
String persistentClass,
String uniqueKeyPropertyName,
boolean lazy,
boolean unwrapProxy,
boolean isEmbeddedInXML,
boolean ignoreNotFound,
boolean isLogicalOneToOne) {
return new ManyToOneType(
persistentClass,
uniqueKeyPropertyName,
lazy,
unwrapProxy,
isEmbeddedInXML,
ignoreNotFound,
isLogicalOneToOne
);
}
/**

View File

@ -629,6 +629,10 @@ public class CustomPersister implements EntityPersister {
return false;
}
public String[] getOrphanRemovalOneToOnePaths() {
return null;
}
public Object[] getNaturalIdentifierSnapshot(Serializable id, SessionImplementor session) throws HibernateException {
return null;
}

View File

@ -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.reversed.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/reversed/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();
}
}

View File

@ -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.reversed.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;
}
}

View File

@ -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.reversed.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;
}
}

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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
-->
<!DOCTYPE hibernate-mapping PUBLIC
'-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping package="org.hibernate.test.orphan.one2one.fk.reversed.bidirectional" >
<class name="Employee">
<id name="id" type="long" column="id">
<generator class="increment" />
</id>
<many-to-one name="info"
column="info_id"
unique="true"
cascade="all,delete-orphan"/>
</class>
<class name="EmployeeInfo">
<id name="id" type="long" column="id">
<generator class="increment" />
</id>
<one-to-one name="employee"
property-ref="info"
class="Employee" />
</class>
</hibernate-mapping>

View File

@ -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.reversed.unidirectional;
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/reversed/unidirectional/Mapping.hbm.xml" };
}
private void createData() {
Session session = openSession();
session.beginTransaction();
Employee emp = new Employee();
emp.setInfo( new EmployeeInfo() );
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();
}
}

View File

@ -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.reversed.unidirectional;
/**
* 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;
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.reversed.unidirectional;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class EmployeeInfo {
private Long id;
public EmployeeInfo() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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
-->
<!DOCTYPE hibernate-mapping PUBLIC
'-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping package="org.hibernate.test.orphan.one2one.fk.reversed.unidirectional" >
<class name="Employee">
<id name="id" type="long" column="id">
<generator class="increment" />
</id>
<many-to-one name="info"
column="info_id"
unique="true"
cascade="all,delete-orphan"/>
</class>
<class name="EmployeeInfo">
<id name="id" type="long" column="id">
<generator class="increment" />
</id>
</class>
</hibernate-mapping>