Skip to content

Commit 28a73f6

Browse files
committed
Make jpa-based traversable resolver optional and depend on ORM extensions
1 parent fd3b1f6 commit 28a73f6

File tree

6 files changed

+91
-6
lines changed

6 files changed

+91
-6
lines changed

extensions/hibernate-orm/deployment/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
<groupId>io.quarkus</groupId>
5454
<artifactId>quarkus-vertx-http-dev-ui-spi</artifactId>
5555
</dependency>
56+
<dependency>
57+
<groupId>io.quarkus</groupId>
58+
<artifactId>quarkus-hibernate-validator-spi</artifactId>
59+
</dependency>
5660
<dependency>
5761
<groupId>io.quarkus</groupId>
5862
<artifactId>quarkus-hibernate-orm-derby-deployment</artifactId>

extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
import io.quarkus.hibernate.orm.runtime.service.FlatClassLoaderService;
132132
import io.quarkus.hibernate.orm.runtime.tenant.DataSourceTenantConnectionResolver;
133133
import io.quarkus.hibernate.orm.runtime.tenant.TenantConnectionResolver;
134+
import io.quarkus.hibernate.validator.spi.BeanValidationTraversableResolverBuildItem;
134135
import io.quarkus.panache.hibernate.common.deployment.HibernateEnhancersRegisteredBuildItem;
135136
import io.quarkus.panache.hibernate.common.deployment.HibernateModelClassCandidatesForFieldAccessBuildItem;
136137
import io.quarkus.runtime.LaunchMode;
@@ -518,9 +519,11 @@ private boolean hasXmlMappings(List<PersistenceUnitDescriptorBuildItem> persiste
518519
public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder,
519520
Capabilities capabilities,
520521
JpaModelBuildItem jpaModel,
522+
HibernateOrmConfig hibernateOrmConfig,
521523
List<PersistenceUnitDescriptorBuildItem> persistenceUnitDescriptorBuildItems,
522524
List<HibernateOrmIntegrationStaticConfiguredBuildItem> integrationBuildItems,
523525
BuildProducer<BeanContainerListenerBuildItem> beanContainerListener,
526+
BuildProducer<BeanValidationTraversableResolverBuildItem> beanValidationTraversableResolver,
524527
LaunchModeBuildItem launchMode) throws Exception {
525528
validateHibernatePropertiesNotUsed();
526529

@@ -578,6 +581,10 @@ public void build(RecorderContext recorderContext, HibernateOrmRecorder recorder
578581
beanContainerListener
579582
.produce(new BeanContainerListenerBuildItem(
580583
recorder.initMetadata(finalStagePUDescriptors, scanner, integratorClasses)));
584+
if (capabilities.isPresent(Capability.HIBERNATE_VALIDATOR) && hibernateOrmConfig.enabled()) {
585+
beanValidationTraversableResolver
586+
.produce(new BeanValidationTraversableResolverBuildItem(recorder.attributeLoadedPredicate()));
587+
}
581588
}
582589

583590
private void validateHibernatePropertiesNotUsed() {

extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRecorder.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import java.util.Map;
77
import java.util.Optional;
88
import java.util.Set;
9+
import java.util.function.BiPredicate;
910
import java.util.function.Function;
1011
import java.util.function.Supplier;
1112

1213
import jakarta.inject.Inject;
1314
import jakarta.persistence.Cache;
1415
import jakarta.persistence.criteria.CriteriaBuilder;
1516
import jakarta.persistence.metamodel.Metamodel;
17+
import jakarta.persistence.spi.LoadState;
1618

1719
import org.hibernate.Session;
1820
import org.hibernate.SessionFactory;
@@ -250,4 +252,29 @@ public void run() {
250252
}
251253
}, "Hibernate post-boot validation thread for " + puName).start();
252254
}
255+
256+
public BiPredicate<Object, String> attributeLoadedPredicate() {
257+
return new IsAttributeLoadedPredicate();
258+
}
259+
260+
private static class IsAttributeLoadedPredicate implements BiPredicate<Object, String> {
261+
private final ProviderUtil providerUtil = new ProviderUtil();
262+
263+
@Override
264+
public boolean test(Object entity, String attributeName) {
265+
LoadState loadstate = providerUtil.isLoadedWithoutReference(entity, attributeName);
266+
if (loadstate == LoadState.LOADED) {
267+
return true;
268+
} else if (loadstate == LoadState.NOT_LOADED) {
269+
return false;
270+
}
271+
loadstate = providerUtil.isLoadedWithReference(entity, attributeName);
272+
if (loadstate == LoadState.LOADED) {
273+
return true;
274+
} else if (loadstate == LoadState.NOT_LOADED) {
275+
return false;
276+
}
277+
return true;
278+
}
279+
}
253280
}

extensions/hibernate-validator/deployment/src/main/java/io/quarkus/hibernate/validator/deployment/HibernateValidatorProcessor.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
import io.quarkus.hibernate.validator.runtime.locale.LocaleResolversWrapper;
121121
import io.quarkus.hibernate.validator.spi.AdditionalConstrainedClassBuildItem;
122122
import io.quarkus.hibernate.validator.spi.BeanValidationAnnotationsBuildItem;
123+
import io.quarkus.hibernate.validator.spi.BeanValidationTraversableResolverBuildItem;
123124
import io.quarkus.jaxrs.spi.deployment.AdditionalJaxRsResourceMethodAnnotationsBuildItem;
124125
import io.quarkus.resteasy.common.spi.ResteasyConfigBuildItem;
125126
import io.quarkus.resteasy.common.spi.ResteasyDotNames;
@@ -475,7 +476,7 @@ public void build(
475476
ShutdownContextBuildItem shutdownContext,
476477
List<ConfigClassBuildItem> configClasses,
477478
List<AdditionalJaxRsResourceMethodAnnotationsBuildItem> additionalJaxRsResourceMethodAnnotations,
478-
Capabilities capabilities,
479+
Optional<BeanValidationTraversableResolverBuildItem> beanValidationTraversableResolver,
479480
LocalesBuildTimeConfig localesBuildTimeConfig,
480481
HibernateValidatorBuildTimeConfig hibernateValidatorBuildTimeConfig) throws Exception {
481482

@@ -607,7 +608,8 @@ public void build(
607608
.createWith(recorder.hibernateValidatorFactory(classesToBeValidated, detectedBuiltinConstraints,
608609
valueExtractorClassProxies,
609610
hasXmlConfiguration(),
610-
capabilities.isPresent(Capability.HIBERNATE_ORM),
611+
beanValidationTraversableResolver
612+
.map(BeanValidationTraversableResolverBuildItem::getAttributeLoadedPredicate),
611613
localesBuildTimeConfig,
612614
hibernateValidatorBuildTimeConfig))
613615
.addQualifier().annotation(DotNames.NAMED).addValue("value", VALIDATOR_FACTORY_NAME).done()

extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package io.quarkus.hibernate.validator.runtime;
22

3+
import java.lang.annotation.ElementType;
34
import java.util.ArrayList;
45
import java.util.HashSet;
56
import java.util.Iterator;
67
import java.util.List;
78
import java.util.Locale;
9+
import java.util.Optional;
810
import java.util.Set;
11+
import java.util.function.BiPredicate;
912
import java.util.function.Function;
1013
import java.util.function.Supplier;
1114

@@ -16,6 +19,7 @@
1619
import jakarta.validation.ConstraintValidatorFactory;
1720
import jakarta.validation.MessageInterpolator;
1821
import jakarta.validation.ParameterNameProvider;
22+
import jakarta.validation.Path;
1923
import jakarta.validation.TraversableResolver;
2024
import jakarta.validation.Validation;
2125
import jakarta.validation.Validator;
@@ -25,7 +29,6 @@
2529
import org.hibernate.validator.HibernateValidatorFactory;
2630
import org.hibernate.validator.PredefinedScopeHibernateValidator;
2731
import org.hibernate.validator.PredefinedScopeHibernateValidatorConfiguration;
28-
import org.hibernate.validator.internal.engine.resolver.JPATraversableResolver;
2932
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
3033
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
3134
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
@@ -83,7 +86,8 @@ public void run() {
8386
public Function<SyntheticCreationalContext<HibernateValidatorFactory>, HibernateValidatorFactory> hibernateValidatorFactory(
8487
Set<Class<?>> classesToBeValidated,
8588
Set<String> detectedBuiltinConstraints, Set<Class<?>> valueExtractorClasses,
86-
boolean hasXmlConfiguration, boolean jpaInClasspath,
89+
boolean hasXmlConfiguration,
90+
Optional<BiPredicate<Object, String>> attributeLoadedPredicate,
8791
LocalesBuildTimeConfig localesBuildTimeConfig,
8892
HibernateValidatorBuildTimeConfig hibernateValidatorBuildTimeConfig) {
8993
return new Function<>() {
@@ -141,8 +145,8 @@ public HibernateValidatorFactory apply(SyntheticCreationalContext<HibernateValid
141145
configuration.traversableResolver(configuredTraversableResolver.get());
142146
} else {
143147
// we still define the one we want to use so that we do not rely on runtime automatic detection
144-
if (jpaInClasspath) {
145-
configuration.traversableResolver(new JPATraversableResolver());
148+
if (attributeLoadedPredicate.isPresent()) {
149+
configuration.traversableResolver(new DelegatingTraversableResolver(attributeLoadedPredicate.get()));
146150
} else {
147151
configuration.traversableResolver(new TraverseAllTraversableResolver());
148152
}
@@ -291,4 +295,24 @@ public ResteasyConfigSupport get() {
291295
public void hibernateValidatorFactoryInit(BeanContainer beanContainer) {
292296
HibernateValidatorFactory hibernateValidatorFactory = beanContainer.beanInstance(HibernateValidatorFactory.class);
293297
}
298+
299+
static final class DelegatingTraversableResolver implements TraversableResolver {
300+
private final BiPredicate<Object, String> attributeLoadedPredicate;
301+
302+
DelegatingTraversableResolver(BiPredicate<Object, String> attributeLoadedPredicate) {
303+
this.attributeLoadedPredicate = attributeLoadedPredicate;
304+
}
305+
306+
@Override
307+
public boolean isReachable(Object entity, Path.Node traversableProperty, Class<?> rootBeanType,
308+
Path pathToTraversableObject, ElementType elementType) {
309+
return attributeLoadedPredicate.test(entity, traversableProperty.getName());
310+
}
311+
312+
@Override
313+
public boolean isCascadable(Object traversableObject, Path.Node traversableProperty, Class<?> rootBeanType,
314+
Path pathToTraversableObject, ElementType elementType) {
315+
return true;
316+
}
317+
}
294318
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.quarkus.hibernate.validator.spi;
2+
3+
import java.util.function.BiPredicate;
4+
5+
import io.quarkus.builder.item.SimpleBuildItem;
6+
7+
/**
8+
* BuildItem to replace the default traversable resolver
9+
*/
10+
public final class BeanValidationTraversableResolverBuildItem extends SimpleBuildItem {
11+
12+
private final BiPredicate<Object, String> attributeLoadedPredicate;
13+
14+
public BeanValidationTraversableResolverBuildItem(BiPredicate<Object, String> attributeLoadedPredicate) {
15+
this.attributeLoadedPredicate = attributeLoadedPredicate;
16+
}
17+
18+
public BiPredicate<Object, String> getAttributeLoadedPredicate() {
19+
return attributeLoadedPredicate;
20+
}
21+
}

0 commit comments

Comments
 (0)