HHH-17955 Bean Validation and @PostXxxx callbacks for StatelessSession
Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
59603dffb3
commit
9a4d21d71d
|
@ -239,19 +239,20 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
|
|||
}
|
||||
|
||||
protected boolean preInsert() {
|
||||
boolean veto = false;
|
||||
|
||||
final EventListenerGroup<PreInsertEventListener> listenerGroup
|
||||
= getFastSessionServices().eventListenerGroup_PRE_INSERT;
|
||||
if ( listenerGroup.isEmpty() ) {
|
||||
return veto;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
boolean veto = false;
|
||||
final PreInsertEvent event = new PreInsertEvent( getInstance(), getId(), getState(), getPersister(), eventSource() );
|
||||
for ( PreInsertEventListener listener : listenerGroup.listeners() ) {
|
||||
veto |= listener.onPreInsert( event );
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterTransactionCompletion(boolean success, SharedSessionContractImplementor session) throws HibernateException {
|
||||
|
|
|
@ -20,6 +20,8 @@ import org.hibernate.event.spi.PreInsertEvent;
|
|||
import org.hibernate.event.spi.PreInsertEventListener;
|
||||
import org.hibernate.event.spi.PreUpdateEvent;
|
||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
||||
import org.hibernate.event.spi.PreUpsertEvent;
|
||||
import org.hibernate.event.spi.PreUpsertEventListener;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.RepresentationMode;
|
||||
|
@ -42,7 +44,7 @@ import jakarta.validation.ValidatorFactory;
|
|||
*/
|
||||
//FIXME review exception model
|
||||
public class BeanValidationEventListener
|
||||
implements PreInsertEventListener, PreUpdateEventListener, PreDeleteEventListener {
|
||||
implements PreInsertEventListener, PreUpdateEventListener, PreDeleteEventListener, PreUpsertEventListener {
|
||||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
|
||||
CoreMessageLogger.class,
|
||||
|
@ -60,7 +62,8 @@ public class BeanValidationEventListener
|
|||
* @param factory The {@code ValidatorFactory} to use to create {@code Validator} instance(s)
|
||||
* @param settings Configured properties
|
||||
*/
|
||||
public BeanValidationEventListener(ValidatorFactory factory, Map<String,Object> settings, ClassLoaderService classLoaderService) {
|
||||
public BeanValidationEventListener(
|
||||
ValidatorFactory factory, Map<String,Object> settings, ClassLoaderService classLoaderService) {
|
||||
init( factory, settings, classLoaderService );
|
||||
}
|
||||
|
||||
|
@ -80,9 +83,8 @@ public class BeanValidationEventListener
|
|||
public boolean onPreInsert(PreInsertEvent event) {
|
||||
validate(
|
||||
event.getEntity(),
|
||||
event.getPersister().getRepresentationStrategy().getMode(),
|
||||
event.getPersister(),
|
||||
event.getSession().getFactory(),
|
||||
event.getFactory(),
|
||||
GroupsPerOperation.Operation.INSERT
|
||||
);
|
||||
return false;
|
||||
|
@ -91,9 +93,8 @@ public class BeanValidationEventListener
|
|||
public boolean onPreUpdate(PreUpdateEvent event) {
|
||||
validate(
|
||||
event.getEntity(),
|
||||
event.getPersister().getRepresentationStrategy().getMode(),
|
||||
event.getPersister(),
|
||||
event.getSession().getFactory(),
|
||||
event.getFactory(),
|
||||
GroupsPerOperation.Operation.UPDATE
|
||||
);
|
||||
return false;
|
||||
|
@ -102,21 +103,30 @@ public class BeanValidationEventListener
|
|||
public boolean onPreDelete(PreDeleteEvent event) {
|
||||
validate(
|
||||
event.getEntity(),
|
||||
event.getPersister().getRepresentationStrategy().getMode(),
|
||||
event.getPersister(),
|
||||
event.getSession().getFactory(),
|
||||
event.getFactory(),
|
||||
GroupsPerOperation.Operation.DELETE
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreUpsert(PreUpsertEvent event) {
|
||||
validate(
|
||||
event.getEntity(),
|
||||
event.getPersister(),
|
||||
event.getFactory(),
|
||||
GroupsPerOperation.Operation.UPSERT
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
private <T> void validate(
|
||||
T object,
|
||||
RepresentationMode mode,
|
||||
EntityPersister persister,
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
GroupsPerOperation.Operation operation) {
|
||||
if ( object == null || mode != RepresentationMode.POJO ) {
|
||||
if ( object == null || persister.getRepresentationStrategy().getMode() != RepresentationMode.POJO ) {
|
||||
return;
|
||||
}
|
||||
TraversableResolver tr = new HibernateTraversableResolver( persister, associationsPerEntityPersister, sessionFactory );
|
||||
|
|
|
@ -39,6 +39,7 @@ public class GroupsPerOperation {
|
|||
applyOperationGrouping( groupsPerOperation, Operation.INSERT, settings, classLoaderAccess );
|
||||
applyOperationGrouping( groupsPerOperation, Operation.UPDATE, settings, classLoaderAccess );
|
||||
applyOperationGrouping( groupsPerOperation, Operation.DELETE, settings, classLoaderAccess );
|
||||
applyOperationGrouping( groupsPerOperation, Operation.UPSERT, settings, classLoaderAccess );
|
||||
applyOperationGrouping( groupsPerOperation, Operation.DDL, settings, classLoaderAccess );
|
||||
|
||||
return groupsPerOperation;
|
||||
|
@ -99,10 +100,11 @@ public class GroupsPerOperation {
|
|||
return groupsPerOperation.get( operation );
|
||||
}
|
||||
|
||||
public static enum Operation {
|
||||
public enum Operation {
|
||||
INSERT( "persist", JPA_GROUP_PREFIX + "pre-persist", JAKARTA_JPA_GROUP_PREFIX + "pre-persist" ),
|
||||
UPDATE( "update", JPA_GROUP_PREFIX + "pre-update", JAKARTA_JPA_GROUP_PREFIX + "pre-update" ),
|
||||
DELETE( "remove", JPA_GROUP_PREFIX + "pre-remove", JAKARTA_JPA_GROUP_PREFIX + "pre-remove" ),
|
||||
UPSERT( "upsert", JPA_GROUP_PREFIX + "pre-upsert", JAKARTA_JPA_GROUP_PREFIX + "pre-upsert" ),
|
||||
DDL( "ddl", HIBERNATE_GROUP_PREFIX + "ddl", HIBERNATE_GROUP_PREFIX + "ddl" );
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.boot.beanvalidation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -23,8 +24,6 @@ import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
|||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||
import org.hibernate.boot.spi.ClassLoaderAccess;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.config.spi.StandardConverters;
|
||||
|
@ -40,6 +39,7 @@ import org.hibernate.mapping.Property;
|
|||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.SingleTableSubclass;
|
||||
|
||||
import org.hibernate.service.spi.SessionFactoryServiceRegistry;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import jakarta.validation.Validation;
|
||||
|
@ -48,6 +48,11 @@ import jakarta.validation.metadata.BeanDescriptor;
|
|||
import jakarta.validation.metadata.ConstraintDescriptor;
|
||||
import jakarta.validation.metadata.PropertyDescriptor;
|
||||
|
||||
import static org.hibernate.boot.beanvalidation.GroupsPerOperation.buildGroupsForOperation;
|
||||
import static org.hibernate.cfg.ValidationSettings.CHECK_NULLABILITY;
|
||||
import static org.hibernate.cfg.ValidationSettings.JAKARTA_VALIDATION_FACTORY;
|
||||
import static org.hibernate.cfg.ValidationSettings.JPA_VALIDATION_FACTORY;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
* @author Hardy Ferentschik
|
||||
|
@ -62,7 +67,7 @@ class TypeSafeActivator {
|
|||
*
|
||||
* @param object The supplied ValidatorFactory instance.
|
||||
*/
|
||||
@SuppressWarnings( {"UnusedDeclaration"})
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public static void validateSuppliedFactory(Object object) {
|
||||
if ( !(object instanceof ValidatorFactory) ) {
|
||||
throw new IntegrationException(
|
||||
|
@ -95,49 +100,46 @@ class TypeSafeActivator {
|
|||
applyCallbackListeners( factory, activationContext );
|
||||
}
|
||||
|
||||
@SuppressWarnings( {"UnusedDeclaration"})
|
||||
public static void applyCallbackListeners(ValidatorFactory validatorFactory, ActivationContext activationContext) {
|
||||
final Set<ValidationMode> modes = activationContext.getValidationModes();
|
||||
if ( ! ( modes.contains( ValidationMode.CALLBACK ) || modes.contains( ValidationMode.AUTO ) ) ) {
|
||||
if ( !modes.contains( ValidationMode.CALLBACK ) && !modes.contains( ValidationMode.AUTO ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ConfigurationService cfgService = activationContext.getServiceRegistry().requireService( ConfigurationService.class );
|
||||
final ClassLoaderService classLoaderService = activationContext.getServiceRegistry().requireService( ClassLoaderService.class );
|
||||
final SessionFactoryServiceRegistry serviceRegistry = activationContext.getServiceRegistry();
|
||||
final ConfigurationService cfgService = serviceRegistry.requireService( ConfigurationService.class );
|
||||
final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
|
||||
final EventListenerRegistry listenerRegistry = serviceRegistry.requireService( EventListenerRegistry.class );
|
||||
|
||||
// de-activate not-null tracking at the core level when Bean Validation is present unless the user explicitly
|
||||
// asks for it
|
||||
if ( cfgService.getSettings().get( Environment.CHECK_NULLABILITY ) == null ) {
|
||||
if ( cfgService.getSettings().get( CHECK_NULLABILITY ) == null ) {
|
||||
activationContext.getSessionFactory().getSessionFactoryOptions().setCheckNullability( false );
|
||||
}
|
||||
|
||||
final BeanValidationEventListener listener = new BeanValidationEventListener(
|
||||
validatorFactory,
|
||||
cfgService.getSettings(),
|
||||
classLoaderService
|
||||
);
|
||||
|
||||
final EventListenerRegistry listenerRegistry = activationContext.getServiceRegistry()
|
||||
.requireService( EventListenerRegistry.class );
|
||||
final BeanValidationEventListener listener =
|
||||
new BeanValidationEventListener( validatorFactory, cfgService.getSettings(), classLoaderService );
|
||||
|
||||
listenerRegistry.addDuplicationStrategy( DuplicationStrategyImpl.INSTANCE );
|
||||
|
||||
listenerRegistry.appendListeners( EventType.PRE_INSERT, listener );
|
||||
listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
|
||||
listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
|
||||
listenerRegistry.appendListeners( EventType.PRE_UPSERT, listener );
|
||||
|
||||
listener.initialize( cfgService.getSettings(), classLoaderService );
|
||||
}
|
||||
|
||||
private static void applyRelationalConstraints(ValidatorFactory factory, ActivationContext activationContext) {
|
||||
final ConfigurationService cfgService = activationContext.getServiceRegistry().requireService( ConfigurationService.class );
|
||||
final SessionFactoryServiceRegistry serviceRegistry = activationContext.getServiceRegistry();
|
||||
final ConfigurationService cfgService = serviceRegistry.requireService( ConfigurationService.class );
|
||||
if ( !cfgService.getSetting( BeanValidationIntegrator.APPLY_CONSTRAINTS, StandardConverters.BOOLEAN, true ) ) {
|
||||
LOG.debug( "Skipping application of relational constraints from legacy Hibernate Validator" );
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<ValidationMode> modes = activationContext.getValidationModes();
|
||||
if ( ! ( modes.contains( ValidationMode.DDL ) || modes.contains( ValidationMode.AUTO ) ) ) {
|
||||
if ( !modes.contains( ValidationMode.DDL ) && !modes.contains( ValidationMode.AUTO ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -145,32 +147,25 @@ class TypeSafeActivator {
|
|||
factory,
|
||||
activationContext.getMetadata().getEntityBindings(),
|
||||
cfgService.getSettings(),
|
||||
activationContext.getServiceRegistry().requireService( JdbcServices.class ).getDialect(),
|
||||
new ClassLoaderAccessImpl(
|
||||
null,
|
||||
activationContext.getServiceRegistry().getService( ClassLoaderService.class )
|
||||
)
|
||||
serviceRegistry.requireService( JdbcServices.class ).getDialect(),
|
||||
new ClassLoaderAccessImpl( null,
|
||||
serviceRegistry.getService( ClassLoaderService.class ) )
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings( {"UnusedDeclaration"})
|
||||
public static void applyRelationalConstraints(
|
||||
ValidatorFactory factory,
|
||||
Collection<PersistentClass> persistentClasses,
|
||||
Map<String,Object> settings,
|
||||
Dialect dialect,
|
||||
ClassLoaderAccess classLoaderAccess) {
|
||||
Class<?>[] groupsArray = GroupsPerOperation.buildGroupsForOperation(
|
||||
GroupsPerOperation.Operation.DDL,
|
||||
settings,
|
||||
classLoaderAccess
|
||||
);
|
||||
Set<Class<?>> groups = new HashSet<>( Arrays.asList( groupsArray ) );
|
||||
final Class<?>[] groupsArray =
|
||||
buildGroupsForOperation( GroupsPerOperation.Operation.DDL, settings, classLoaderAccess );
|
||||
final Set<Class<?>> groups = new HashSet<>( Arrays.asList( groupsArray ) );
|
||||
|
||||
for ( PersistentClass persistentClass : persistentClasses ) {
|
||||
final String className = persistentClass.getClassName();
|
||||
|
||||
if ( className == null || className.length() == 0 ) {
|
||||
if ( className == null || className.isEmpty() ) {
|
||||
continue;
|
||||
}
|
||||
Class<?> clazz;
|
||||
|
@ -202,17 +197,21 @@ class TypeSafeActivator {
|
|||
//no bean level constraints can be applied, go to the properties
|
||||
|
||||
for ( PropertyDescriptor propertyDesc : descriptor.getConstrainedProperties() ) {
|
||||
Property property = findPropertyByName( persistentClass, prefix + propertyDesc.getPropertyName() );
|
||||
boolean hasNotNull;
|
||||
final Property property =
|
||||
findPropertyByName( persistentClass, prefix + propertyDesc.getPropertyName() );
|
||||
if ( property != null ) {
|
||||
hasNotNull = applyConstraints(
|
||||
propertyDesc.getConstraintDescriptors(), property, propertyDesc, groups, activateNotNull, dialect
|
||||
final boolean hasNotNull = applyConstraints(
|
||||
propertyDesc.getConstraintDescriptors(),
|
||||
property,
|
||||
propertyDesc,
|
||||
groups,
|
||||
activateNotNull,
|
||||
dialect
|
||||
);
|
||||
if ( property.isComposite() && propertyDesc.isCascaded() ) {
|
||||
Class<?> componentClass = ( (Component) property.getValue() ).getComponentClass();
|
||||
|
||||
final Component component = (Component) property.getValue();
|
||||
/*
|
||||
* we can apply not null if the upper component let's us activate not null
|
||||
* we can apply not null if the upper component lets us activate not null
|
||||
* and if the property is not null.
|
||||
* Otherwise, all sub columns should be left nullable
|
||||
*/
|
||||
|
@ -220,7 +219,7 @@ class TypeSafeActivator {
|
|||
applyDDL(
|
||||
prefix + propertyDesc.getPropertyName() + ".",
|
||||
persistentClass,
|
||||
componentClass,
|
||||
component.getComponentClass(),
|
||||
factory,
|
||||
groups,
|
||||
canSetNotNullOnColumns,
|
||||
|
@ -275,8 +274,8 @@ class TypeSafeActivator {
|
|||
private static void applyMin(Property property, ConstraintDescriptor<?> descriptor, Dialect dialect) {
|
||||
if ( Min.class.equals( descriptor.getAnnotation().annotationType() ) ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ConstraintDescriptor<Min> minConstraint = (ConstraintDescriptor<Min>) descriptor;
|
||||
long min = minConstraint.getAnnotation().value();
|
||||
final ConstraintDescriptor<Min> minConstraint = (ConstraintDescriptor<Min>) descriptor;
|
||||
final long min = minConstraint.getAnnotation().value();
|
||||
|
||||
for ( Selectable selectable : property.getSelectables() ) {
|
||||
if ( selectable instanceof Column ) {
|
||||
|
@ -291,8 +290,8 @@ class TypeSafeActivator {
|
|||
private static void applyMax(Property property, ConstraintDescriptor<?> descriptor, Dialect dialect) {
|
||||
if ( Max.class.equals( descriptor.getAnnotation().annotationType() ) ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ConstraintDescriptor<Max> maxConstraint = (ConstraintDescriptor<Max>) descriptor;
|
||||
long max = maxConstraint.getAnnotation().value();
|
||||
final ConstraintDescriptor<Max> maxConstraint = (ConstraintDescriptor<Max>) descriptor;
|
||||
final long max = maxConstraint.getAnnotation().value();
|
||||
|
||||
for ( Selectable selectable : property.getSelectables() ) {
|
||||
if ( selectable instanceof Column ) {
|
||||
|
@ -318,9 +317,10 @@ class TypeSafeActivator {
|
|||
private static boolean applyNotNull(Property property, ConstraintDescriptor<?> descriptor) {
|
||||
boolean hasNotNull = false;
|
||||
// NotNull, NotEmpty, and NotBlank annotation add not-null on column
|
||||
if ( NotNull.class.equals( descriptor.getAnnotation().annotationType())
|
||||
|| NotEmpty.class.equals( descriptor.getAnnotation().annotationType())
|
||||
|| NotBlank.class.equals( descriptor.getAnnotation().annotationType())) {
|
||||
final Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType();
|
||||
if ( NotNull.class.equals(annotationType)
|
||||
|| NotEmpty.class.equals(annotationType)
|
||||
|| NotBlank.class.equals(annotationType)) {
|
||||
// single table inheritance should not be forced to null due to shared state
|
||||
if ( !( property.getPersistentClass() instanceof SingleTableSubclass ) ) {
|
||||
//composite should not add not-null on all columns
|
||||
|
@ -406,7 +406,7 @@ class TypeSafeActivator {
|
|||
String idName = idProperty != null ? idProperty.getName() : null;
|
||||
try {
|
||||
if ( propertyName == null
|
||||
|| propertyName.length() == 0
|
||||
|| propertyName.isEmpty()
|
||||
|| propertyName.equals( idName ) ) {
|
||||
//default to id
|
||||
property = idProperty;
|
||||
|
@ -488,11 +488,9 @@ class TypeSafeActivator {
|
|||
|
||||
private static ValidatorFactory resolveProvidedFactory(SessionFactoryOptions options) {
|
||||
final Object validatorFactoryReference = options.getValidatorFactoryReference();
|
||||
|
||||
if ( validatorFactoryReference == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return (ValidatorFactory) validatorFactoryReference;
|
||||
}
|
||||
|
@ -511,7 +509,7 @@ class TypeSafeActivator {
|
|||
|
||||
private static ValidatorFactory resolveProvidedFactory(ConfigurationService cfgService) {
|
||||
return cfgService.getSetting(
|
||||
AvailableSettings.JPA_VALIDATION_FACTORY,
|
||||
JPA_VALIDATION_FACTORY,
|
||||
value -> {
|
||||
try {
|
||||
return (ValidatorFactory) value;
|
||||
|
@ -521,7 +519,7 @@ class TypeSafeActivator {
|
|||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"ValidatorFactory reference (provided via `%s` setting) was not castable to %s : %s",
|
||||
AvailableSettings.JPA_VALIDATION_FACTORY,
|
||||
JPA_VALIDATION_FACTORY,
|
||||
ValidatorFactory.class.getName(),
|
||||
value.getClass().getName()
|
||||
)
|
||||
|
@ -529,7 +527,7 @@ class TypeSafeActivator {
|
|||
}
|
||||
},
|
||||
cfgService.getSetting(
|
||||
AvailableSettings.JAKARTA_VALIDATION_FACTORY,
|
||||
JAKARTA_VALIDATION_FACTORY,
|
||||
value -> {
|
||||
try {
|
||||
return (ValidatorFactory) value;
|
||||
|
@ -539,7 +537,7 @@ class TypeSafeActivator {
|
|||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"ValidatorFactory reference (provided via `%s` setting) was not castable to %s : %s",
|
||||
AvailableSettings.JAKARTA_VALIDATION_FACTORY,
|
||||
JAKARTA_VALIDATION_FACTORY,
|
||||
ValidatorFactory.class.getName(),
|
||||
value.getClass().getName()
|
||||
)
|
||||
|
|
|
@ -10,14 +10,12 @@ import java.util.Set;
|
|||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.action.internal.CollectionAction;
|
||||
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.cache.spi.access.CollectionDataAccess;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.io.ObjectOutputStream;
|
|||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
|
|
|
@ -115,18 +115,19 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
}
|
||||
final EventSource session = event.getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
final ActionQueue actionQueue = session.getActionQueue();
|
||||
LOG.debugf(
|
||||
"Flushed: %s insertions, %s updates, %s deletions to %s objects",
|
||||
session.getActionQueue().numberOfInsertions(),
|
||||
session.getActionQueue().numberOfUpdates(),
|
||||
session.getActionQueue().numberOfDeletions(),
|
||||
actionQueue.numberOfInsertions(),
|
||||
actionQueue.numberOfUpdates(),
|
||||
actionQueue.numberOfDeletions(),
|
||||
persistenceContext.getNumberOfManagedEntities()
|
||||
);
|
||||
LOG.debugf(
|
||||
"Flushed: %s (re)creations, %s updates, %s removals to %s collections",
|
||||
session.getActionQueue().numberOfCollectionCreations(),
|
||||
session.getActionQueue().numberOfCollectionUpdates(),
|
||||
session.getActionQueue().numberOfCollectionRemovals(),
|
||||
actionQueue.numberOfCollectionCreations(),
|
||||
actionQueue.numberOfCollectionUpdates(),
|
||||
actionQueue.numberOfCollectionRemovals(),
|
||||
persistenceContext.getCollectionEntriesSize()
|
||||
);
|
||||
new EntityPrinter( session.getFactory() ).toString(
|
||||
|
@ -205,8 +206,8 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
LOG.trace( "Flushing entities and processing referenced collections" );
|
||||
|
||||
final EventSource source = event.getSession();
|
||||
final EventListenerGroup<FlushEntityEventListener> flushListeners = source.getFactory()
|
||||
.getFastSessionServices().eventListenerGroup_FLUSH_ENTITY;
|
||||
final EventListenerGroup<FlushEntityEventListener> flushListeners =
|
||||
event.getFactory().getFastSessionServices().eventListenerGroup_FLUSH_ENTITY;
|
||||
|
||||
// Among other things, updateReachables() will recursively load all
|
||||
// collections that are moving roles. This might cause entities to
|
||||
|
|
|
@ -47,7 +47,7 @@ public abstract class AbstractReassociateEventListener {
|
|||
if ( log.isTraceEnabled() ) {
|
||||
log.tracev(
|
||||
"Reassociating transient instance: {0}",
|
||||
MessageHelper.infoString( persister, id, event.getSession().getFactory() )
|
||||
MessageHelper.infoString( persister, id, event.getFactory() )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import org.hibernate.engine.spi.CascadingAction;
|
|||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityEntryExtraState;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.ManagedEntity;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SelfDirtinessTracker;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
|
|
@ -104,7 +104,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
|
|||
if ( lazyInitializer != null ) {
|
||||
if ( lazyInitializer.isUninitialized() ) {
|
||||
final EventSource source = event.getSession();
|
||||
final EntityPersister persister = source.getFactory().getMappingMetamodel()
|
||||
final EntityPersister persister = event.getFactory().getMappingMetamodel()
|
||||
.findEntityDescriptor( lazyInitializer.getEntityName() );
|
||||
final Object id = lazyInitializer.getIdentifier();
|
||||
final EntityKey key = source.generateEntityKey( id, persister );
|
||||
|
|
|
@ -223,10 +223,9 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
final Object entity = event.getEntity();
|
||||
processIfSelfDirtinessTracker( entity, SelfDirtinessTracker::$$_hibernate_clearDirtyAttributes );
|
||||
processIfManagedEntity( entity, DefaultFlushEntityEventListener::useTracker );
|
||||
final EventSource source = event.getSession();
|
||||
source.getFactory()
|
||||
event.getFactory()
|
||||
.getCustomEntityDirtinessStrategy()
|
||||
.resetDirty( entity, entry.getPersister(), source );
|
||||
.resetDirty( entity, entry.getPersister(), event.getSession() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +246,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
final EntityPersister persister = entry.getPersister();
|
||||
final Object[] values = event.getPropertyValues();
|
||||
|
||||
logScheduleUpdate( entry, session, status, persister );
|
||||
logScheduleUpdate( entry, event.getFactory(), status, persister );
|
||||
|
||||
final boolean intercepted = !entry.isBeingReplicated() && handleInterception( event );
|
||||
|
||||
|
@ -299,32 +298,32 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
}
|
||||
}
|
||||
|
||||
private static void logScheduleUpdate(EntityEntry entry, EventSource session, Status status, EntityPersister persister) {
|
||||
private static void logScheduleUpdate(EntityEntry entry, SessionFactoryImplementor factory, Status status, EntityPersister persister) {
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
if ( status == Status.DELETED ) {
|
||||
if ( !persister.isMutable() ) {
|
||||
LOG.tracev(
|
||||
"Updating immutable, deleted entity: {0}",
|
||||
MessageHelper.infoString(persister, entry.getId(), session.getFactory() )
|
||||
MessageHelper.infoString(persister, entry.getId(), factory)
|
||||
);
|
||||
}
|
||||
else if ( !entry.isModifiableEntity() ) {
|
||||
LOG.tracev(
|
||||
"Updating non-modifiable, deleted entity: {0}",
|
||||
MessageHelper.infoString(persister, entry.getId(), session.getFactory() )
|
||||
MessageHelper.infoString(persister, entry.getId(), factory)
|
||||
);
|
||||
}
|
||||
else {
|
||||
LOG.tracev(
|
||||
"Updating deleted entity: {0}",
|
||||
MessageHelper.infoString(persister, entry.getId(), session.getFactory() )
|
||||
MessageHelper.infoString(persister, entry.getId(), factory)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG.tracev(
|
||||
"Updating entity: {0}",
|
||||
MessageHelper.infoString(persister, entry.getId(), session.getFactory() )
|
||||
MessageHelper.infoString(persister, entry.getId(), factory)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +351,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
|
||||
if ( entry.getStatus() != Status.DELETED ) {
|
||||
if ( callbackRegistry.preUpdate( entity ) ) {
|
||||
isDirty = copyState( entity, persister.getPropertyTypes(), values, session.getFactory() );
|
||||
isDirty = copyState( entity, persister.getPropertyTypes(), values, event.getFactory() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,10 +592,9 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
|
|||
}
|
||||
}
|
||||
}
|
||||
final EventSource session = event.getSession();
|
||||
final DirtyCheckContextImpl context = new DirtyCheckContextImpl();
|
||||
session.getFactory().getCustomEntityDirtinessStrategy()
|
||||
.findDirty( event.getEntity(), event.getEntityEntry().getPersister(), session, context );
|
||||
event.getFactory().getCustomEntityDirtinessStrategy()
|
||||
.findDirty( event.getEntity(), event.getEntityEntry().getPersister(), event.getSession(), context );
|
||||
return context.found;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,21 +85,19 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
|
||||
protected EntityPersister getPersister(final LoadEvent event) {
|
||||
final Object instanceToLoad = event.getInstanceToLoad();
|
||||
final EventSource source = event.getSession();
|
||||
if ( instanceToLoad != null ) {
|
||||
//the load() which takes an entity does not pass an entityName
|
||||
event.setEntityClassName( instanceToLoad.getClass().getName() );
|
||||
return source.getEntityPersister( null, instanceToLoad );
|
||||
return event.getSession().getEntityPersister( null, instanceToLoad );
|
||||
}
|
||||
else {
|
||||
return source.getFactory().getMappingMetamodel().getEntityDescriptor( event.getEntityClassName() );
|
||||
return event.getFactory().getMappingMetamodel().getEntityDescriptor( event.getEntityClassName() );
|
||||
}
|
||||
}
|
||||
|
||||
private void doOnLoad(EntityPersister persister, LoadEvent event, LoadType loadType) {
|
||||
try {
|
||||
final EventSource session = event.getSession();
|
||||
final EntityKey keyToLoad = session.generateEntityKey( event.getEntityId(), persister );
|
||||
final EntityKey keyToLoad = event.getSession().generateEntityKey( event.getEntityId(), persister );
|
||||
if ( loadType.isNakedEntityReturned() ) {
|
||||
//do not return a proxy!
|
||||
//(this option indicates we are initializing a proxy)
|
||||
|
@ -193,22 +191,22 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
* @return The loaded entity.
|
||||
*/
|
||||
private Object load(LoadEvent event, EntityPersister persister, EntityKey keyToLoad, LoadType options) {
|
||||
final EventSource session = event.getSession();
|
||||
if ( event.getInstanceToLoad() != null ) {
|
||||
final EventSource session = event.getSession();
|
||||
if ( session.getPersistenceContextInternal().getEntry( event.getInstanceToLoad() ) != null ) {
|
||||
throw new PersistentObjectException(
|
||||
"attempted to load into an instance that was already associated with the session: "
|
||||
+ infoString( persister, event.getEntityId(), session.getFactory() )
|
||||
+ infoString( persister, event.getEntityId(), event.getFactory() )
|
||||
);
|
||||
}
|
||||
persister.setIdentifier( event.getInstanceToLoad(), event.getEntityId(), session);
|
||||
persister.setIdentifier( event.getInstanceToLoad(), event.getEntityId(), session );
|
||||
}
|
||||
|
||||
final Object entity = doLoad( event, persister, keyToLoad, options );
|
||||
boolean isOptionalInstance = event.getInstanceToLoad() != null;
|
||||
if ( entity == null
|
||||
&& ( !options.isAllowNulls() || isOptionalInstance ) ) {
|
||||
session.getFactory().getEntityNotFoundDelegate()
|
||||
event.getFactory().getEntityNotFoundDelegate()
|
||||
.handleEntityNotFound( event.getEntityClassName(), event.getEntityId() );
|
||||
}
|
||||
else if ( isOptionalInstance && entity != event.getInstanceToLoad() ) {
|
||||
|
@ -397,7 +395,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
else {
|
||||
if ( options != LoadEventListener.INTERNAL_LOAD_NULLABLE ) {
|
||||
// throw an appropriate exception
|
||||
event.getSession().getFactory().getEntityNotFoundDelegate()
|
||||
event.getFactory().getEntityNotFoundDelegate()
|
||||
.handleEntityNotFound( persister.getEntityName(), keyToLoad.getIdentifier() );
|
||||
}
|
||||
// Otherwise, if it's INTERNAL_LOAD_NULLABLE, the proxy is
|
||||
|
@ -448,8 +446,9 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
|
||||
private static Object createProxy(LoadEvent event, EntityPersister persister, EntityKey keyToLoad) {
|
||||
// return new uninitialized proxy
|
||||
final Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
|
||||
PersistenceContext persistenceContext = event.getSession().getPersistenceContextInternal();
|
||||
final EventSource session = event.getSession();
|
||||
final Object proxy = persister.createProxy( event.getEntityId(), session );
|
||||
PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
|
||||
persistenceContext.addProxy( keyToLoad, proxy );
|
||||
return proxy;
|
||||
|
@ -477,7 +476,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
ck = cache.generateCacheKey(
|
||||
event.getEntityId(),
|
||||
persister,
|
||||
source.getFactory(),
|
||||
event.getFactory(),
|
||||
source.getTenantIdentifier()
|
||||
);
|
||||
lock = cache.lockItem( source, ck, null );
|
||||
|
@ -520,7 +519,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"Attempting to resolve: {0}",
|
||||
infoString( persister, event.getEntityId(), session.getFactory() )
|
||||
infoString( persister, event.getEntityId(), event.getFactory() )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -575,7 +574,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"Resolved object in second-level cache: {0}",
|
||||
infoString( persister, event.getEntityId(), event.getSession().getFactory() )
|
||||
infoString( persister, event.getEntityId(), event.getFactory() )
|
||||
);
|
||||
}
|
||||
return entity;
|
||||
|
@ -584,7 +583,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"Object not resolved in any cache: {0}",
|
||||
infoString( persister, event.getEntityId(), event.getSession().getFactory() )
|
||||
infoString( persister, event.getEntityId(), event.getFactory() )
|
||||
);
|
||||
}
|
||||
return loadFromDatasource( event, persister );
|
||||
|
@ -620,7 +619,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
|
|||
entity = lazyInitializer.getImplementation();
|
||||
}
|
||||
|
||||
final StatisticsImplementor statistics = event.getSession().getFactory().getStatistics();
|
||||
final StatisticsImplementor statistics = event.getFactory().getStatistics();
|
||||
if ( event.isAssociationFetch() && statistics.isStatisticsEnabled() ) {
|
||||
statistics.fetchEntity( event.getEntityClassName() );
|
||||
}
|
||||
|
|
|
@ -216,16 +216,15 @@ public class DefaultMergeEventListener
|
|||
entityIsPersistent( event, copiedAlready );
|
||||
break;
|
||||
default: //DELETED
|
||||
if ( event.getSession().getPersistenceContext().getEntry( entity ) == null ) {
|
||||
assert event.getSession().getPersistenceContext().containsDeletedUnloadedEntityKey(
|
||||
event.getSession().generateEntityKey(
|
||||
event.getSession()
|
||||
.getEntityPersister( event.getEntityName(), entity )
|
||||
if ( persistenceContext.getEntry( entity ) == null ) {
|
||||
assert persistenceContext.containsDeletedUnloadedEntityKey(
|
||||
source.generateEntityKey(
|
||||
source.getEntityPersister( event.getEntityName(), entity )
|
||||
.getIdentifier( entity, event.getSession() ),
|
||||
event.getSession().getEntityPersister( event.getEntityName(), entity )
|
||||
source.getEntityPersister( event.getEntityName(), entity )
|
||||
)
|
||||
);
|
||||
event.getSession().getActionQueue().unScheduleUnloadedDeletion( entity );
|
||||
source.getActionQueue().unScheduleUnloadedDeletion( entity );
|
||||
entityIsDetached(event, copiedId, originalId, copiedAlready);
|
||||
break;
|
||||
}
|
||||
|
@ -395,7 +394,7 @@ public class DefaultMergeEventListener
|
|||
}
|
||||
final Object clonedIdentifier;
|
||||
if ( copiedId == null ) {
|
||||
clonedIdentifier = persister.getIdentifierType().deepCopy( originalId, source.getFactory() );
|
||||
clonedIdentifier = persister.getIdentifierType().deepCopy( originalId, event.getFactory() );
|
||||
}
|
||||
else {
|
||||
clonedIdentifier = copiedId;
|
||||
|
|
|
@ -120,7 +120,7 @@ public class DefaultPersistEventListener
|
|||
// entity state again.
|
||||
|
||||
// NOTE: entityEntry must be null to get here, so we cannot use any of its values
|
||||
final EntityPersister persister = source.getFactory().getMappingMetamodel()
|
||||
final EntityPersister persister = event.getFactory().getMappingMetamodel()
|
||||
.getEntityDescriptor( entityName );
|
||||
if ( persister.getGenerator() instanceof ForeignGenerator ) {
|
||||
if ( LOG.isDebugEnabled() && persister.getIdentifier( entity, source ) != null ) {
|
||||
|
@ -183,7 +183,7 @@ public class DefaultPersistEventListener
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracef(
|
||||
"un-scheduling entity deletion [%s]",
|
||||
MessageHelper.infoString( persister, persister.getIdentifier( entity, source ), source.getFactory() )
|
||||
MessageHelper.infoString( persister, persister.getIdentifier( entity, source ), event.getFactory() )
|
||||
);
|
||||
}
|
||||
if ( createCache.add( entity ) ) {
|
||||
|
|
|
@ -134,7 +134,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"Refreshing transient {0}",
|
||||
infoString( persister, id, source.getFactory() )
|
||||
infoString( persister, id, event.getFactory() )
|
||||
);
|
||||
}
|
||||
if ( persistenceContext.getEntry( source.generateEntityKey( id, persister ) ) != null ) {
|
||||
|
@ -145,7 +145,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"Refreshing ",
|
||||
infoString( entry.getPersister(), entry.getId(), source.getFactory() )
|
||||
infoString( entry.getPersister(), entry.getId(), event.getFactory() )
|
||||
);
|
||||
}
|
||||
if ( !entry.isExistsInDatabase() ) {
|
||||
|
|
|
@ -85,11 +85,8 @@ public class DefaultReplicateEventListener
|
|||
if ( oldVersion != null ) {
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"Found existing row for {0}", MessageHelper.infoString(
|
||||
persister,
|
||||
id,
|
||||
source.getFactory()
|
||||
)
|
||||
"Found existing row for {0}",
|
||||
MessageHelper.infoString( persister, id, event.getFactory() )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -119,7 +116,7 @@ public class DefaultReplicateEventListener
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"No existing row, replicating new instance {0}",
|
||||
MessageHelper.infoString( persister, id, source.getFactory() )
|
||||
MessageHelper.infoString( persister, id, event.getFactory() )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ import org.hibernate.event.spi.ResolveNaturalIdEventListener;
|
|||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
@ -106,7 +105,7 @@ public class DefaultResolveNaturalIdEventListener
|
|||
protected Object loadFromDatasource(ResolveNaturalIdEvent event) {
|
||||
final EventSource session = event.getSession();
|
||||
final EntityPersister entityPersister = event.getEntityPersister();
|
||||
final StatisticsImplementor statistics = session.getFactory().getStatistics();
|
||||
final StatisticsImplementor statistics = event.getFactory().getStatistics();
|
||||
final boolean statisticsEnabled = statistics.isStatisticsEnabled();
|
||||
final long startTime = statisticsEnabled ? System.nanoTime() : 0;
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ public class DefaultSaveOrUpdateEventListener
|
|||
throw new AssertionFailure( "entity was deleted" );
|
||||
}
|
||||
|
||||
final SessionFactoryImplementor factory = event.getSession().getFactory();
|
||||
final SessionFactoryImplementor factory = event.getFactory();
|
||||
|
||||
final Object requestedId = event.getRequestedId();
|
||||
final Object savedId;
|
||||
|
@ -250,7 +250,7 @@ public class DefaultSaveOrUpdateEventListener
|
|||
}
|
||||
LOG.tracev(
|
||||
"Updating {0}",
|
||||
MessageHelper.infoString( persister, event.getRequestedId(), source.getFactory() )
|
||||
MessageHelper.infoString( persister, event.getRequestedId(), event.getFactory() )
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ public class DefaultSaveOrUpdateEventListener
|
|||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev(
|
||||
"Updating {0}",
|
||||
MessageHelper.infoString( persister, event.getRequestedId(), source.getFactory() )
|
||||
MessageHelper.infoString( persister, event.getRequestedId(), event.getFactory() )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.event.internal;
|
||||
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.PostUpdateEvent;
|
||||
|
@ -33,9 +32,9 @@ public class PostUpdateEventListenerStandardImpl implements PostUpdateEventListe
|
|||
}
|
||||
|
||||
private void handlePostUpdate(Object entity, EventSource source) {
|
||||
EntityEntry entry = source.getPersistenceContextInternal().getEntry( entity );
|
||||
// mimic the preUpdate filter
|
||||
if ( Status.DELETED != entry.getStatus() ) {
|
||||
if ( source == null // it must be a StatelessSession
|
||||
|| source.getPersistenceContextInternal().getEntry(entity).getStatus() != Status.DELETED ) {
|
||||
callbackRegistry.postUpdate(entity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.event.internal;
|
||||
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.PostUpsertEvent;
|
||||
import org.hibernate.event.spi.PostUpsertEventListener;
|
||||
import org.hibernate.jpa.event.spi.CallbackRegistry;
|
||||
import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* This is just a stub, since we don't yet have a {@code @PostUpsert} callback
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PostUpsertEventListenerStandardImpl implements PostUpsertEventListener, CallbackRegistryConsumer {
|
||||
private CallbackRegistry callbackRegistry;
|
||||
|
||||
@Override
|
||||
public void injectCallbackRegistry(CallbackRegistry callbackRegistry) {
|
||||
this.callbackRegistry = callbackRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostUpsert(PostUpsertEvent event) {
|
||||
handlePostUpsert( event.getEntity(), event.getSession() );
|
||||
}
|
||||
|
||||
private void handlePostUpsert(Object entity, EventSource source) {
|
||||
// // mimic the preUpdate filter
|
||||
// if ( source == null // it must be a StatelessSession
|
||||
// || source.getPersistenceContextInternal().getEntry(entity).getStatus() != Status.DELETED ) {
|
||||
// callbackRegistry.postUpdate(entity);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPostCommitHandling(EntityPersister persister) {
|
||||
return false; //callbackRegistry.hasRegisteredCallbacks( persister.getMappedClass(), CallbackType.POST_UPDATE );
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ import org.hibernate.event.internal.DefaultUpdateEventListener;
|
|||
import org.hibernate.event.internal.PostDeleteEventListenerStandardImpl;
|
||||
import org.hibernate.event.internal.PostInsertEventListenerStandardImpl;
|
||||
import org.hibernate.event.internal.PostUpdateEventListenerStandardImpl;
|
||||
import org.hibernate.event.internal.PostUpsertEventListenerStandardImpl;
|
||||
import org.hibernate.event.service.spi.DuplicationStrategy;
|
||||
import org.hibernate.event.service.spi.EventListenerGroup;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistrationException;
|
||||
|
@ -68,6 +69,7 @@ import static org.hibernate.event.spi.EventType.POST_DELETE;
|
|||
import static org.hibernate.event.spi.EventType.POST_INSERT;
|
||||
import static org.hibernate.event.spi.EventType.POST_LOAD;
|
||||
import static org.hibernate.event.spi.EventType.POST_UPDATE;
|
||||
import static org.hibernate.event.spi.EventType.POST_UPSERT;
|
||||
import static org.hibernate.event.spi.EventType.PRE_COLLECTION_RECREATE;
|
||||
import static org.hibernate.event.spi.EventType.PRE_COLLECTION_REMOVE;
|
||||
import static org.hibernate.event.spi.EventType.PRE_COLLECTION_UPDATE;
|
||||
|
@ -75,6 +77,7 @@ import static org.hibernate.event.spi.EventType.PRE_DELETE;
|
|||
import static org.hibernate.event.spi.EventType.PRE_INSERT;
|
||||
import static org.hibernate.event.spi.EventType.PRE_LOAD;
|
||||
import static org.hibernate.event.spi.EventType.PRE_UPDATE;
|
||||
import static org.hibernate.event.spi.EventType.PRE_UPSERT;
|
||||
import static org.hibernate.event.spi.EventType.REFRESH;
|
||||
import static org.hibernate.event.spi.EventType.REPLICATE;
|
||||
import static org.hibernate.event.spi.EventType.RESOLVE_NATURAL_ID;
|
||||
|
@ -97,14 +100,14 @@ public class EventListenerRegistryImpl implements EventListenerRegistry {
|
|||
this.eventListeners = eventListeners;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> EventListenerGroup<T> getEventListenerGroup(EventType<T> eventType) {
|
||||
if ( eventListeners.length < eventType.ordinal() + 1 ) {
|
||||
// eventTpe is a custom EventType that has not been registered.
|
||||
// eventType is a custom EventType that has not been registered.
|
||||
// registeredEventListeners array was not allocated enough space to
|
||||
// accommodate it.
|
||||
throw new HibernateException( "Unable to find listeners for type [" + eventType.eventName() + "]" );
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final EventListenerGroup<T> listeners = eventListeners[ eventType.ordinal() ];
|
||||
if ( listeners == null ) {
|
||||
throw new HibernateException( "Unable to find listeners for type [" + eventType.eventName() + "]" );
|
||||
|
@ -278,6 +281,9 @@ public class EventListenerRegistryImpl implements EventListenerRegistry {
|
|||
// pre-update listeners
|
||||
prepareListeners( PRE_UPDATE );
|
||||
|
||||
// pre-update listeners
|
||||
prepareListeners( PRE_UPSERT );
|
||||
|
||||
// post-collection-recreate listeners
|
||||
prepareListeners( POST_COLLECTION_RECREATE );
|
||||
|
||||
|
@ -308,6 +314,9 @@ public class EventListenerRegistryImpl implements EventListenerRegistry {
|
|||
// post-update listeners
|
||||
prepareListeners( POST_UPDATE, new PostUpdateEventListenerStandardImpl() );
|
||||
|
||||
// post-upsert listeners
|
||||
prepareListeners( POST_UPSERT, new PostUpsertEventListenerStandardImpl() );
|
||||
|
||||
// update listeners
|
||||
prepareListeners( UPDATE, new DefaultUpdateEventListener() );
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
|
@ -36,4 +38,7 @@ public abstract class AbstractEvent implements Serializable {
|
|||
return session;
|
||||
}
|
||||
|
||||
public SessionFactoryImplementor getFactory() {
|
||||
return session.getFactory();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
|
@ -64,5 +65,15 @@ public abstract class AbstractPreDatabaseOperationEvent extends AbstractEvent {
|
|||
public EntityPersister getPersister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory which owns the persister for the entity.
|
||||
*
|
||||
* @return The factory
|
||||
*/
|
||||
@Override
|
||||
public SessionFactoryImplementor getFactory() {
|
||||
return persister.getFactory();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,11 +61,13 @@ public final class EventType<T> {
|
|||
public static final EventType<PreDeleteEventListener> PRE_DELETE = create( "pre-delete", PreDeleteEventListener.class );
|
||||
public static final EventType<PreUpdateEventListener> PRE_UPDATE = create( "pre-update", PreUpdateEventListener.class );
|
||||
public static final EventType<PreInsertEventListener> PRE_INSERT = create( "pre-insert", PreInsertEventListener.class );
|
||||
public static final EventType<PreUpsertEventListener> PRE_UPSERT = create( "pre-upsert", PreUpsertEventListener.class );
|
||||
|
||||
public static final EventType<PostLoadEventListener> POST_LOAD = create( "post-load", PostLoadEventListener.class );
|
||||
public static final EventType<PostDeleteEventListener> POST_DELETE = create( "post-delete", PostDeleteEventListener.class );
|
||||
public static final EventType<PostUpdateEventListener> POST_UPDATE = create( "post-update", PostUpdateEventListener.class );
|
||||
public static final EventType<PostInsertEventListener> POST_INSERT = create( "post-insert", PostInsertEventListener.class );
|
||||
public static final EventType<PostUpsertEventListener> POST_UPSERT = create( "post-upsert", PostUpsertEventListener.class );
|
||||
|
||||
public static final EventType<PostDeleteEventListener> POST_COMMIT_DELETE = create( "post-commit-delete", PostDeleteEventListener.class );
|
||||
public static final EventType<PostUpdateEventListener> POST_COMMIT_UPDATE = create( "post-commit-update", PostUpdateEventListener.class );
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
|
@ -40,6 +41,11 @@ public class PostDeleteEvent extends AbstractEvent {
|
|||
return persister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionFactoryImplementor getFactory() {
|
||||
return persister.getFactory();
|
||||
}
|
||||
|
||||
public Object getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +44,12 @@ public class PostInsertEvent extends AbstractEvent {
|
|||
public EntityPersister getPersister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionFactoryImplementor getFactory() {
|
||||
return persister.getFactory();
|
||||
}
|
||||
|
||||
public Object[] getState() {
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
|
@ -56,6 +57,11 @@ public class PostUpdateEvent extends AbstractEvent {
|
|||
return persister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionFactoryImplementor getFactory() {
|
||||
return persister.getFactory();
|
||||
}
|
||||
|
||||
public Object[] getState() {
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* Occurs after the datastore is updated
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PostUpsertEvent extends AbstractEvent {
|
||||
private Object entity;
|
||||
private EntityPersister persister;
|
||||
private Object[] state;
|
||||
private Object id;
|
||||
//list of dirty properties as computed by Hibernate during a FlushEntityEvent
|
||||
private final int[] dirtyProperties;
|
||||
|
||||
public PostUpsertEvent(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object[] state,
|
||||
int[] dirtyProperties,
|
||||
EntityPersister persister,
|
||||
EventSource source
|
||||
) {
|
||||
super(source);
|
||||
this.entity = entity;
|
||||
this.id = id;
|
||||
this.state = state;
|
||||
this.dirtyProperties = dirtyProperties;
|
||||
this.persister = persister;
|
||||
}
|
||||
|
||||
public Object getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public EntityPersister getPersister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionFactoryImplementor getFactory() {
|
||||
return persister.getFactory();
|
||||
}
|
||||
|
||||
public Object[] getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public int[] getDirtyProperties() {
|
||||
return dirtyProperties;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
/**
|
||||
* Called after updating the datastore
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface PostUpsertEventListener extends PostActionEventListener {
|
||||
void onPostUpsert(PostUpsertEvent event);
|
||||
}
|
|
@ -16,7 +16,7 @@ import org.hibernate.persister.entity.EntityPersister;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PreInsertEvent extends AbstractPreDatabaseOperationEvent {
|
||||
private Object[] state;
|
||||
private final Object[] state;
|
||||
|
||||
/**
|
||||
* Constructs an event containing the pertinent information.
|
||||
|
|
|
@ -16,8 +16,8 @@ import org.hibernate.persister.entity.EntityPersister;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class PreUpdateEvent extends AbstractPreDatabaseOperationEvent {
|
||||
private Object[] state;
|
||||
private Object[] oldState;
|
||||
private final Object[] state;
|
||||
private final Object[] oldState;
|
||||
|
||||
/**
|
||||
* Constructs an event containing the pertinent information.
|
||||
|
@ -25,7 +25,7 @@ public class PreUpdateEvent extends AbstractPreDatabaseOperationEvent {
|
|||
* @param id The id of the entity to use for updating.
|
||||
* @param state The state to be updated.
|
||||
* @param oldState The state of the entity at the time it was loaded from
|
||||
* the database.
|
||||
* the database.
|
||||
* @param persister The entity's persister.
|
||||
* @param source The session from which the event originated.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* Represents a pre-upsert event, which occurs just prior to
|
||||
* performing the upsert of an entity in the database.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PreUpsertEvent extends AbstractPreDatabaseOperationEvent {
|
||||
private final Object[] state;
|
||||
|
||||
/**
|
||||
* Constructs an event containing the pertinent information.
|
||||
* @param entity The entity to be updated.
|
||||
* @param id The id of the entity to use for updating.
|
||||
* @param state The state to be updated.
|
||||
* @param persister The entity's persister.
|
||||
* @param source The session from which the event originated.
|
||||
*/
|
||||
public PreUpsertEvent(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object[] state,
|
||||
EntityPersister persister,
|
||||
EventSource source) {
|
||||
super( source, entity, id, persister );
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the state to be used in the upsert.
|
||||
*
|
||||
* @return The current state.
|
||||
*/
|
||||
public Object[] getState() {
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.event.spi;
|
||||
|
||||
/**
|
||||
* Called before updating the datastore
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface PreUpsertEventListener {
|
||||
/**
|
||||
* Return true if the operation should be vetoed
|
||||
*/
|
||||
boolean onPreUpsert(PreUpsertEvent event);
|
||||
}
|
|
@ -26,7 +26,6 @@ import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
|
|||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.event.internal.EmptyEventManager;
|
||||
import org.hibernate.event.spi.EventManager;
|
||||
import org.hibernate.event.service.spi.EventListenerGroup;
|
||||
import org.hibernate.event.service.spi.EventListenerRegistry;
|
||||
import org.hibernate.event.spi.AutoFlushEventListener;
|
||||
|
@ -34,6 +33,7 @@ import org.hibernate.event.spi.ClearEventListener;
|
|||
import org.hibernate.event.spi.DeleteEventListener;
|
||||
import org.hibernate.event.spi.DirtyCheckEventListener;
|
||||
import org.hibernate.event.spi.EntityCopyObserverFactory;
|
||||
import org.hibernate.event.spi.EventManager;
|
||||
import org.hibernate.event.spi.EventType;
|
||||
import org.hibernate.event.spi.EvictEventListener;
|
||||
import org.hibernate.event.spi.FlushEntityEventListener;
|
||||
|
@ -51,6 +51,7 @@ import org.hibernate.event.spi.PostInsertEventListener;
|
|||
import org.hibernate.event.spi.PostLoadEvent;
|
||||
import org.hibernate.event.spi.PostLoadEventListener;
|
||||
import org.hibernate.event.spi.PostUpdateEventListener;
|
||||
import org.hibernate.event.spi.PostUpsertEventListener;
|
||||
import org.hibernate.event.spi.PreCollectionRecreateEventListener;
|
||||
import org.hibernate.event.spi.PreCollectionRemoveEventListener;
|
||||
import org.hibernate.event.spi.PreCollectionUpdateEventListener;
|
||||
|
@ -58,6 +59,7 @@ import org.hibernate.event.spi.PreDeleteEventListener;
|
|||
import org.hibernate.event.spi.PreInsertEventListener;
|
||||
import org.hibernate.event.spi.PreLoadEventListener;
|
||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
||||
import org.hibernate.event.spi.PreUpsertEventListener;
|
||||
import org.hibernate.event.spi.RefreshEventListener;
|
||||
import org.hibernate.event.spi.ReplicateEventListener;
|
||||
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
|
||||
|
@ -140,6 +142,7 @@ public final class FastSessionServices {
|
|||
public final EventListenerGroup<PostLoadEventListener> eventListenerGroup_POST_LOAD; //Frequently used by 2LC initialization:
|
||||
public final EventListenerGroup<PostUpdateEventListener> eventListenerGroup_POST_COMMIT_UPDATE;
|
||||
public final EventListenerGroup<PostUpdateEventListener> eventListenerGroup_POST_UPDATE;
|
||||
public final EventListenerGroup<PostUpsertEventListener> eventListenerGroup_POST_UPSERT;
|
||||
public final EventListenerGroup<PreCollectionRecreateEventListener> eventListenerGroup_PRE_COLLECTION_RECREATE;
|
||||
public final EventListenerGroup<PreCollectionRemoveEventListener> eventListenerGroup_PRE_COLLECTION_REMOVE;
|
||||
public final EventListenerGroup<PreCollectionUpdateEventListener> eventListenerGroup_PRE_COLLECTION_UPDATE;
|
||||
|
@ -147,6 +150,7 @@ public final class FastSessionServices {
|
|||
public final EventListenerGroup<PreInsertEventListener> eventListenerGroup_PRE_INSERT;
|
||||
public final EventListenerGroup<PreLoadEventListener> eventListenerGroup_PRE_LOAD;
|
||||
public final EventListenerGroup<PreUpdateEventListener> eventListenerGroup_PRE_UPDATE;
|
||||
public final EventListenerGroup<PreUpsertEventListener> eventListenerGroup_PRE_UPSERT;
|
||||
public final EventListenerGroup<RefreshEventListener> eventListenerGroup_REFRESH;
|
||||
public final EventListenerGroup<ReplicateEventListener> eventListenerGroup_REPLICATE;
|
||||
public final EventListenerGroup<ResolveNaturalIdEventListener> eventListenerGroup_RESOLVE_NATURAL_ID;
|
||||
|
@ -220,6 +224,7 @@ public final class FastSessionServices {
|
|||
this.eventListenerGroup_POST_INSERT = listeners( eventListenerRegistry, EventType.POST_INSERT );
|
||||
this.eventListenerGroup_POST_LOAD = listeners( eventListenerRegistry, EventType.POST_LOAD );
|
||||
this.eventListenerGroup_POST_UPDATE = listeners( eventListenerRegistry, EventType.POST_UPDATE );
|
||||
this.eventListenerGroup_POST_UPSERT = listeners( eventListenerRegistry, EventType.POST_UPSERT );
|
||||
this.eventListenerGroup_PRE_COLLECTION_RECREATE = listeners( eventListenerRegistry, EventType.PRE_COLLECTION_RECREATE );
|
||||
this.eventListenerGroup_PRE_COLLECTION_REMOVE = listeners( eventListenerRegistry, EventType.PRE_COLLECTION_REMOVE );
|
||||
this.eventListenerGroup_PRE_COLLECTION_UPDATE = listeners( eventListenerRegistry, EventType.PRE_COLLECTION_UPDATE );
|
||||
|
@ -227,6 +232,7 @@ public final class FastSessionServices {
|
|||
this.eventListenerGroup_PRE_INSERT = listeners( eventListenerRegistry, EventType.PRE_INSERT );
|
||||
this.eventListenerGroup_PRE_LOAD = listeners( eventListenerRegistry, EventType.PRE_LOAD );
|
||||
this.eventListenerGroup_PRE_UPDATE = listeners( eventListenerRegistry, EventType.PRE_UPDATE );
|
||||
this.eventListenerGroup_PRE_UPSERT = listeners( eventListenerRegistry, EventType.PRE_UPSERT );
|
||||
this.eventListenerGroup_REFRESH = listeners( eventListenerRegistry, EventType.REFRESH );
|
||||
this.eventListenerGroup_REPLICATE = listeners( eventListenerRegistry, EventType.REPLICATE );
|
||||
this.eventListenerGroup_RESOLVE_NATURAL_ID = listeners( eventListenerRegistry, EventType.RESOLVE_NATURAL_ID );
|
||||
|
|
|
@ -32,6 +32,22 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
|||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||
import org.hibernate.event.spi.PostDeleteEvent;
|
||||
import org.hibernate.event.spi.PostDeleteEventListener;
|
||||
import org.hibernate.event.spi.PostInsertEvent;
|
||||
import org.hibernate.event.spi.PostInsertEventListener;
|
||||
import org.hibernate.event.spi.PostUpdateEvent;
|
||||
import org.hibernate.event.spi.PostUpdateEventListener;
|
||||
import org.hibernate.event.spi.PostUpsertEvent;
|
||||
import org.hibernate.event.spi.PostUpsertEventListener;
|
||||
import org.hibernate.event.spi.PreDeleteEvent;
|
||||
import org.hibernate.event.spi.PreDeleteEventListener;
|
||||
import org.hibernate.event.spi.PreInsertEvent;
|
||||
import org.hibernate.event.spi.PreInsertEventListener;
|
||||
import org.hibernate.event.spi.PreUpdateEvent;
|
||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
||||
import org.hibernate.event.spi.PreUpsertEvent;
|
||||
import org.hibernate.event.spi.PreUpsertEventListener;
|
||||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
|
@ -114,15 +130,22 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
persister.setValues( entity, state );
|
||||
}
|
||||
}
|
||||
if ( firePreInsert(entity, id, state, persister) ) {
|
||||
return id;
|
||||
}
|
||||
persister.getInsertCoordinator().insert( entity, id, state, this );
|
||||
}
|
||||
else {
|
||||
if ( firePreInsert(entity, null, state, persister) ) {
|
||||
return null;
|
||||
}
|
||||
final GeneratedValues generatedValues = persister.getInsertCoordinator().insert( entity, state, this );
|
||||
id = castNonNull( generatedValues ).getGeneratedValue( persister.getIdentifierMapping() );
|
||||
}
|
||||
persister.setIdentifier( entity, id, this );
|
||||
forEachOwnedCollection( entity, id, persister,
|
||||
(descriptor, collection) -> descriptor.recreate( collection, id, this) );
|
||||
firePostInsert(entity, id, state, persister);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
@ -140,9 +163,12 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
final EntityPersister persister = getEntityPersister( entityName, entity );
|
||||
final Object id = persister.getIdentifier( entity, this );
|
||||
final Object version = persister.getVersion( entity );
|
||||
if ( !firePreDelete(entity, id, persister) ) {
|
||||
forEachOwnedCollection( entity, id, persister,
|
||||
(descriptor, collection) -> descriptor.remove(id, this) );
|
||||
persister.getDeleteCoordinator().delete( entity, id, version, this );
|
||||
firePostDelete(entity, id, persister);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -176,12 +202,15 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
else {
|
||||
oldVersion = null;
|
||||
}
|
||||
if ( !firePreUpdate(entity, id, state, persister) ) {
|
||||
persister.getUpdateCoordinator().update( entity, id, null, state, oldVersion, null, null, false, this );
|
||||
// TODO: can we do better here?
|
||||
forEachOwnedCollection( entity, id, persister,
|
||||
(descriptor, collection) -> descriptor.remove(id, this) );
|
||||
forEachOwnedCollection( entity, id, persister,
|
||||
(descriptor, collection) -> descriptor.recreate( collection, id, this) );
|
||||
firePostUpdate(entity, id, state, persister);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -190,13 +219,17 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
final EntityPersister persister = getEntityPersister( entityName, entity );
|
||||
final Object id = idToUpsert( entity, persister );
|
||||
final Object[] state = persister.getValues( entity );
|
||||
if ( !firePreUpsert(entity, id, state, persister) ) {
|
||||
final Object oldVersion = versionToUpsert( entity, persister, state );
|
||||
persister.getMergeCoordinator().update( entity, id, null, state, oldVersion, null, null, false, this );
|
||||
// TODO: need PreUpsert and PostUpsert events!
|
||||
// TODO: can we do better here?
|
||||
forEachOwnedCollection( entity, id, persister,
|
||||
(descriptor, collection) -> descriptor.remove(id, this) );
|
||||
forEachOwnedCollection( entity, id, persister,
|
||||
(descriptor, collection) -> descriptor.recreate( collection, id, this) );
|
||||
firePostUpsert(entity, id, state, persister);
|
||||
}
|
||||
}
|
||||
|
||||
private Object versionToUpsert(Object entity, EntityPersister persister, Object[] state) {
|
||||
|
@ -239,6 +272,100 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
return id;
|
||||
}
|
||||
|
||||
// event processing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
private boolean firePreInsert(Object entity, Object id, Object[] state, EntityPersister persister) {
|
||||
if ( fastSessionServices.eventListenerGroup_PRE_INSERT.isEmpty() ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
boolean veto = false;
|
||||
final PreInsertEvent event = new PreInsertEvent( entity, id, state, persister, null );
|
||||
for ( PreInsertEventListener listener : fastSessionServices.eventListenerGroup_PRE_INSERT.listeners() ) {
|
||||
veto |= listener.onPreInsert( event );
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean firePreUpdate(Object entity, Object id, Object[] state, EntityPersister persister) {
|
||||
if ( fastSessionServices.eventListenerGroup_PRE_UPDATE.isEmpty() ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
boolean veto = false;
|
||||
final PreUpdateEvent event = new PreUpdateEvent( entity, id, state, null, persister, null );
|
||||
for ( PreUpdateEventListener listener : fastSessionServices.eventListenerGroup_PRE_UPDATE.listeners() ) {
|
||||
veto |= listener.onPreUpdate( event );
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean firePreUpsert(Object entity, Object id, Object[] state, EntityPersister persister) {
|
||||
if ( fastSessionServices.eventListenerGroup_PRE_UPSERT.isEmpty() ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
boolean veto = false;
|
||||
final PreUpsertEvent event = new PreUpsertEvent( entity, id, state, persister, null );
|
||||
for ( PreUpsertEventListener listener : fastSessionServices.eventListenerGroup_PRE_UPSERT.listeners() ) {
|
||||
veto |= listener.onPreUpsert( event );
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean firePreDelete(Object entity, Object id, EntityPersister persister) {
|
||||
if ( fastSessionServices.eventListenerGroup_PRE_DELETE.isEmpty() ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
boolean veto = false;
|
||||
final PreDeleteEvent event = new PreDeleteEvent( entity, id, null, persister, null );
|
||||
for ( PreDeleteEventListener listener : fastSessionServices.eventListenerGroup_PRE_DELETE.listeners() ) {
|
||||
veto |= listener.onPreDelete( event );
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
}
|
||||
|
||||
private void firePostInsert(Object entity, Object id, Object[] state, EntityPersister persister) {
|
||||
if ( !fastSessionServices.eventListenerGroup_POST_INSERT.isEmpty() ) {
|
||||
final PostInsertEvent event = new PostInsertEvent( entity, id, state, persister, null );
|
||||
for ( PostInsertEventListener listener : fastSessionServices.eventListenerGroup_POST_INSERT.listeners() ) {
|
||||
listener.onPostInsert( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void firePostUpdate(Object entity, Object id, Object[] state, EntityPersister persister) {
|
||||
if ( !fastSessionServices.eventListenerGroup_POST_UPDATE.isEmpty() ) {
|
||||
final PostUpdateEvent event = new PostUpdateEvent( entity, id, state, null, null, persister, null );
|
||||
for ( PostUpdateEventListener listener : fastSessionServices.eventListenerGroup_POST_UPDATE.listeners() ) {
|
||||
listener.onPostUpdate( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void firePostUpsert(Object entity, Object id, Object[] state, EntityPersister persister) {
|
||||
if ( !fastSessionServices.eventListenerGroup_POST_UPSERT.isEmpty() ) {
|
||||
final PostUpsertEvent event = new PostUpsertEvent( entity, id, state, null, persister, null );
|
||||
for ( PostUpsertEventListener listener : fastSessionServices.eventListenerGroup_POST_UPSERT.listeners() ) {
|
||||
listener.onPostUpsert( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void firePostDelete(Object entity, Object id, EntityPersister persister) {
|
||||
if (!fastSessionServices.eventListenerGroup_POST_DELETE.isEmpty()) {
|
||||
final PostDeleteEvent event = new PostDeleteEvent( entity, id, null, persister, null );
|
||||
for ( PostDeleteEventListener listener : fastSessionServices.eventListenerGroup_POST_DELETE.listeners() ) {
|
||||
listener.onPostDelete( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
private void forEachOwnedCollection(
|
||||
|
|
|
@ -169,8 +169,7 @@ public class CacheEntityLoaderHelper {
|
|||
}
|
||||
}
|
||||
if ( options.isAllowNulls() ) {
|
||||
final EntityPersister persister = event.getSession()
|
||||
.getFactory()
|
||||
final EntityPersister persister = event.getFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel()
|
||||
.getEntityDescriptor( keyToLoad.getEntityName() );
|
||||
|
@ -215,7 +214,7 @@ public class CacheEntityLoaderHelper {
|
|||
.setId( event.getEntityId() )
|
||||
.setPersister( persister );
|
||||
|
||||
event.getSession().getSessionFactory()
|
||||
event.getFactory()
|
||||
.getFastSessionServices()
|
||||
.firePostLoadEvent( postLoadEvent );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.jpa.beanvalidation;
|
||||
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@SessionFactory
|
||||
@DomainModel(annotatedClasses = CupHolder.class)
|
||||
@ServiceRegistry(settings = @Setting(name = AvailableSettings.JAKARTA_VALIDATION_MODE, value = "auto"))
|
||||
public class StatelessBeanValidationTest {
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope){
|
||||
scope.inTransaction(
|
||||
session -> session.createQuery( "delete from CupHolder" ).executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatelessBeanValidationIntegrationOnInsert(SessionFactoryScope scope) {
|
||||
scope.inStatelessTransaction(
|
||||
entityManager -> {
|
||||
CupHolder ch = new CupHolder();
|
||||
ch.setRadius( new BigDecimal( "12" ) );
|
||||
ch.setTitle( "foo" );
|
||||
try {
|
||||
entityManager.insert(ch);
|
||||
fail( "invalid object should not be persisted" );
|
||||
}
|
||||
catch (ConstraintViolationException e) {
|
||||
assertEquals( 1, e.getConstraintViolations().size() );
|
||||
}
|
||||
assertFalse(
|
||||
entityManager.getTransaction().getRollbackOnly(),
|
||||
"Stateless session errors don't need to mark the transaction for rollback"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatelessBeanValidationIntegrationOnUpdate(SessionFactoryScope scope) {
|
||||
scope.inStatelessTransaction(
|
||||
entityManager -> {
|
||||
CupHolder ch = new CupHolder();
|
||||
ch.setId(123);
|
||||
ch.setRadius( new BigDecimal( "12" ) );
|
||||
ch.setTitle( "foo" );
|
||||
try {
|
||||
entityManager.update(ch);
|
||||
fail( "invalid object should not be persisted" );
|
||||
}
|
||||
catch (ConstraintViolationException e) {
|
||||
assertEquals( 1, e.getConstraintViolations().size() );
|
||||
}
|
||||
assertFalse(
|
||||
entityManager.getTransaction().getRollbackOnly(),
|
||||
"Stateless session errors don't need to mark the transaction for rollback"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatelessBeanValidationIntegrationOnUpsert(SessionFactoryScope scope) {
|
||||
scope.inStatelessTransaction(
|
||||
entityManager -> {
|
||||
CupHolder ch = new CupHolder();
|
||||
ch.setId(123);
|
||||
ch.setRadius( new BigDecimal( "12" ) );
|
||||
ch.setTitle( "foo" );
|
||||
try {
|
||||
entityManager.upsert(ch);
|
||||
fail( "invalid object should not be persisted" );
|
||||
}
|
||||
catch (ConstraintViolationException e) {
|
||||
assertEquals( 1, e.getConstraintViolations().size() );
|
||||
}
|
||||
assertFalse(
|
||||
entityManager.getTransaction().getRollbackOnly(),
|
||||
"Stateless session errors don't need to mark the transaction for rollback"
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package org.hibernate.orm.test.stateless;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.PostPersist;
|
||||
import jakarta.persistence.PostRemove;
|
||||
import jakarta.persistence.PostUpdate;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreRemove;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@SessionFactory
|
||||
@DomainModel(annotatedClasses = StatelessCallbacksTest.WithCallbacks.class)
|
||||
public class StatelessCallbacksTest {
|
||||
@Test void test(SessionFactoryScope scope) {
|
||||
scope.inStatelessSession(s -> {
|
||||
WithCallbacks instance = new WithCallbacks();
|
||||
instance.name = "gavin";
|
||||
s.insert(instance);
|
||||
// because the semantics of @PrePersist and @PreRemove
|
||||
// are inappropriate for a StatelessSession, the @Pre
|
||||
// don't get called. However, the @Post events do make
|
||||
// sense, since they correspond to database operations
|
||||
assertFalse(instance.prePersist);
|
||||
assertTrue(instance.postPersist);
|
||||
s.update(instance);
|
||||
assertFalse(instance.preUpdate);
|
||||
assertTrue(instance.postUpdate);
|
||||
s.delete(instance);
|
||||
assertFalse(instance.preRemove);
|
||||
assertTrue(instance.postRemove);
|
||||
});
|
||||
}
|
||||
|
||||
@Entity
|
||||
static class WithCallbacks {
|
||||
boolean prePersist = false;
|
||||
boolean preUpdate = false;
|
||||
boolean preRemove = false;
|
||||
boolean postPersist = false;
|
||||
boolean postUpdate = false;
|
||||
boolean postRemove = false;
|
||||
|
||||
@GeneratedValue @Id
|
||||
Long id;
|
||||
String name;
|
||||
@PrePersist
|
||||
void prePersist() {
|
||||
prePersist = true;
|
||||
}
|
||||
@PostPersist
|
||||
void postPersist() {
|
||||
postPersist = true;
|
||||
}
|
||||
@PreUpdate
|
||||
void preUpdate() {
|
||||
preUpdate = true;
|
||||
}
|
||||
@PostUpdate
|
||||
void postUpdate() {
|
||||
postUpdate = true;
|
||||
}
|
||||
@PreRemove
|
||||
void preRemove() {
|
||||
preRemove = true;
|
||||
}
|
||||
@PostRemove
|
||||
void postRemove() {
|
||||
postRemove = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ import org.hibernate.engine.spi.EntityEntry;
|
|||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.event.spi.AutoFlushEvent;
|
||||
import org.hibernate.event.spi.EventManager;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.event.spi.HibernateMonitoringEvent;
|
||||
import org.hibernate.internal.build.AllowNonPortable;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
@ -453,8 +452,7 @@ public class JfrEventManager implements EventManager {
|
|||
flushEvent.end();
|
||||
if ( flushEvent.shouldCommit() ) {
|
||||
flushEvent.executionTime = getExecutionTime( flushEvent.startedAt );
|
||||
EventSource session = event.getSession();
|
||||
flushEvent.sessionIdentifier = getSessionIdentifier( session );
|
||||
flushEvent.sessionIdentifier = getSessionIdentifier( event.getSession() );
|
||||
flushEvent.numberOfEntitiesProcessed = event.getNumberOfEntitiesProcessed();
|
||||
flushEvent.numberOfCollectionsProcessed = event.getNumberOfCollectionsProcessed();
|
||||
flushEvent.isAutoFlush = autoFlush;
|
||||
|
@ -485,8 +483,7 @@ public class JfrEventManager implements EventManager {
|
|||
flushEvent.end();
|
||||
if ( flushEvent.shouldCommit() ) {
|
||||
flushEvent.executionTime = getExecutionTime( flushEvent.startedAt );
|
||||
EventSource session = event.getSession();
|
||||
flushEvent.sessionIdentifier = getSessionIdentifier( session );
|
||||
flushEvent.sessionIdentifier = getSessionIdentifier( event.getSession() );
|
||||
flushEvent.numberOfEntitiesProcessed = event.getNumberOfEntitiesProcessed();
|
||||
flushEvent.numberOfCollectionsProcessed = event.getNumberOfCollectionsProcessed();
|
||||
flushEvent.isAutoFlush = true;
|
||||
|
|
|
@ -101,7 +101,7 @@ public class HibernateQueryMetrics implements MeterBinder {
|
|||
|
||||
@Override
|
||||
public void onPostLoad(PostLoadEvent event) {
|
||||
registerQueryMetric( event.getSession().getFactory().getStatistics() );
|
||||
registerQueryMetric( event.getFactory().getStatistics() );
|
||||
}
|
||||
|
||||
void registerQueryMetric(Statistics statistics) {
|
||||
|
|
Loading…
Reference in New Issue