minor cleanups in TypeSafeActivator
This commit is contained in:
parent
968a66186d
commit
7afb5a0c50
|
@ -6,16 +6,20 @@ package org.hibernate.boot.beanvalidation;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.Digits;
|
||||||
|
import jakarta.validation.constraints.Max;
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
|
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
|
||||||
|
@ -47,12 +51,19 @@ import jakarta.validation.metadata.BeanDescriptor;
|
||||||
import jakarta.validation.metadata.ConstraintDescriptor;
|
import jakarta.validation.metadata.ConstraintDescriptor;
|
||||||
import jakarta.validation.metadata.PropertyDescriptor;
|
import jakarta.validation.metadata.PropertyDescriptor;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.Collections.disjoint;
|
||||||
|
import static org.hibernate.boot.beanvalidation.BeanValidationIntegrator.APPLY_CONSTRAINTS;
|
||||||
import static org.hibernate.boot.beanvalidation.GroupsPerOperation.buildGroupsForOperation;
|
import static org.hibernate.boot.beanvalidation.GroupsPerOperation.buildGroupsForOperation;
|
||||||
import static org.hibernate.cfg.ValidationSettings.CHECK_NULLABILITY;
|
import static org.hibernate.cfg.ValidationSettings.CHECK_NULLABILITY;
|
||||||
import static org.hibernate.cfg.ValidationSettings.JAKARTA_VALIDATION_FACTORY;
|
import static org.hibernate.cfg.ValidationSettings.JAKARTA_VALIDATION_FACTORY;
|
||||||
import static org.hibernate.cfg.ValidationSettings.JPA_VALIDATION_FACTORY;
|
import static org.hibernate.cfg.ValidationSettings.JPA_VALIDATION_FACTORY;
|
||||||
|
import static org.hibernate.internal.util.StringHelper.isEmpty;
|
||||||
|
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Sets up Bean Validation {@linkplain BeanValidationEventListener event listener} and DDL-based constraints.
|
||||||
|
*
|
||||||
* @author Emmanuel Bernard
|
* @author Emmanuel Bernard
|
||||||
* @author Hardy Ferentschik
|
* @author Hardy Ferentschik
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -69,87 +80,102 @@ class TypeSafeActivator {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static void validateSuppliedFactory(Object object) {
|
public static void validateSuppliedFactory(Object object) {
|
||||||
if ( !(object instanceof ValidatorFactory) ) {
|
if ( !(object instanceof ValidatorFactory) ) {
|
||||||
throw new IntegrationException(
|
throw new IntegrationException( "Given object was not an instance of " + ValidatorFactory.class.getName()
|
||||||
"Given object was not an instance of " + ValidatorFactory.class.getName()
|
+ " [" + object.getClass().getName() + "]" );
|
||||||
+ "[" + object.getClass().getName() + "]"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static void activate(ActivationContext activationContext) {
|
public static void activate(ActivationContext context) {
|
||||||
final ValidatorFactory factory;
|
final ValidatorFactory factory;
|
||||||
try {
|
try {
|
||||||
factory = getValidatorFactory( activationContext );
|
factory = getValidatorFactory( context );
|
||||||
}
|
}
|
||||||
catch (IntegrationException e) {
|
catch (IntegrationException e) {
|
||||||
if ( activationContext.getValidationModes().contains( ValidationMode.CALLBACK ) ) {
|
final Set<ValidationMode> validationModes = context.getValidationModes();
|
||||||
|
if ( validationModes.contains( ValidationMode.CALLBACK ) ) {
|
||||||
throw new IntegrationException( "Bean Validation provider was not available, but 'callback' validation was requested", e );
|
throw new IntegrationException( "Bean Validation provider was not available, but 'callback' validation was requested", e );
|
||||||
}
|
}
|
||||||
if ( activationContext.getValidationModes().contains( ValidationMode.DDL ) ) {
|
else if ( validationModes.contains( ValidationMode.DDL ) ) {
|
||||||
throw new IntegrationException( "Bean Validation provider was not available, but 'ddl' validation was requested", e );
|
throw new IntegrationException( "Bean Validation provider was not available, but 'ddl' validation was requested", e );
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
LOG.debug( "Unable to acquire Bean Validation ValidatorFactory, skipping activation" );
|
LOG.debug( "Unable to acquire Bean Validation ValidatorFactory, skipping activation" );
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyRelationalConstraints( factory, activationContext );
|
applyRelationalConstraints( factory, context );
|
||||||
|
applyCallbackListeners( factory, context );
|
||||||
applyCallbackListeners( factory, activationContext );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyCallbackListeners(ValidatorFactory validatorFactory, ActivationContext activationContext) {
|
public static void applyCallbackListeners(ValidatorFactory validatorFactory, ActivationContext context) {
|
||||||
final Set<ValidationMode> modes = activationContext.getValidationModes();
|
if ( isValidationEnabled( context ) ) {
|
||||||
if ( !modes.contains( ValidationMode.CALLBACK ) && !modes.contains( ValidationMode.AUTO ) ) {
|
disableNullabilityChecking( context );
|
||||||
return;
|
setupListener( validatorFactory, context.getServiceRegistry() );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final SessionFactoryServiceRegistry serviceRegistry = activationContext.getServiceRegistry();
|
private static boolean isValidationEnabled(ActivationContext context) {
|
||||||
final ConfigurationService cfgService = serviceRegistry.requireService( ConfigurationService.class );
|
final Set<ValidationMode> modes = context.getValidationModes();
|
||||||
|
return modes.contains( ValidationMode.CALLBACK )
|
||||||
|
|| modes.contains( ValidationMode.AUTO );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate not-null tracking at the core level when Bean Validation
|
||||||
|
* is present, unless the user explicitly asks for it.
|
||||||
|
*/
|
||||||
|
private static void disableNullabilityChecking(ActivationContext context) {
|
||||||
|
if ( isCheckNullabilityExplicit( context ) ) {
|
||||||
|
// no explicit setting, so disable null checking
|
||||||
|
context.getSessionFactory().getSessionFactoryOptions().setCheckNullability( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isCheckNullabilityExplicit(ActivationContext context) {
|
||||||
|
return context.getServiceRegistry().requireService( ConfigurationService.class )
|
||||||
|
.getSettings().get( CHECK_NULLABILITY ) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupListener(ValidatorFactory validatorFactory, SessionFactoryServiceRegistry serviceRegistry) {
|
||||||
final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
|
final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
|
||||||
final EventListenerRegistry listenerRegistry = serviceRegistry.requireService( EventListenerRegistry.class );
|
final ConfigurationService cfgService = serviceRegistry.requireService( ConfigurationService.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( CHECK_NULLABILITY ) == null ) {
|
|
||||||
activationContext.getSessionFactory().getSessionFactoryOptions().setCheckNullability( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
final BeanValidationEventListener listener =
|
final BeanValidationEventListener listener =
|
||||||
new BeanValidationEventListener( validatorFactory, cfgService.getSettings(), classLoaderService );
|
new BeanValidationEventListener( validatorFactory, cfgService.getSettings(), classLoaderService );
|
||||||
|
final EventListenerRegistry listenerRegistry = serviceRegistry.requireService( EventListenerRegistry.class );
|
||||||
listenerRegistry.addDuplicationStrategy( DuplicationStrategyImpl.INSTANCE );
|
listenerRegistry.addDuplicationStrategy( DuplicationStrategyImpl.INSTANCE );
|
||||||
|
|
||||||
listenerRegistry.appendListeners( EventType.PRE_INSERT, listener );
|
listenerRegistry.appendListeners( EventType.PRE_INSERT, listener );
|
||||||
listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
|
listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
|
||||||
listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
|
listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
|
||||||
listenerRegistry.appendListeners( EventType.PRE_UPSERT, listener );
|
listenerRegistry.appendListeners( EventType.PRE_UPSERT, listener );
|
||||||
|
|
||||||
listener.initialize( cfgService.getSettings(), classLoaderService );
|
listener.initialize( cfgService.getSettings(), classLoaderService );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyRelationalConstraints(ValidatorFactory factory, ActivationContext activationContext) {
|
private static boolean isConstraintBasedValidationEnabled(ActivationContext context) {
|
||||||
final SessionFactoryServiceRegistry serviceRegistry = activationContext.getServiceRegistry();
|
if ( context.getServiceRegistry().requireService( ConfigurationService.class )
|
||||||
final ConfigurationService cfgService = serviceRegistry.requireService( ConfigurationService.class );
|
.getSetting( APPLY_CONSTRAINTS, StandardConverters.BOOLEAN, true ) ) {
|
||||||
if ( !cfgService.getSetting( BeanValidationIntegrator.APPLY_CONSTRAINTS, StandardConverters.BOOLEAN, true ) ) {
|
final Set<ValidationMode> modes = context.getValidationModes();
|
||||||
|
return modes.contains( ValidationMode.DDL )
|
||||||
|
|| modes.contains( ValidationMode.AUTO );
|
||||||
|
}
|
||||||
|
else {
|
||||||
LOG.debug( "Skipping application of relational constraints from legacy Hibernate Validator" );
|
LOG.debug( "Skipping application of relational constraints from legacy Hibernate Validator" );
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Set<ValidationMode> modes = activationContext.getValidationModes();
|
private static void applyRelationalConstraints(ValidatorFactory factory, ActivationContext context) {
|
||||||
if ( !modes.contains( ValidationMode.DDL ) && !modes.contains( ValidationMode.AUTO ) ) {
|
if ( isConstraintBasedValidationEnabled( context ) ) {
|
||||||
return;
|
final SessionFactoryServiceRegistry serviceRegistry = context.getServiceRegistry();
|
||||||
|
applyRelationalConstraints(
|
||||||
|
factory,
|
||||||
|
context.getMetadata().getEntityBindings(),
|
||||||
|
serviceRegistry.requireService( ConfigurationService.class ).getSettings(),
|
||||||
|
serviceRegistry.requireService( JdbcServices.class ).getDialect(),
|
||||||
|
new ClassLoaderAccessImpl( null, serviceRegistry.getService( ClassLoaderService.class ) )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyRelationalConstraints(
|
|
||||||
factory,
|
|
||||||
activationContext.getMetadata().getEntityBindings(),
|
|
||||||
cfgService.getSettings(),
|
|
||||||
serviceRegistry.requireService( JdbcServices.class ).getDialect(),
|
|
||||||
new ClassLoaderAccessImpl( null,
|
|
||||||
serviceRegistry.getService( ClassLoaderService.class ) )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyRelationalConstraints(
|
public static void applyRelationalConstraints(
|
||||||
|
@ -160,27 +186,27 @@ class TypeSafeActivator {
|
||||||
ClassLoaderAccess classLoaderAccess) {
|
ClassLoaderAccess classLoaderAccess) {
|
||||||
final Class<?>[] groupsArray =
|
final Class<?>[] groupsArray =
|
||||||
buildGroupsForOperation( GroupsPerOperation.Operation.DDL, settings, classLoaderAccess );
|
buildGroupsForOperation( GroupsPerOperation.Operation.DDL, settings, classLoaderAccess );
|
||||||
final Set<Class<?>> groups = new HashSet<>( Arrays.asList( groupsArray ) );
|
final Set<Class<?>> groups = new HashSet<>( asList( groupsArray ) );
|
||||||
|
|
||||||
for ( PersistentClass persistentClass : persistentClasses ) {
|
for ( PersistentClass persistentClass : persistentClasses ) {
|
||||||
final String className = persistentClass.getClassName();
|
final String className = persistentClass.getClassName();
|
||||||
if ( className == null || className.isEmpty() ) {
|
if ( isNotEmpty( className ) ) {
|
||||||
continue;
|
final Class<?> clazz = entityClass( classLoaderAccess, className );
|
||||||
}
|
try {
|
||||||
Class<?> clazz;
|
applyDDL( "", persistentClass, clazz, factory, groups, true, dialect );
|
||||||
try {
|
}
|
||||||
clazz = classLoaderAccess.classForName( className );
|
catch (Exception e) {
|
||||||
}
|
LOG.unableToApplyConstraints( className, e );
|
||||||
catch ( ClassLoadingException e ) {
|
}
|
||||||
throw new AssertionFailure( "Entity class not found", e );
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
private static Class<?> entityClass(ClassLoaderAccess classLoaderAccess, String className) {
|
||||||
applyDDL( "", persistentClass, clazz, factory, groups, true, dialect );
|
try {
|
||||||
}
|
return classLoaderAccess.classForName( className );
|
||||||
catch (Exception e) {
|
}
|
||||||
LOG.unableToApplyConstraints( className, e );
|
catch (ClassLoadingException e) {
|
||||||
}
|
throw new AssertionFailure( "Entity class not found", e );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +219,7 @@ class TypeSafeActivator {
|
||||||
boolean activateNotNull,
|
boolean activateNotNull,
|
||||||
Dialect dialect) {
|
Dialect dialect) {
|
||||||
final BeanDescriptor descriptor = factory.getValidator().getConstraintsForClass( clazz );
|
final BeanDescriptor descriptor = factory.getValidator().getConstraintsForClass( clazz );
|
||||||
//no bean level constraints can be applied, go to the properties
|
//cno bean level constraints can be applied, go to the properties
|
||||||
|
|
||||||
for ( PropertyDescriptor propertyDesc : descriptor.getConstrainedProperties() ) {
|
for ( PropertyDescriptor propertyDesc : descriptor.getConstrainedProperties() ) {
|
||||||
final Property property =
|
final Property property =
|
||||||
findPropertyByName( persistentClass, prefix + propertyDesc.getPropertyName() );
|
findPropertyByName( persistentClass, prefix + propertyDesc.getPropertyName() );
|
||||||
|
@ -209,19 +234,16 @@ class TypeSafeActivator {
|
||||||
);
|
);
|
||||||
if ( property.isComposite() && propertyDesc.isCascaded() ) {
|
if ( property.isComposite() && propertyDesc.isCascaded() ) {
|
||||||
final Component component = (Component) property.getValue();
|
final Component component = (Component) property.getValue();
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
final boolean canSetNotNullOnColumns = activateNotNull && hasNotNull;
|
|
||||||
applyDDL(
|
applyDDL(
|
||||||
prefix + propertyDesc.getPropertyName() + ".",
|
prefix + propertyDesc.getPropertyName() + ".",
|
||||||
persistentClass,
|
persistentClass,
|
||||||
component.getComponentClass(),
|
component.getComponentClass(),
|
||||||
factory,
|
factory,
|
||||||
groups,
|
groups,
|
||||||
canSetNotNullOnColumns,
|
// 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
|
||||||
|
activateNotNull && hasNotNull,
|
||||||
dialect
|
dialect
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -238,34 +260,33 @@ class TypeSafeActivator {
|
||||||
Dialect dialect) {
|
Dialect dialect) {
|
||||||
boolean hasNotNull = false;
|
boolean hasNotNull = false;
|
||||||
for ( ConstraintDescriptor<?> descriptor : constraintDescriptors ) {
|
for ( ConstraintDescriptor<?> descriptor : constraintDescriptors ) {
|
||||||
if ( groups != null && Collections.disjoint( descriptor.getGroups(), groups ) ) {
|
if ( groups == null || !disjoint( descriptor.getGroups(), groups ) ) {
|
||||||
continue;
|
if ( canApplyNotNull ) {
|
||||||
|
hasNotNull = hasNotNull || applyNotNull( property, descriptor );
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply bean validation specific constraints
|
||||||
|
applyDigits( property, descriptor );
|
||||||
|
applySize( property, descriptor, propertyDesc );
|
||||||
|
applyMin( property, descriptor, dialect );
|
||||||
|
applyMax( property, descriptor, dialect );
|
||||||
|
|
||||||
|
// Apply Hibernate Validator specific constraints - we cannot import any HV specific classes though!
|
||||||
|
// No need to check explicitly for @Range. @Range is a composed constraint using @Min and @Max which
|
||||||
|
// will be taken care later.
|
||||||
|
applyLength( property, descriptor, propertyDesc );
|
||||||
|
|
||||||
|
// pass an empty set as composing constraints inherit the main constraint and thus are matching already
|
||||||
|
final boolean hasNotNullFromComposingConstraints = applyConstraints(
|
||||||
|
descriptor.getComposingConstraints(),
|
||||||
|
property, propertyDesc, null,
|
||||||
|
canApplyNotNull,
|
||||||
|
dialect
|
||||||
|
);
|
||||||
|
|
||||||
|
hasNotNull = hasNotNull || hasNotNullFromComposingConstraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( canApplyNotNull ) {
|
|
||||||
hasNotNull = hasNotNull || applyNotNull( property, descriptor );
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply bean validation specific constraints
|
|
||||||
applyDigits( property, descriptor );
|
|
||||||
applySize( property, descriptor, propertyDesc );
|
|
||||||
applyMin( property, descriptor, dialect );
|
|
||||||
applyMax( property, descriptor, dialect );
|
|
||||||
|
|
||||||
// apply hibernate validator specific constraints - we cannot import any HV specific classes though!
|
|
||||||
// no need to check explicitly for @Range. @Range is a composed constraint using @Min and @Max which
|
|
||||||
// will be taken care later
|
|
||||||
applyLength( property, descriptor, propertyDesc );
|
|
||||||
|
|
||||||
// pass an empty set as composing constraints inherit the main constraint and thus are matching already
|
|
||||||
boolean hasNotNullFromComposingConstraints = applyConstraints(
|
|
||||||
descriptor.getComposingConstraints(),
|
|
||||||
property, propertyDesc, null,
|
|
||||||
canApplyNotNull,
|
|
||||||
dialect
|
|
||||||
);
|
|
||||||
|
|
||||||
hasNotNull = hasNotNull || hasNotNullFromComposingConstraints;
|
|
||||||
}
|
}
|
||||||
return hasNotNull;
|
return hasNotNull;
|
||||||
}
|
}
|
||||||
|
@ -275,11 +296,9 @@ class TypeSafeActivator {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final ConstraintDescriptor<Min> minConstraint = (ConstraintDescriptor<Min>) descriptor;
|
final ConstraintDescriptor<Min> minConstraint = (ConstraintDescriptor<Min>) descriptor;
|
||||||
final long min = minConstraint.getAnnotation().value();
|
final long min = minConstraint.getAnnotation().value();
|
||||||
|
|
||||||
for ( Selectable selectable : property.getSelectables() ) {
|
for ( Selectable selectable : property.getSelectables() ) {
|
||||||
if ( selectable instanceof Column column ) {
|
if ( selectable instanceof Column column ) {
|
||||||
final String checkConstraint = column.getQuotedName( dialect ) + ">=" + min;
|
applySQLCheck( column, column.getQuotedName( dialect ) + ">=" + min );
|
||||||
applySQLCheck( column, checkConstraint );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,25 +309,23 @@ class TypeSafeActivator {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final ConstraintDescriptor<Max> maxConstraint = (ConstraintDescriptor<Max>) descriptor;
|
final ConstraintDescriptor<Max> maxConstraint = (ConstraintDescriptor<Max>) descriptor;
|
||||||
final long max = maxConstraint.getAnnotation().value();
|
final long max = maxConstraint.getAnnotation().value();
|
||||||
|
|
||||||
for ( Selectable selectable : property.getSelectables() ) {
|
for ( Selectable selectable : property.getSelectables() ) {
|
||||||
if ( selectable instanceof Column column ) {
|
if ( selectable instanceof Column column ) {
|
||||||
final String checkConstraint = column.getQuotedName( dialect ) + "<=" + max;
|
applySQLCheck( column, column.getQuotedName( dialect ) + "<=" + max );
|
||||||
applySQLCheck( column, checkConstraint );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applySQLCheck(Column col, String checkConstraint) {
|
private static void applySQLCheck(Column column, String checkConstraint) {
|
||||||
// need to check whether the new check is already part of the existing check,
|
// need to check whether the new check is already part of the existing check,
|
||||||
// because applyDDL can be called multiple times
|
// because applyDDL can be called multiple times
|
||||||
for ( CheckConstraint constraint : col.getCheckConstraints() ) {
|
for ( CheckConstraint constraint : column.getCheckConstraints() ) {
|
||||||
if ( constraint.getConstraint().equalsIgnoreCase( checkConstraint ) ) {
|
if ( constraint.getConstraint().equalsIgnoreCase( checkConstraint ) ) {
|
||||||
return; //EARLY EXIT
|
return; //EARLY EXIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
col.addCheckConstraint( new CheckConstraint( checkConstraint ) );
|
column.addCheckConstraint( new CheckConstraint( checkConstraint ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean applyNotNull(Property property, ConstraintDescriptor<?> descriptor) {
|
private static boolean applyNotNull(Property property, ConstraintDescriptor<?> descriptor) {
|
||||||
|
@ -320,11 +337,11 @@ class TypeSafeActivator {
|
||||||
|| NotBlank.class.equals(annotationType)) {
|
|| NotBlank.class.equals(annotationType)) {
|
||||||
// single table inheritance should not be forced to null due to shared state
|
// single table inheritance should not be forced to null due to shared state
|
||||||
if ( !( property.getPersistentClass() instanceof SingleTableSubclass ) ) {
|
if ( !( property.getPersistentClass() instanceof SingleTableSubclass ) ) {
|
||||||
//composite should not add not-null on all columns
|
// composite should not add not-null on all columns
|
||||||
if ( !property.isComposite() ) {
|
if ( !property.isComposite() ) {
|
||||||
for ( Selectable selectable : property.getSelectables() ) {
|
for ( Selectable selectable : property.getSelectables() ) {
|
||||||
if ( selectable instanceof Column ) {
|
if ( selectable instanceof Column column ) {
|
||||||
((Column) selectable).setNullable( false );
|
column.setNullable( false );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG.debugf(
|
LOG.debugf(
|
||||||
|
@ -345,10 +362,9 @@ class TypeSafeActivator {
|
||||||
private static void applyDigits(Property property, ConstraintDescriptor<?> descriptor) {
|
private static void applyDigits(Property property, ConstraintDescriptor<?> descriptor) {
|
||||||
if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) {
|
if ( Digits.class.equals( descriptor.getAnnotation().annotationType() ) ) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ConstraintDescriptor<Digits> digitsConstraint = (ConstraintDescriptor<Digits>) descriptor;
|
final ConstraintDescriptor<Digits> digitsConstraint = (ConstraintDescriptor<Digits>) descriptor;
|
||||||
int integerDigits = digitsConstraint.getAnnotation().integer();
|
final int integerDigits = digitsConstraint.getAnnotation().integer();
|
||||||
int fractionalDigits = digitsConstraint.getAnnotation().fraction();
|
final int fractionalDigits = digitsConstraint.getAnnotation().fraction();
|
||||||
|
|
||||||
for ( Selectable selectable : property.getSelectables() ) {
|
for ( Selectable selectable : property.getSelectables() ) {
|
||||||
if ( selectable instanceof Column column ) {
|
if ( selectable instanceof Column column ) {
|
||||||
column.setPrecision( integerDigits + fractionalDigits );
|
column.setPrecision( integerDigits + fractionalDigits );
|
||||||
|
@ -363,9 +379,8 @@ class TypeSafeActivator {
|
||||||
if ( Size.class.equals( descriptor.getAnnotation().annotationType() )
|
if ( Size.class.equals( descriptor.getAnnotation().annotationType() )
|
||||||
&& String.class.equals( propertyDescriptor.getElementClass() ) ) {
|
&& String.class.equals( propertyDescriptor.getElementClass() ) ) {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
ConstraintDescriptor<Size> sizeConstraint = (ConstraintDescriptor<Size>) descriptor;
|
final ConstraintDescriptor<Size> sizeConstraint = (ConstraintDescriptor<Size>) descriptor;
|
||||||
int max = sizeConstraint.getAnnotation().max();
|
final int max = sizeConstraint.getAnnotation().max();
|
||||||
|
|
||||||
for ( Column col : property.getColumns() ) {
|
for ( Column col : property.getColumns() ) {
|
||||||
if ( max < Integer.MAX_VALUE ) {
|
if ( max < Integer.MAX_VALUE ) {
|
||||||
col.setLength( max );
|
col.setLength( max );
|
||||||
|
@ -375,12 +390,9 @@ class TypeSafeActivator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyLength(Property property, ConstraintDescriptor<?> descriptor, PropertyDescriptor propertyDescriptor) {
|
private static void applyLength(Property property, ConstraintDescriptor<?> descriptor, PropertyDescriptor propertyDescriptor) {
|
||||||
if ( "org.hibernate.validator.constraints.Length".equals(
|
if ( isValidatorLengthAnnotation( descriptor )
|
||||||
descriptor.getAnnotation().annotationType().getName()
|
|
||||||
)
|
|
||||||
&& String.class.equals( propertyDescriptor.getElementClass() ) ) {
|
&& String.class.equals( propertyDescriptor.getElementClass() ) ) {
|
||||||
int max = (Integer) descriptor.getAttributes().get( "max" );
|
final int max = (Integer) descriptor.getAttributes().get( "max" );
|
||||||
|
|
||||||
for ( Selectable selectable : property.getSelectables() ) {
|
for ( Selectable selectable : property.getSelectables() ) {
|
||||||
if ( selectable instanceof Column column ) {
|
if ( selectable instanceof Column column ) {
|
||||||
if ( max < Integer.MAX_VALUE ) {
|
if ( max < Integer.MAX_VALUE ) {
|
||||||
|
@ -391,18 +403,21 @@ class TypeSafeActivator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isValidatorLengthAnnotation(ConstraintDescriptor<?> descriptor) {
|
||||||
|
return "org.hibernate.validator.constraints.Length"
|
||||||
|
.equals( descriptor.getAnnotation().annotationType().getName() );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locate the property by path in a recursive way, including IdentifierProperty in the loop if propertyName is
|
* Locate the property by path in a recursive way, including IdentifierProperty in the loop if propertyName is
|
||||||
* {@code null}. If propertyName is {@code null} or empty, the IdentifierProperty is returned
|
* {@code null}. If propertyName is {@code null} or empty, the IdentifierProperty is returned
|
||||||
*/
|
*/
|
||||||
private static Property findPropertyByName(PersistentClass associatedClass, String propertyName) {
|
private static Property findPropertyByName(PersistentClass associatedClass, String propertyName) {
|
||||||
Property property = null;
|
Property property = null;
|
||||||
Property idProperty = associatedClass.getIdentifierProperty();
|
final Property idProperty = associatedClass.getIdentifierProperty();
|
||||||
String idName = idProperty != null ? idProperty.getName() : null;
|
final String idName = idProperty != null ? idProperty.getName() : null;
|
||||||
try {
|
try {
|
||||||
if ( propertyName == null
|
if ( isEmpty( propertyName ) || propertyName.equals( idName ) ) {
|
||||||
|| propertyName.isEmpty()
|
|
||||||
|| propertyName.equals( idName ) ) {
|
|
||||||
//default to id
|
//default to id
|
||||||
property = idProperty;
|
property = idProperty;
|
||||||
}
|
}
|
||||||
|
@ -411,9 +426,9 @@ class TypeSafeActivator {
|
||||||
property = idProperty;
|
property = idProperty;
|
||||||
propertyName = propertyName.substring( idName.length() + 1 );
|
propertyName = propertyName.substring( idName.length() + 1 );
|
||||||
}
|
}
|
||||||
StringTokenizer st = new StringTokenizer( propertyName, ".", false );
|
final StringTokenizer tokens = new StringTokenizer( propertyName, ".", false );
|
||||||
while ( st.hasMoreElements() ) {
|
while ( tokens.hasMoreTokens() ) {
|
||||||
String element = (String) st.nextElement();
|
final String element = tokens.nextToken();
|
||||||
if ( property == null ) {
|
if ( property == null ) {
|
||||||
property = associatedClass.getProperty( element );
|
property = associatedClass.getProperty( element );
|
||||||
}
|
}
|
||||||
|
@ -421,7 +436,9 @@ class TypeSafeActivator {
|
||||||
if ( !property.isComposite() ) {
|
if ( !property.isComposite() ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
property = ( (Component) property.getValue() ).getProperty( element );
|
else {
|
||||||
|
property = ( (Component) property.getValue() ).getProperty( element );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,17 +449,21 @@ class TypeSafeActivator {
|
||||||
if ( associatedClass.getIdentifierMapper() == null ) {
|
if ( associatedClass.getIdentifierMapper() == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
StringTokenizer st = new StringTokenizer( propertyName, ".", false );
|
else {
|
||||||
while ( st.hasMoreElements() ) {
|
final StringTokenizer tokens = new StringTokenizer( propertyName, ".", false );
|
||||||
String element = (String) st.nextElement();
|
while ( tokens.hasMoreTokens() ) {
|
||||||
if ( property == null ) {
|
final String element = tokens.nextToken();
|
||||||
property = associatedClass.getIdentifierMapper().getProperty( element );
|
if ( property == null ) {
|
||||||
}
|
property = associatedClass.getIdentifierMapper().getProperty( element );
|
||||||
else {
|
}
|
||||||
if ( !property.isComposite() ) {
|
else {
|
||||||
return null;
|
if ( !property.isComposite() ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
property = ( (Component) property.getValue() ).getProperty( element );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
property = ( (Component) property.getValue() ).getProperty( element );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -453,7 +474,7 @@ class TypeSafeActivator {
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ValidatorFactory getValidatorFactory(ActivationContext activationContext) {
|
private static ValidatorFactory getValidatorFactory(ActivationContext context) {
|
||||||
// IMPL NOTE : We can either be provided a ValidatorFactory or make one. We can be provided
|
// IMPL NOTE : We can either be provided a ValidatorFactory or make one. We can be provided
|
||||||
// a ValidatorFactory in 2 different ways. So here we "get" a ValidatorFactory in the following order:
|
// a ValidatorFactory in 2 different ways. So here we "get" a ValidatorFactory in the following order:
|
||||||
// 1) Look into SessionFactoryOptions.getValidatorFactoryReference()
|
// 1) Look into SessionFactoryOptions.getValidatorFactoryReference()
|
||||||
|
@ -461,15 +482,17 @@ class TypeSafeActivator {
|
||||||
// 3) build a new ValidatorFactory
|
// 3) build a new ValidatorFactory
|
||||||
|
|
||||||
// 1 - look in SessionFactoryOptions.getValidatorFactoryReference()
|
// 1 - look in SessionFactoryOptions.getValidatorFactoryReference()
|
||||||
ValidatorFactory factory = resolveProvidedFactory( activationContext.getSessionFactory().getSessionFactoryOptions() );
|
final ValidatorFactory providedFactory =
|
||||||
if ( factory != null ) {
|
resolveProvidedFactory( context.getSessionFactory().getSessionFactoryOptions() );
|
||||||
return factory;
|
if ( providedFactory != null ) {
|
||||||
|
return providedFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 - look in ConfigurationService
|
// 2 - look in ConfigurationService
|
||||||
factory = resolveProvidedFactory( activationContext.getServiceRegistry().requireService( ConfigurationService.class ) );
|
final ValidatorFactory configuredFactory =
|
||||||
if ( factory != null ) {
|
resolveProvidedFactory( context.getServiceRegistry().requireService( ConfigurationService.class ) );
|
||||||
return factory;
|
if ( configuredFactory != null ) {
|
||||||
|
return configuredFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 - build our own
|
// 3 - build our own
|
||||||
|
@ -486,10 +509,10 @@ class TypeSafeActivator {
|
||||||
if ( validatorFactoryReference == null ) {
|
if ( validatorFactoryReference == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
else if ( validatorFactoryReference instanceof ValidatorFactory result ) {
|
||||||
return (ValidatorFactory) validatorFactoryReference;
|
return result;
|
||||||
}
|
}
|
||||||
catch ( ClassCastException e ) {
|
else {
|
||||||
throw new IntegrationException(
|
throw new IntegrationException(
|
||||||
String.format(
|
String.format(
|
||||||
Locale.ENGLISH,
|
Locale.ENGLISH,
|
||||||
|
@ -505,42 +528,29 @@ class TypeSafeActivator {
|
||||||
private static ValidatorFactory resolveProvidedFactory(ConfigurationService cfgService) {
|
private static ValidatorFactory resolveProvidedFactory(ConfigurationService cfgService) {
|
||||||
return cfgService.getSetting(
|
return cfgService.getSetting(
|
||||||
JPA_VALIDATION_FACTORY,
|
JPA_VALIDATION_FACTORY,
|
||||||
value -> {
|
value -> validatorFactory( value, JPA_VALIDATION_FACTORY ),
|
||||||
try {
|
|
||||||
return (ValidatorFactory) value;
|
|
||||||
}
|
|
||||||
catch ( ClassCastException e ) {
|
|
||||||
throw new IntegrationException(
|
|
||||||
String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
"ValidatorFactory reference (provided via `%s` setting) was not castable to %s : %s",
|
|
||||||
JPA_VALIDATION_FACTORY,
|
|
||||||
ValidatorFactory.class.getName(),
|
|
||||||
value.getClass().getName()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cfgService.getSetting(
|
cfgService.getSetting(
|
||||||
JAKARTA_VALIDATION_FACTORY,
|
JAKARTA_VALIDATION_FACTORY,
|
||||||
value -> {
|
value -> validatorFactory( value, JAKARTA_VALIDATION_FACTORY ),
|
||||||
try {
|
|
||||||
return (ValidatorFactory) value;
|
|
||||||
}
|
|
||||||
catch ( ClassCastException e ) {
|
|
||||||
throw new IntegrationException(
|
|
||||||
String.format(
|
|
||||||
Locale.ENGLISH,
|
|
||||||
"ValidatorFactory reference (provided via `%s` setting) was not castable to %s : %s",
|
|
||||||
JAKARTA_VALIDATION_FACTORY,
|
|
||||||
ValidatorFactory.class.getName(),
|
|
||||||
value.getClass().getName()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ValidatorFactory validatorFactory(Object value, String setting) {
|
||||||
|
if ( value instanceof ValidatorFactory validatorFactory ) {
|
||||||
|
return validatorFactory;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IntegrationException(
|
||||||
|
String.format(
|
||||||
|
Locale.ENGLISH,
|
||||||
|
"ValidatorFactory reference (provided via '%s' setting) was not an instance of '%s': %s",
|
||||||
|
setting,
|
||||||
|
ValidatorFactory.class.getName(),
|
||||||
|
value.getClass().getName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue