Skip to content

Commit 7ae0202

Browse files
committed
Do not recreate the validator on each BeanValidationEventListener#validate call
1 parent 0bc1dff commit 7ae0202

File tree

3 files changed

+24
-48
lines changed

3 files changed

+24
-48
lines changed

hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/BeanValidationEventListener.java

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.util.HashSet;
99
import java.util.Map;
1010
import java.util.Set;
11-
import java.util.concurrent.ConcurrentHashMap;
1211

1312
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
1413
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
@@ -29,11 +28,9 @@
2928

3029
import jakarta.validation.ConstraintViolation;
3130
import jakarta.validation.ConstraintViolationException;
32-
import jakarta.validation.TraversableResolver;
3331
import jakarta.validation.Validator;
3432
import jakarta.validation.ValidatorFactory;
3533

36-
import static jakarta.validation.Validation.buildDefaultValidatorFactory;
3734
import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize;
3835

3936
/**
@@ -52,32 +49,17 @@ public class BeanValidationEventListener
5249
BeanValidationEventListener.class.getName()
5350
);
5451

55-
private ValidatorFactory factory;
56-
private final ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister = new ConcurrentHashMap<>();
52+
private HibernateTraversableResolver traversableResolver;
53+
private Validator validator;
5754
private GroupsPerOperation groupsPerOperation;
58-
boolean initialized;
59-
60-
/**
61-
* Constructor used in an environment where validator factory is injected (JPA2).
62-
*
63-
* @param factory The {@code ValidatorFactory} to use to create {@code Validator} instance(s)
64-
* @param settings Configured properties
65-
*/
55+
6656
public BeanValidationEventListener(
6757
ValidatorFactory factory, Map<String,Object> settings, ClassLoaderService classLoaderService) {
68-
init( factory, settings, classLoaderService );
69-
}
70-
71-
public void initialize(Map<String,Object> settings, ClassLoaderService classLoaderService) {
72-
if ( !initialized ) {
73-
init( buildDefaultValidatorFactory(), settings, classLoaderService );
74-
}
75-
}
76-
77-
private void init(ValidatorFactory factory, Map<String,Object> settings, ClassLoaderService classLoaderService) {
78-
this.factory = factory;
58+
traversableResolver = new HibernateTraversableResolver();
59+
validator = factory.usingContext()
60+
.traversableResolver( traversableResolver )
61+
.getValidator();
7962
groupsPerOperation = GroupsPerOperation.from( settings, new ClassLoaderAccessImpl( classLoaderService ) );
80-
initialized = true;
8163
}
8264

8365
public boolean onPreInsert(PreInsertEvent event) {
@@ -129,10 +111,7 @@ private <T> void validate(
129111
if ( object == null || persister.getRepresentationStrategy().getMode() != RepresentationMode.POJO ) {
130112
return;
131113
}
132-
TraversableResolver tr = new HibernateTraversableResolver( persister, associationsPerEntityPersister, sessionFactory );
133-
Validator validator = factory.usingContext()
134-
.traversableResolver( tr )
135-
.getValidator();
114+
traversableResolver.addPersisterIfNecessary( persister, sessionFactory );
136115
final Class<?>[] groups = groupsPerOperation.get( operation );
137116
if ( groups.length > 0 ) {
138117
final Set<ConstraintViolation<T>> constraintViolations = validator.validate( object, groups );

hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/HibernateTraversableResolver.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,29 @@
3030
* @author Emmanuel Bernard
3131
*/
3232
public class HibernateTraversableResolver implements TraversableResolver {
33-
private Set<String> associations;
33+
private final ConcurrentHashMap<Class<?>, Set<String>> associationsPerEntityClass = new ConcurrentHashMap<>();
3434

35-
public HibernateTraversableResolver(
36-
EntityPersister persister,
37-
ConcurrentHashMap<EntityPersister, Set<String>> associationsPerEntityPersister,
38-
SessionFactoryImplementor factory) {
39-
associations = associationsPerEntityPersister.get( persister );
40-
if ( associations == null ) {
41-
associations = new HashSet<>();
42-
addAssociationsToTheSetForAllProperties( persister.getPropertyNames(), persister.getPropertyTypes(), "", factory );
43-
associationsPerEntityPersister.put( persister, associations );
35+
public void addPersisterIfNecessary(EntityPersister persister, SessionFactoryImplementor factory) {
36+
Class<?> javaTypeClass = persister.getEntityMappingType().getMappedJavaType().getJavaTypeClass();
37+
if ( !associationsPerEntityClass.containsKey( javaTypeClass ) ) {
38+
Set<String> associations = new HashSet<>();
39+
addAssociationsToTheSetForAllProperties( persister.getPropertyNames(), persister.getPropertyTypes(), "", factory, associations );
40+
associationsPerEntityClass.put( javaTypeClass, associations );
4441
}
4542
}
4643

47-
private void addAssociationsToTheSetForAllProperties(
48-
String[] names, Type[] types, String prefix, SessionFactoryImplementor factory) {
44+
private static void addAssociationsToTheSetForAllProperties(
45+
String[] names, Type[] types, String prefix, SessionFactoryImplementor factory, Set<String> associations) {
4946
final int length = names.length;
5047
for( int index = 0 ; index < length; index++ ) {
51-
addAssociationsToTheSetForOneProperty( names[index], types[index], prefix, factory );
48+
addAssociationsToTheSetForOneProperty( names[index], types[index], prefix, factory, associations );
5249
}
5350
}
5451

55-
private void addAssociationsToTheSetForOneProperty(
56-
String name, Type type, String prefix, SessionFactoryImplementor factory) {
52+
private static void addAssociationsToTheSetForOneProperty(
53+
String name, Type type, String prefix, SessionFactoryImplementor factory, Set<String> associations) {
5754
if ( type instanceof CollectionType collectionType ) {
58-
addAssociationsToTheSetForOneProperty( name, collectionType.getElementType( factory ), prefix, factory );
55+
addAssociationsToTheSetForOneProperty( name, collectionType.getElementType( factory ), prefix, factory, associations );
5956
}
6057
//ToOne association
6158
else if ( type instanceof EntityType || type instanceof AnyType ) {
@@ -66,7 +63,8 @@ else if ( type instanceof ComponentType componentType ) {
6663
componentType.getPropertyNames(),
6764
componentType.getSubtypes(),
6865
( prefix.isEmpty() ? name : prefix + name ) + '.',
69-
factory
66+
factory,
67+
associations
7068
);
7169
}
7270
}
@@ -102,6 +100,6 @@ public boolean isCascadable(Object traversableObject,
102100
Class<?> rootBeanType,
103101
Path pathToTraversableObject,
104102
ElementType elementType) {
105-
return !associations.contains( getStringBasedPath( traversableProperty, pathToTraversableObject ) );
103+
return !associationsPerEntityClass.getOrDefault( rootBeanType, Set.of() ).contains( getStringBasedPath( traversableProperty, pathToTraversableObject ) );
106104
}
107105
}

hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ private static void setupListener(ValidatorFactory validatorFactory, SessionFact
152152
listenerRegistry.appendListeners( EventType.PRE_UPDATE, listener );
153153
listenerRegistry.appendListeners( EventType.PRE_DELETE, listener );
154154
listenerRegistry.appendListeners( EventType.PRE_UPSERT, listener );
155-
listener.initialize( cfgService.getSettings(), classLoaderService );
156155
}
157156

158157
private static boolean isConstraintBasedValidationEnabled(ActivationContext context) {

0 commit comments

Comments
 (0)