/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008-2011, 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.action.internal; import java.io.Serializable; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EventType; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEventListener; import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreInsertEventListener; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.event.service.spi.EventListenerGroup; public final class EntityIdentityInsertAction extends EntityAction { private transient Object[] state; private final boolean isDelayed; private final EntityKey delayedEntityKey; //private CacheEntry cacheEntry; private Serializable generatedId; public EntityIdentityInsertAction( Object[] state, Object instance, EntityPersister persister, SessionImplementor session, boolean isDelayed) throws HibernateException { super( session, ( isDelayed ? generateDelayedPostInsertIdentifier() : null ), instance, persister ); this.state = state; this.isDelayed = isDelayed; this.delayedEntityKey = isDelayed ? generateDelayedEntityKey() : null; } @Override public void execute() throws HibernateException { final EntityPersister persister = getPersister(); final SessionImplementor session = getSession(); final Object instance = getInstance(); boolean veto = preInsert(); // Don't need to lock the cache here, since if someone // else inserted the same pk first, the insert would fail if ( !veto ) { generatedId = persister.insert( state, instance, session ); if ( persister.hasInsertGeneratedProperties() ) { persister.processInsertGeneratedProperties( generatedId, instance, state, session ); } //need to do that here rather than in the save event listener to let //the post insert events to have a id-filled entity when IDENTITY is used (EJB3) persister.setIdentifier( instance, generatedId, session ); getSession().getPersistenceContext().registerInsertedKey( getPersister(), generatedId ); } //TODO: this bit actually has to be called after all cascades! // but since identity insert is called *synchronously*, // instead of asynchronously as other actions, it isn't /*if ( persister.hasCache() && !persister.isCacheInvalidationRequired() ) { cacheEntry = new CacheEntry(object, persister, session); persister.getCache().insert(generatedId, cacheEntry); }*/ postInsert(); if ( session.getFactory().getStatistics().isStatisticsEnabled() && !veto ) { session.getFactory().getStatisticsImplementor().insertEntity( getPersister().getEntityName() ); } } @Override public boolean needsAfterTransactionCompletion() { //TODO: simply remove this override if we fix the above todos return hasPostCommitEventListeners(); } @Override protected boolean hasPostCommitEventListeners() { return ! listenerGroup( EventType.POST_COMMIT_INSERT ).isEmpty(); } @Override public void doAfterTransactionCompletion(boolean success, SessionImplementor session) { //TODO: reenable if we also fix the above todo /*EntityPersister persister = getEntityPersister(); if ( success && persister.hasCache() && !persister.isCacheInvalidationRequired() ) { persister.getCache().afterInsert( getGeneratedId(), cacheEntry ); }*/ postCommitInsert(); } private void postInsert() { if ( isDelayed ) { getSession().getPersistenceContext().replaceDelayedEntityIdentityInsertKeys( delayedEntityKey, generatedId ); } EventListenerGroup<PostInsertEventListener> listenerGroup = listenerGroup( EventType.POST_INSERT ); if ( listenerGroup.isEmpty() ) { return; } final PostInsertEvent event = new PostInsertEvent( getInstance(), generatedId, state, getPersister(), eventSource() ); for ( PostInsertEventListener listener : listenerGroup.listeners() ) { listener.onPostInsert( event ); } } private void postCommitInsert() { EventListenerGroup<PostInsertEventListener> listenerGroup = listenerGroup( EventType.POST_COMMIT_INSERT ); if ( listenerGroup.isEmpty() ) { return; } final PostInsertEvent event = new PostInsertEvent( getInstance(), generatedId, state, getPersister(), eventSource() ); for ( PostInsertEventListener listener : listenerGroup.listeners() ) { listener.onPostInsert( event ); } } private boolean preInsert() { EventListenerGroup<PreInsertEventListener> listenerGroup = listenerGroup( EventType.PRE_INSERT ); if ( listenerGroup.isEmpty() ) { return false; // NO_VETO } boolean veto = false; final PreInsertEvent event = new PreInsertEvent( getInstance(), null, state, getPersister(), eventSource() ); for ( PreInsertEventListener listener : listenerGroup.listeners() ) { veto |= listener.onPreInsert( event ); } return veto; } public final Serializable getGeneratedId() { return generatedId; } public EntityKey getDelayedEntityKey() { return delayedEntityKey; } private synchronized static DelayedPostInsertIdentifier generateDelayedPostInsertIdentifier() { return new DelayedPostInsertIdentifier(); } private EntityKey generateDelayedEntityKey() { if ( !isDelayed ) { throw new AssertionFailure( "cannot request delayed entity-key for non-delayed post-insert-id generation" ); } return getSession().generateEntityKey( getDelayedId(), getPersister() ); } @Override public void afterDeserialize(SessionImplementor session) { super.afterDeserialize( session ); // IMPL NOTE: non-flushed changes code calls this method with session == null... // guard against NullPointerException if ( session != null ) { EntityEntry entityEntry = session.getPersistenceContext().getEntry( getInstance() ); this.state = entityEntry.getLoadedState(); } } }