Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,33 @@
/**
* Specifies an {@code on delete} action for a foreign key constraint.
* The most common usage is {@code @OnDelete(action = CASCADE)}.
* <pre>
* &#064;ManyToOne
* &#064;OnDelete(action = CASCADE)
* Parent parent;
* </pre>
* Note that this results in an {@code on delete cascade} clause in
* the DDL definition of the foreign key. It's completely different
* to {@link jakarta.persistence.CascadeType#REMOVE}.
* <p>
* In fact, {@code @OnDelete} may be combined with {@code cascade=REMOVE}.
* <pre>
* &#064;ManyToOne(cascade = REMOVE)
* &#064;OnDelete(action = CASCADE)
* Parent parent;
* </pre>
* <ul>
* <li>If {@code @OnDelete(action = CASCADE)} is used in conjunction
* with {@code cascade=REMOVE}, then associated entities are fetched
* from the database, marked deleted in the persistence context,
* and evicted from the second-level cache.
* <li>If {@code @OnDelete(action = CASCADE)} is used on its own,
* <em>without</em> {@code cascade=REMOVE}, then associated
* entities are not fetched from the database, are not marked
* deleted in the persistence context, and are not automatically
* evicted from the second-level cache.
* </ul>
* <p>
* Like database triggers, {@code on delete} actions can cause state
* held in memory to lose synchronization with the database.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.hibernate.HibernateException;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadingAction;
Expand Down Expand Up @@ -137,6 +138,7 @@ public static <T> void cascade(
final boolean isUninitializedProperty =
hasUninitializedLazyProperties &&
!persister.getBytecodeEnhancementMetadata().isAttributeLoaded( parent, propertyName );
final boolean isCascadeDeleteEnabled = cascadeDeleteEnabled( action, persister, i );

if ( style.doCascade( action ) ) {
final Object child;
Expand Down Expand Up @@ -200,7 +202,7 @@ else if ( action.performOnLazyProperty() && type instanceof EntityType ) {
style,
propertyName,
anything,
false
isCascadeDeleteEnabled
);
}
else {
Expand All @@ -215,7 +217,7 @@ else if ( action.performOnLazyProperty() && type instanceof EntityType ) {
type,
style,
propertyName,
false
isCascadeDeleteEnabled
);
}
}
Expand Down Expand Up @@ -471,8 +473,8 @@ private static <T> void cascadeComponent(
componentPropertyStyle,
subPropertyName,
anything,
false
);
cascadeDeleteEnabled( action, componentType, i )
);
}
}
}
Expand Down Expand Up @@ -542,7 +544,7 @@ private static <T> void cascadeCollection(
style,
elemType,
anything,
persister.isCascadeDeleteEnabled()
cascadeDeleteEnabled( action, persister )
);
}
}
Expand Down Expand Up @@ -678,4 +680,19 @@ private static void deleteOrphans(EventSource eventSource, String entityName, Pe
}
}
}

private static <T> boolean cascadeDeleteEnabled(CascadingAction<T> action, CollectionPersister persister) {
return action.directionAffectedByCascadeDelete() == ForeignKeyDirection.FROM_PARENT
&& persister.isCascadeDeleteEnabled();
}

private static <T> boolean cascadeDeleteEnabled(CascadingAction<T> action, EntityPersister persister, int i) {
return action.directionAffectedByCascadeDelete() == ForeignKeyDirection.TO_PARENT
&& persister.getEntityMetamodel().getPropertyOnDeleteActions()[i] == OnDeleteAction.CASCADE;
}

private static <T> boolean cascadeDeleteEnabled(CascadingAction<T> action, CompositeType componentType, int i) {
return action.directionAffectedByCascadeDelete() == ForeignKeyDirection.TO_PARENT
&& componentType.getOnDeleteAction( i ) == OnDeleteAction.CASCADE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@
package org.hibernate.engine.spi;

import java.util.Iterator;
import java.util.List;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.HibernateException;
import org.hibernate.Incubating;
import org.hibernate.event.spi.EventSource;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.CollectionType;
import org.hibernate.type.ForeignKeyDirection;
import org.hibernate.type.Type;

/**
* A session action that may be cascaded from parent entity to its children
*
* @param <T> The type of some context propagated with the cascading action
*
* @author Gavin King
* @author Steve Ebersole
*/
Expand All @@ -27,10 +33,9 @@ public interface CascadingAction<T> {
*
* @param session The session within which the cascade is occurring.
* @param child The child to which cascading should be performed.
* @param entityName The child's entity name
* @param anything Anything ;) Typically some form of cascade-local cache
* which is specific to each CascadingAction type
* @param isCascadeDeleteEnabled Are cascading deletes enabled.
* @param anything Some context specific to the kind of {@link CascadingAction}
* @param isCascadeDeleteEnabled Whether the foreign key is declared with
* {@link org.hibernate.annotations.OnDeleteAction#CASCADE on delete cascade}.
*/
void cascade(
EventSource session,
Expand Down Expand Up @@ -92,4 +97,18 @@ default void noCascade(EventSource session, Object parent, EntityPersister persi
* Should this action be performed (or noCascade consulted) in the case of lazy properties.
*/
boolean performOnLazyProperty();

/**
* The cascade direction in which we care whether the foreign key is declared with
* {@link org.hibernate.annotations.OnDeleteAction#CASCADE on delete cascade}.
*
* @apiNote This allows us to reuse the long-existing boolean parameter of
* {@link #cascade(EventSource, Object, String, Object, boolean)}
* for multiple purposes.
*
*/
@Incubating @Nullable
default ForeignKeyDirection directionAffectedByCascadeDelete(){
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.engine.spi;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.HibernateException;
import org.hibernate.Internal;
import org.hibernate.LockMode;
Expand All @@ -20,6 +21,7 @@
import org.hibernate.event.spi.RefreshContext;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.type.CollectionType;
import org.hibernate.type.ForeignKeyDirection;
import org.jboss.logging.Logger;

import java.util.Iterator;
Expand Down Expand Up @@ -73,6 +75,11 @@ public boolean deleteOrphans() {
return true;
}

@Override
public ForeignKeyDirection directionAffectedByCascadeDelete() {
return ForeignKeyDirection.FROM_PARENT;
}

@Override
public String toString() {
return "ACTION_DELETE";
Expand Down Expand Up @@ -378,7 +385,7 @@ public void cascade(
Void context,
boolean isCascadeDeleteEnabled)
throws HibernateException {
if ( child != null && isChildTransient( session, child, entityName ) ) {
if ( child != null && isChildTransient( session, child, entityName, isCascadeDeleteEnabled ) ) {
throw new TransientObjectException( "persistent instance references an unsaved transient instance of '"
+ entityName + "' (save the transient instance before flushing)" );
//TODO: should be TransientPropertyValueException
Expand Down Expand Up @@ -419,13 +426,18 @@ public boolean performOnLazyProperty() {
return false;
}

@Override
public ForeignKeyDirection directionAffectedByCascadeDelete() {
return ForeignKeyDirection.TO_PARENT;
}

@Override
public String toString() {
return "ACTION_CHECK_ON_FLUSH";
}
};

private static boolean isChildTransient(EventSource session, Object child, String entityName) {
private static boolean isChildTransient(EventSource session, Object child, String entityName, boolean isCascadeDeleteEnabled) {
if ( isHibernateProxy( child ) ) {
// a proxy is always non-transient
// and ForeignKeys.isTransient()
Expand All @@ -440,7 +452,11 @@ private static boolean isChildTransient(EventSource session, Object child, Strin
// we are good, even if it's not yet
// inserted, since ordering problems
// are detected and handled elsewhere
return entry.getStatus().isDeletedOrGone();
return entry.getStatus().isDeletedOrGone()
// if the foreign key is 'on delete cascade'
// we don't have to throw because the database
// will delete the parent for us
&& !isCascadeDeleteEnabled;
}
else {
// TODO: check if it is a merged entity which has not yet been flushed
Expand Down Expand Up @@ -495,6 +511,11 @@ public abstract static class BaseCascadingAction<T> implements CascadingAction<T
public boolean performOnLazyProperty() {
return true;
}

@Override @Nullable
public ForeignKeyDirection directionAffectedByCascadeDelete() {
return null;
}
}

/**
Expand Down
29 changes: 17 additions & 12 deletions hibernate-core/src/main/java/org/hibernate/mapping/Property.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.hibernate.HibernateException;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper;
import org.hibernate.cfg.AvailableSettings;
Expand Down Expand Up @@ -86,7 +87,7 @@ public boolean isSynthetic() {
public Type getType() throws MappingException {
return value.getType();
}

public int getColumnSpan() {
return value.getColumnSpan();
}
Expand All @@ -106,11 +107,11 @@ public java.util.List<Selectable> getSelectables() {
public java.util.List<Column> getColumns() {
return value.getColumns();
}

public String getName() {
return name;
}

public boolean isComposite() {
return value instanceof Component;
}
Expand All @@ -137,6 +138,10 @@ public void resetOptional(boolean optional) {
}
}

public OnDeleteAction getOnDeleteAction() {
return value instanceof ToOne ? ( (ToOne) value ).getOnDeleteAction() : null;
}

/**
* @deprecated this method is no longer used
*/
Expand All @@ -158,7 +163,7 @@ else if ( type instanceof CollectionType ) {
return getCollectionCascadeStyle( collection.getElement().getType(), cascade );
}
else {
return getCascadeStyle( cascade );
return getCascadeStyle( cascade );
}
}

Expand Down Expand Up @@ -192,7 +197,7 @@ else if ( elementType instanceof ComponentType ) {
return getCascadeStyle( cascade );
}
}

private static CascadeStyle getCascadeStyle(String cascade) {
if ( cascade==null || cascade.equals("none") ) {
return CascadeStyles.NONE;
Expand All @@ -205,9 +210,9 @@ private static CascadeStyle getCascadeStyle(String cascade) {
styles[i++] = CascadeStyles.getCascadeStyle( tokens.nextToken() );
}
return new CascadeStyles.MultipleCascadeStyle(styles);
}
}
}

public String getCascade() {
return cascade;
}
Expand All @@ -231,7 +236,7 @@ public boolean isUpdateable() {
}

public boolean isInsertable() {
// if the property mapping consists of all formulas,
// if the property mapping consists of all formulas,
// make it non-insertable
return insertable && value.hasAnyInsertableColumns();
}
Expand Down Expand Up @@ -318,7 +323,7 @@ public boolean isValid(Mapping mapping) throws MappingException {
public String toString() {
return getClass().getSimpleName() + '(' + name + ')';
}

public void setLazy(boolean lazy) {
this.lazy=lazy;
}
Expand Down Expand Up @@ -364,11 +369,11 @@ public boolean isOptimisticLocked() {
public void setOptimisticLocked(boolean optimisticLocked) {
this.optimisticLocked = optimisticLocked;
}

public boolean isOptional() {
return optional;
}

public void setOptional(boolean optional) {
this.optional = optional;
}
Expand All @@ -384,7 +389,7 @@ public void setPersistentClass(PersistentClass persistentClass) {
public boolean isSelectable() {
return selectable;
}

public void setSelectable(boolean selectable) {
this.selectable = selectable;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package org.hibernate.tuple;

import org.hibernate.FetchMode;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.walking.spi.AttributeSource;
Expand Down Expand Up @@ -94,6 +95,11 @@ public CascadeStyle getCascadeStyle() {
return attributeInformation.getCascadeStyle();
}

@Override
public OnDeleteAction getOnDeleteAction() {
return attributeInformation.getOnDeleteAction();
}

@Override
public FetchMode getFetchMode() {
return attributeInformation.getFetchMode();
Expand Down
Loading
Loading