Java tutorial
/* * Mirante Tecnologia * Copyright 2010, Mirante Informatica LTDA, * and individual contributors as indicated by the @authors tag * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.miranteinfo.seam.hibernate; import java.lang.reflect.Field; import java.util.Collection; import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.collection.PersistentCollection; import org.hibernate.engine.Cascade; import org.hibernate.engine.CascadeStyle; import org.hibernate.engine.CascadingAction; import org.hibernate.engine.CollectionEntry; import org.hibernate.engine.CascadeStyle.MultipleCascadeStyle; import org.hibernate.event.EventSource; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.AbstractComponentType; import org.hibernate.type.AssociationType; import org.hibernate.type.CollectionType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; import org.hibernate.util.CollectionHelper; /** * Delegate responsible for, in conjunction with the various * {@link CascadingAction actions}, implementing cascade processing. * * @author lucas lins * @see Cascade * */ @SuppressWarnings("rawtypes") public class HibernateCascade { /** * A cascade point that occurs just after the insertion of the parent entity and * just before deletion */ public static final int AFTER_INSERT_BEFORE_DELETE = 1; /** * A cascade point that occurs just before the insertion of the parent entity and * just after deletion */ public static final int BEFORE_INSERT_AFTER_DELETE = 2; /** * A cascade point that occurs just after the insertion of the parent entity and * just before deletion, inside a collection */ public static final int AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION = 3; /** * A cascade point that occurs just after update of the parent entity */ public static final int AFTER_UPDATE = 0; /** * A cascade point that occurs just before the session is flushed */ public static final int BEFORE_FLUSH = 0; /** * A cascade point that occurs just after eviction of the parent entity from the * session cache */ public static final int AFTER_EVICT = 0; /** * A cascade point that occurs just after locking a transient parent entity into the * session cache */ public static final int BEFORE_REFRESH = 0; /** * A cascade point that occurs just after refreshing a parent entity */ public static final int AFTER_LOCK = 0; /** * A cascade point that occurs just before merging from a transient parent entity into * the object in the session cache */ public static final int BEFORE_MERGE = 0; private static final Log log = LogFactory.getLog(HibernateCascade.class); private int cascadeTo; private EventSource eventSource; private CascadingAction action; private static Field mcs_style_field; static { try { mcs_style_field = MultipleCascadeStyle.class.getDeclaredField("styles"); mcs_style_field.setAccessible(true); } catch (Exception e) { new RuntimeException(e); } } public HibernateCascade(final CascadingAction action, final int cascadeTo, final EventSource eventSource) { this.cascadeTo = cascadeTo; this.eventSource = eventSource; this.action = action; } /** * Cascade an action from the parent entity instance to all its children. * * @param persister The parent's entity persister * @param parent The parent reference. * @throws HibernateException */ public void cascade(final EntityPersister persister, final Object parent) throws HibernateException { cascade(persister, parent, null); } /** * Cascade an action from the parent entity instance to all its children. This * form is typicaly called from within cascade actions. * * @param persister The parent's entity persister * @param parent The parent reference. * @param anything Anything ;) Typically some form of cascade-local cache * which is specific to each CascadingAction type * @throws HibernateException */ public void cascade(final EntityPersister persister, final Object parent, final Object anything) throws HibernateException { if (persister.hasCascades() || action.requiresNoCascadeChecking()) { // performance opt if (log.isTraceEnabled()) { log.trace("processing cascade " + action + " for: " + persister.getEntityName()); } Type[] types = persister.getPropertyTypes(); CascadeStyle[] cascadeStyles = persister.getPropertyCascadeStyles(); EntityMode entityMode = eventSource.getEntityMode(); boolean hasUninitializedLazyProperties = persister.hasUninitializedLazyProperties(parent, entityMode); for (int i = 0; i < types.length; i++) { CascadeStyle style = cascadeStyles[i]; if (hasUninitializedLazyProperties && persister.getPropertyLaziness()[i] && !action.performOnLazyProperty()) { //do nothing to avoid a lazy property initialization continue; } if (style.doCascade(action)) { cascadeProperty(persister.getPropertyValue(parent, i, entityMode), types[i], style, anything, false); } else if (action.requiresNoCascadeChecking()) { action.noCascade(eventSource, persister.getPropertyValue(parent, i, entityMode), parent, persister, i); } } if (log.isTraceEnabled()) { log.trace("done processing cascade " + action + " for: " + persister.getEntityName()); } } } /** * Cascade an action to the child or children */ private void cascadeProperty(final Object child, final Type type, final CascadeStyle style, final Object anything, final boolean isCascadeDeleteEnabled) throws HibernateException { if (child != null) { if (type.isAssociationType()) { AssociationType associationType = (AssociationType) type; if (cascadeAssociationNow(associationType)) { cascadeAssociation(child, type, style, anything, isCascadeDeleteEnabled); } } else if (type.isComponentType()) { cascadeComponent(child, (AbstractComponentType) type, anything); } } } private boolean cascadeAssociationNow(AssociationType associationType) { return associationType.getForeignKeyDirection().cascadeNow(cascadeTo) && (eventSource.getEntityMode() != EntityMode.DOM4J || associationType.isEmbeddedInXML()); } private void cascadeComponent(final Object child, final AbstractComponentType componentType, final Object anything) { Object[] children = componentType.getPropertyValues(child, eventSource); Type[] types = componentType.getSubtypes(); for (int i = 0; i < types.length; i++) { CascadeStyle componentPropertyStyle = componentType.getCascadeStyle(i); if (componentPropertyStyle.doCascade(action)) { cascadeProperty(children[i], types[i], componentPropertyStyle, anything, false); } } } private void cascadeAssociation(final Object child, final Type type, final CascadeStyle style, final Object anything, final boolean isCascadeDeleteEnabled) { if (type.isEntityType() || type.isAnyType()) { cascadeToOne(child, type, style, anything, isCascadeDeleteEnabled); } else if (type.isCollectionType()) { cascadeCollection(child, style, anything, (CollectionType) type); } } /** * Cascade an action to a collection */ private void cascadeCollection(final Object child, final CascadeStyle style, final Object anything, final CollectionType type) { CollectionPersister persister = eventSource.getFactory().getCollectionPersister(type.getRole()); Type elemType = persister.getElementType(); final int oldCascadeTo = cascadeTo; if (cascadeTo == AFTER_INSERT_BEFORE_DELETE) { cascadeTo = AFTER_INSERT_BEFORE_DELETE_VIA_COLLECTION; } //cascade to current collection elements if (elemType.isEntityType() || elemType.isAnyType() || elemType.isComponentType()) { cascadeCollectionElements(child, type, style, elemType, anything, persister.isCascadeDeleteEnabled()); } cascadeTo = oldCascadeTo; } /** * Cascade an action to a to-one association or any type */ private void cascadeToOne(final Object child, final Type type, final CascadeStyle style, final Object anything, final boolean isCascadeDeleteEnabled) { final String entityName = type.isEntityType() ? ((EntityType) type).getAssociatedEntityName() : null; if (style.reallyDoCascade(action)) { //not really necessary, but good for consistency... action.cascade(eventSource, child, entityName, anything, isCascadeDeleteEnabled); } } /** * Cascade to the collection elements */ private void cascadeCollectionElements(final Object child, final CollectionType collectionType, final CascadeStyle style, final Type elemType, final Object anything, final boolean isCascadeDeleteEnabled) throws HibernateException { // we can't cascade to non-embedded elements boolean embeddedElements = eventSource.getEntityMode() != EntityMode.DOM4J || ((EntityType) collectionType.getElementType(eventSource.getFactory())).isEmbeddedInXML(); boolean reallyDoCascade = style.reallyDoCascade(action) && embeddedElements && child != CollectionType.UNFETCHED_COLLECTION; if (reallyDoCascade) { if (log.isTraceEnabled()) { log.trace("cascade " + action + " for collection: " + collectionType.getRole()); } Iterator iter = action.getCascadableChildrenIterator(eventSource, collectionType, child); while (iter.hasNext()) { cascadeProperty(iter.next(), elemType, style, anything, isCascadeDeleteEnabled); } if (log.isTraceEnabled()) { log.trace("done cascade " + action + " for collection: " + collectionType.getRole()); } } final boolean deleteOrphans = hasOrphanDelete(style) && action.deleteOrphans() && elemType.isEntityType() && child instanceof PersistentCollection; //a newly instantiated collection can't have orphans if (deleteOrphans) { // handle orphaned entities!! if (log.isTraceEnabled()) { log.trace("deleting orphans for collection: " + collectionType.getRole()); } // we can do the cast since orphan-delete does not apply to: // 1. newly instantiated collections // 2. arrays (we can't track orphans for detached arrays) final String entityName = collectionType.getAssociatedEntityName(eventSource.getFactory()); deleteOrphans(entityName, (PersistentCollection) child); if (log.isTraceEnabled()) { log.trace("done deleting orphans for collection: " + collectionType.getRole()); } } } private boolean hasOrphanDelete(CascadeStyle style) { if (style.hasOrphanDelete()) return true; if (style.equals(CascadeStyle.ALL)) return true; if (style instanceof MultipleCascadeStyle) { try { CascadeStyle[] styles = (CascadeStyle[]) mcs_style_field.get((MultipleCascadeStyle) style); for (CascadeStyle cs : styles) { if (cs.equals(CascadeStyle.ALL)) { return true; } } } catch (Exception e) { throw new RuntimeException(e); } } return false; } /** * Delete any entities that were removed from the collection */ private void deleteOrphans(String entityName, PersistentCollection pc) throws HibernateException { //TODO: suck this logic into the collection! final Collection orphans; if (pc.wasInitialized()) { CollectionEntry ce = eventSource.getPersistenceContext().getCollectionEntry(pc); orphans = ce == null ? CollectionHelper.EMPTY_COLLECTION : ce.getOrphans(entityName, pc); } else { orphans = pc.getQueuedOrphans(entityName); } final Iterator orphanIter = orphans.iterator(); while (orphanIter.hasNext()) { Object orphan = orphanIter.next(); if (orphan != null) { if (log.isTraceEnabled()) { log.trace("deleting orphaned entity instance: " + entityName); } eventSource.delete(entityName, orphan, false, null); } } } }