0
# Custom Constraint Validators
1
2
Hibernate Validator extensions to the constraint validator API providing enhanced initialization context, dynamic message parameters, expression language variables, dynamic payloads, and constraint validator payload access for advanced custom validation logic.
3
4
## Capabilities
5
6
### Hibernate Constraint Validator
7
8
Enhanced constraint validator interface with Hibernate-specific initialization.
9
10
```java { .api }
11
package org.hibernate.validator.constraintvalidation;
12
13
import jakarta.validation.ConstraintValidator;
14
import jakarta.validation.metadata.ConstraintDescriptor;
15
import java.lang.annotation.Annotation;
16
17
/**
18
* Hibernate Validator extension to ConstraintValidator.
19
* Provides enhanced initialization with additional Hibernate-specific context.
20
*
21
* @param <A> constraint annotation type
22
* @param <T> target type this validator can validate
23
* @since 6.0.5
24
*/
25
@Incubating
26
interface HibernateConstraintValidator<A extends Annotation, T> extends ConstraintValidator<A, T> {
27
/**
28
* Initialize validator with enhanced Hibernate-specific context.
29
* This method is called instead of initialize(A) when using Hibernate Validator.
30
*
31
* @param constraintDescriptor descriptor for the constraint
32
* @param initializationContext Hibernate-specific initialization context
33
*/
34
default void initialize(
35
ConstraintDescriptor<A> constraintDescriptor,
36
HibernateConstraintValidatorInitializationContext initializationContext
37
) {
38
// Default implementation delegates to standard initialize method
39
initialize(constraintDescriptor.getAnnotation());
40
}
41
}
42
```
43
44
**Usage Example:**
45
46
```java
47
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator;
48
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext;
49
import jakarta.validation.metadata.ConstraintDescriptor;
50
51
public class MyValidator implements HibernateConstraintValidator<MyConstraint, String> {
52
53
private String pattern;
54
55
@Override
56
public void initialize(
57
ConstraintDescriptor<MyConstraint> constraintDescriptor,
58
HibernateConstraintValidatorInitializationContext initializationContext
59
) {
60
MyConstraint annotation = constraintDescriptor.getAnnotation();
61
this.pattern = annotation.pattern();
62
63
// Access Hibernate-specific initialization context
64
// (future extensions may add more context information here)
65
}
66
67
@Override
68
public boolean isValid(String value, ConstraintValidatorContext context) {
69
if (value == null) {
70
return true;
71
}
72
return value.matches(pattern);
73
}
74
}
75
```
76
77
### Hibernate Constraint Validator Initialization Context
78
79
Provides contextual data and operations when initializing a constraint validator, including access to script evaluators, clock providers, and temporal validation tolerance.
80
81
```java { .api }
82
package org.hibernate.validator.constraintvalidation;
83
84
import jakarta.validation.ClockProvider;
85
import java.time.Duration;
86
import org.hibernate.validator.spi.scripting.ScriptEvaluator;
87
88
/**
89
* Provides contextual data and operations when initializing a constraint validator.
90
*
91
* @since 6.0.5
92
*/
93
@Incubating
94
interface HibernateConstraintValidatorInitializationContext {
95
/**
96
* Returns a ScriptEvaluator for the given scripting language.
97
* The ScriptEvaluator is created by the ScriptEvaluatorFactory passed at bootstrap.
98
*
99
* @param languageName the name of the scripting language (e.g., "javascript", "groovy")
100
* @return script evaluator for the given language, never null
101
* @throws ScriptEvaluatorNotFoundException if no evaluator found for the language
102
*/
103
ScriptEvaluator getScriptEvaluatorForLanguage(String languageName);
104
105
/**
106
* Returns the provider for obtaining the current time as a Clock.
107
* Used for validating temporal constraints like @Future and @Past.
108
*
109
* @return the clock provider, never null. If not configured, returns default
110
* implementation using current system time and default time zone.
111
*/
112
ClockProvider getClockProvider();
113
114
/**
115
* Returns the temporal validation tolerance (acceptable margin of error
116
* when comparing date/time in temporal constraints).
117
*
118
* @return the tolerance duration
119
* @since 6.0.5
120
*/
121
@Incubating
122
Duration getTemporalValidationTolerance();
123
}
124
```
125
126
**Usage Example:**
127
128
```java
129
import org.hibernate.validator.constraintvalidation.*;
130
import org.hibernate.validator.spi.scripting.ScriptEvaluator;
131
import jakarta.validation.ClockProvider;
132
import jakarta.validation.metadata.ConstraintDescriptor;
133
import java.time.*;
134
135
public class AdvancedValidator implements HibernateConstraintValidator<AdvancedConstraint, String> {
136
137
private ScriptEvaluator scriptEvaluator;
138
private Clock clock;
139
private Duration tolerance;
140
141
@Override
142
public void initialize(
143
ConstraintDescriptor<AdvancedConstraint> constraintDescriptor,
144
HibernateConstraintValidatorInitializationContext initializationContext
145
) {
146
// Access script evaluator for dynamic validation logic
147
this.scriptEvaluator = initializationContext
148
.getScriptEvaluatorForLanguage("javascript");
149
150
// Access clock for temporal validation
151
ClockProvider clockProvider = initializationContext.getClockProvider();
152
this.clock = clockProvider.getClock();
153
154
// Access temporal tolerance for fuzzy time comparisons
155
this.tolerance = initializationContext.getTemporalValidationTolerance();
156
}
157
158
@Override
159
public boolean isValid(String value, ConstraintValidatorContext context) {
160
// Use initialized resources for validation
161
LocalDateTime now = LocalDateTime.now(clock);
162
// ... validation logic using script evaluator, clock, and tolerance
163
return true;
164
}
165
}
166
```
167
168
### Hibernate Constraint Validator Context
169
170
Enhanced validation context with dynamic message parameters, expression language variables, and dynamic payloads.
171
172
```java { .api }
173
package org.hibernate.validator.constraintvalidation;
174
175
import jakarta.validation.ConstraintValidatorContext;
176
177
/**
178
* Hibernate-specific extension to ConstraintValidatorContext.
179
* Provides additional capabilities for message parameters, expression variables,
180
* dynamic payloads, and constraint validator payload access.
181
*/
182
interface HibernateConstraintValidatorContext extends ConstraintValidatorContext {
183
/**
184
* Add named message parameter for interpolation.
185
* Parameters can be referenced in message templates as {paramName}.
186
*
187
* @param name parameter name
188
* @param value parameter value (will be converted to string)
189
* @return this context for method chaining
190
* @since 5.4.1
191
*/
192
HibernateConstraintValidatorContext addMessageParameter(String name, Object value);
193
194
/**
195
* Add expression language variable for message interpolation.
196
* Variables can be used in EL expressions within message templates.
197
*
198
* @param name variable name
199
* @param value variable value
200
* @return this context for method chaining
201
*/
202
HibernateConstraintValidatorContext addExpressionVariable(String name, Object value);
203
204
/**
205
* Set dynamic payload for constraint violation.
206
* Payload can be retrieved from HibernateConstraintViolation.
207
*
208
* @param payload payload object
209
* @return this context for method chaining
210
* @since 5.3
211
*/
212
HibernateConstraintValidatorContext withDynamicPayload(Object payload);
213
214
/**
215
* Get constraint validator payload passed during configuration.
216
* Payload is set via HibernateValidatorContext.constraintValidatorPayload().
217
*
218
* @param <C> payload type
219
* @param type payload class
220
* @return constraint validator payload or null if not set
221
* @since 6.0.9
222
*/
223
@Incubating
224
<C> C getConstraintValidatorPayload(Class<C> type);
225
226
/**
227
* Build constraint violation with Hibernate-specific features.
228
* Returns HibernateConstraintViolationBuilder for additional configuration.
229
*
230
* @param messageTemplate message template
231
* @return Hibernate constraint violation builder
232
*/
233
@Override
234
HibernateConstraintViolationBuilder buildConstraintViolationWithTemplate(String messageTemplate);
235
}
236
```
237
238
**Usage Example:**
239
240
```java
241
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
242
import jakarta.validation.ConstraintValidator;
243
import jakarta.validation.ConstraintValidatorContext;
244
245
public class RangeValidator implements ConstraintValidator<Range, Integer> {
246
247
private int min;
248
private int max;
249
250
@Override
251
public void initialize(Range constraintAnnotation) {
252
this.min = constraintAnnotation.min();
253
this.max = constraintAnnotation.max();
254
}
255
256
@Override
257
public boolean isValid(Integer value, ConstraintValidatorContext context) {
258
if (value == null) {
259
return true;
260
}
261
262
if (value < min || value > max) {
263
// Unwrap to Hibernate-specific context
264
HibernateConstraintValidatorContext hibernateContext =
265
context.unwrap(HibernateConstraintValidatorContext.class);
266
267
// Disable default violation
268
context.disableDefaultConstraintViolation();
269
270
// Add message parameters
271
hibernateContext
272
.addMessageParameter("min", min)
273
.addMessageParameter("max", max)
274
.addMessageParameter("value", value)
275
// Add EL variable
276
.addExpressionVariable("outOfRange", Math.abs(value - (min + max) / 2))
277
// Set dynamic payload with additional info
278
.withDynamicPayload(new RangeValidationInfo(value, min, max))
279
// Build custom message
280
.buildConstraintViolationWithTemplate(
281
"Value {value} is out of range [{min}, {max}]")
282
.addConstraintViolation();
283
284
return false;
285
}
286
287
return true;
288
}
289
}
290
291
// Payload class for additional validation info
292
class RangeValidationInfo {
293
private final int value;
294
private final int min;
295
private final int max;
296
297
RangeValidationInfo(int value, int min, int max) {
298
this.value = value;
299
this.min = min;
300
this.max = max;
301
}
302
303
// Getters...
304
}
305
```
306
307
### Hibernate Constraint Violation Builder
308
309
Enhanced constraint violation builder with Hibernate-specific features.
310
311
```java { .api }
312
package org.hibernate.validator.constraintvalidation;
313
314
import jakarta.validation.ConstraintValidatorContext;
315
316
/**
317
* Hibernate-specific extension to ConstraintViolationBuilder.
318
* Provides enhanced constraint violation building capabilities.
319
*/
320
interface HibernateConstraintViolationBuilder
321
extends ConstraintValidatorContext.ConstraintViolationBuilder {
322
323
/**
324
* Add node to constraint violation property path.
325
*
326
* @param name node name
327
* @return node builder context
328
*/
329
@Override
330
NodeBuilderDefinedContext addNode(String name);
331
332
/**
333
* Add property node to constraint violation property path.
334
*
335
* @param name property name
336
* @return node builder defined context
337
*/
338
@Override
339
NodeBuilderCustomizableContext addPropertyNode(String name);
340
341
/**
342
* Add bean node to constraint violation property path.
343
*
344
* @return node builder defined context
345
*/
346
@Override
347
NodeBuilderCustomizableContext addBeanNode();
348
349
/**
350
* Add parameter node to constraint violation property path.
351
*
352
* @param index parameter index
353
* @return node builder defined context
354
*/
355
@Override
356
LeafNodeBuilderCustomizableContext addParameterNode(int index);
357
358
/**
359
* Add container element node to constraint violation property path.
360
*
361
* @param name property name
362
* @param containerType container type
363
* @param typeArgumentIndex type argument index
364
* @return node builder defined context
365
*/
366
@Override
367
ContainerElementNodeBuilderDefinedContext addContainerElementNode(
368
String name, Class<?> containerType, Integer typeArgumentIndex);
369
}
370
```
371
372
### Hibernate Constraint Violation
373
374
Enhanced constraint violation containing dynamic payload.
375
376
```java { .api }
377
package org.hibernate.validator.engine;
378
379
import jakarta.validation.ConstraintViolation;
380
381
/**
382
* Custom ConstraintViolation with additional Hibernate-specific information.
383
* Provides access to dynamic payload set during validation.
384
*
385
* @param <T> root bean type
386
* @since 5.3
387
*/
388
interface HibernateConstraintViolation<T> extends ConstraintViolation<T> {
389
/**
390
* Get dynamic payload set via HibernateConstraintValidatorContext.
391
* Returns null if no payload was set.
392
*
393
* @param <C> payload type
394
* @param type payload class
395
* @return dynamic payload or null
396
*/
397
<C> C getDynamicPayload(Class<C> type);
398
}
399
```
400
401
**Usage Example:**
402
403
```java
404
import org.hibernate.validator.engine.HibernateConstraintViolation;
405
import jakarta.validation.ConstraintViolation;
406
import java.util.Set;
407
408
// Validate object
409
Set<ConstraintViolation<User>> violations = validator.validate(user);
410
411
// Process violations and extract dynamic payloads
412
for (ConstraintViolation<User> violation : violations) {
413
// Unwrap to Hibernate-specific violation
414
HibernateConstraintViolation<User> hibernateViolation =
415
violation.unwrap(HibernateConstraintViolation.class);
416
417
// Get dynamic payload if available
418
RangeValidationInfo info = hibernateViolation.getDynamicPayload(RangeValidationInfo.class);
419
420
if (info != null) {
421
System.out.println("Range violation details:");
422
System.out.println(" Value: " + info.getValue());
423
System.out.println(" Min: " + info.getMin());
424
System.out.println(" Max: " + info.getMax());
425
}
426
427
System.out.println("Message: " + violation.getMessage());
428
System.out.println("Property: " + violation.getPropertyPath());
429
}
430
```
431
432
### Hibernate Cross-Parameter Constraint Validator Context
433
434
Hibernate-specific context for cross-parameter constraint validators, extending HibernateConstraintValidatorContext with access to method parameter names.
435
436
```java { .api }
437
package org.hibernate.validator.constraintvalidation;
438
439
import java.util.List;
440
441
/**
442
* Hibernate-specific context for cross-parameter constraint validators.
443
* Extends HibernateConstraintValidatorContext with additional functionality
444
* for accessing method parameter names during cross-parameter validation.
445
*
446
* @since 6.1.0
447
*/
448
@Incubating
449
interface HibernateCrossParameterConstraintValidatorContext extends HibernateConstraintValidatorContext {
450
/**
451
* Returns the list of parameter names of the validated method.
452
* Useful for providing detailed error messages that reference specific parameters by name.
453
*
454
* @return list of method parameter names, never null
455
*/
456
List<String> getMethodParameterNames();
457
}
458
```
459
460
**Note:** This interface extends `HibernateConstraintValidatorContext`, so all methods from that interface are also available:
461
- `addMessageParameter(String name, Object value)`
462
- `addExpressionVariable(String name, Object value)`
463
- `withDynamicPayload(Object payload)`
464
- `getConstraintValidatorPayload(Class<C> type)`
465
- `buildConstraintViolationWithTemplate(String messageTemplate)`
466
467
**Usage Example:**
468
469
```java
470
import org.hibernate.validator.constraintvalidation.HibernateCrossParameterConstraintValidatorContext;
471
import jakarta.validation.ConstraintValidator;
472
import jakarta.validation.ConstraintValidatorContext;
473
import java.util.List;
474
475
public class CrossParamValidator
476
implements ConstraintValidator<CrossParamConstraint, Object[]> {
477
478
@Override
479
public boolean isValid(Object[] parameters, ConstraintValidatorContext context) {
480
if (parameters[0] == null || parameters[1] == null) {
481
return true;
482
}
483
484
// Unwrap to Hibernate cross-parameter context
485
HibernateCrossParameterConstraintValidatorContext hibernateContext =
486
context.unwrap(HibernateCrossParameterConstraintValidatorContext.class);
487
488
// Get parameter names for detailed error messages
489
List<String> paramNames = hibernateContext.getMethodParameterNames();
490
491
if (isInvalid(parameters)) {
492
context.disableDefaultConstraintViolation();
493
494
hibernateContext
495
.addMessageParameter("param1Name", paramNames.get(0))
496
.addMessageParameter("param2Name", paramNames.get(1))
497
.addMessageParameter("param1Value", parameters[0])
498
.addMessageParameter("param2Value", parameters[1])
499
.buildConstraintViolationWithTemplate(
500
"Parameters {param1Name}={param1Value} and {param2Name}={param2Value} are incompatible")
501
.addConstraintViolation();
502
503
return false;
504
}
505
506
return true;
507
}
508
}
509
```
510
511
### Default Constraint Validator Factory
512
513
Default implementation of constraint validator factory.
514
515
```java { .api }
516
package org.hibernate.validator.constraintvalidation.spi;
517
518
import jakarta.validation.ConstraintValidator;
519
import jakarta.validation.ConstraintValidatorFactory;
520
521
/**
522
* Default implementation of ConstraintValidatorFactory.
523
* Creates validator instances using zero-argument constructors.
524
*/
525
class DefaultConstraintValidatorFactory implements ConstraintValidatorFactory {
526
/**
527
* Create instance of constraint validator.
528
* Uses zero-argument constructor.
529
*
530
* @param <T> validator type
531
* @param key validator class
532
* @return validator instance
533
*/
534
@Override
535
<T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key);
536
537
/**
538
* Release validator instance.
539
* Default implementation does nothing.
540
*
541
* @param instance validator instance to release
542
*/
543
@Override
544
void releaseInstance(ConstraintValidator<?, ?> instance);
545
}
546
```
547
548
### Regular Expression URL Validator
549
550
Alternative URL validator using regular expressions instead of java.net.URL.
551
552
```java { .api }
553
package org.hibernate.validator.constraintvalidators;
554
555
import jakarta.validation.ConstraintValidator;
556
import jakarta.validation.ConstraintValidatorContext;
557
import org.hibernate.validator.constraints.URL;
558
559
/**
560
* Regular expression-based URL validator.
561
* Can be used as alternative to default java.net.URL-based validator
562
* for validating non-standard protocols or URL formats.
563
*/
564
class RegexpURLValidator implements ConstraintValidator<URL, CharSequence> {
565
/**
566
* Initialize validator with constraint annotation.
567
*
568
* @param url URL constraint annotation
569
*/
570
@Override
571
void initialize(URL url);
572
573
/**
574
* Validate URL using regular expression matching.
575
*
576
* @param value URL string to validate
577
* @param context validation context
578
* @return true if valid, false otherwise
579
*/
580
@Override
581
boolean isValid(CharSequence value, ConstraintValidatorContext context);
582
}
583
```
584
585
**Usage Example:**
586
587
```java
588
// Configure to use RegexpURLValidator via XML or programmatic API
589
// In META-INF/validation.xml or constraint mapping:
590
591
ConstraintMapping mapping = configuration.createConstraintMapping();
592
593
mapping.constraintDefinition(URL.class)
594
.includeExistingValidators(false)
595
.validatedBy(CharSequence.class, RegexpURLValidator.class);
596
```
597
598
## Complete Custom Validator Example
599
600
```java
601
import jakarta.validation.*;
602
import jakarta.validation.constraints.Pattern;
603
import org.hibernate.validator.constraintvalidation.*;
604
import org.hibernate.validator.engine.HibernateConstraintViolation;
605
import java.lang.annotation.*;
606
607
// 1. Define custom constraint annotation
608
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
609
@Retention(RetentionPolicy.RUNTIME)
610
@Constraint(validatedBy = PhoneNumberValidator.class)
611
@interface PhoneNumber {
612
String message() default "Invalid phone number";
613
Class<?>[] groups() default {};
614
Class<? extends Payload>[] payload() default {};
615
616
String countryCode() default "US";
617
}
618
619
// 2. Create validator using Hibernate extensions
620
class PhoneNumberValidator implements HibernateConstraintValidator<PhoneNumber, String> {
621
622
private String countryCode;
623
private PhoneValidationRules rules;
624
625
@Override
626
public void initialize(
627
ConstraintDescriptor<PhoneNumber> constraintDescriptor,
628
HibernateConstraintValidatorInitializationContext initializationContext
629
) {
630
PhoneNumber annotation = constraintDescriptor.getAnnotation();
631
this.countryCode = annotation.countryCode();
632
this.rules = PhoneValidationRules.forCountry(countryCode);
633
}
634
635
@Override
636
public boolean isValid(String value, ConstraintValidatorContext context) {
637
if (value == null || value.isEmpty()) {
638
return true;
639
}
640
641
// Unwrap to Hibernate context for enhanced features
642
HibernateConstraintValidatorContext hibernateContext =
643
context.unwrap(HibernateConstraintValidatorContext.class);
644
645
// Normalize phone number
646
String normalized = normalizePhoneNumber(value);
647
648
// Validate format
649
if (!rules.matches(normalized)) {
650
context.disableDefaultConstraintViolation();
651
652
hibernateContext
653
.addMessageParameter("countryCode", countryCode)
654
.addMessageParameter("format", rules.getFormat())
655
.addMessageParameter("example", rules.getExample())
656
.addExpressionVariable("providedValue", value)
657
.withDynamicPayload(new PhoneValidationDetails(
658
value, normalized, countryCode, rules.getFormat()))
659
.buildConstraintViolationWithTemplate(
660
"Invalid phone number for {countryCode}. Expected format: {format}")
661
.addConstraintViolation();
662
663
return false;
664
}
665
666
return true;
667
}
668
669
private String normalizePhoneNumber(String phone) {
670
return phone.replaceAll("[^0-9+]", "");
671
}
672
}
673
674
// 3. Dynamic payload class
675
class PhoneValidationDetails {
676
private final String originalValue;
677
private final String normalizedValue;
678
private final String countryCode;
679
private final String expectedFormat;
680
681
PhoneValidationDetails(String originalValue, String normalizedValue,
682
String countryCode, String expectedFormat) {
683
this.originalValue = originalValue;
684
this.normalizedValue = normalizedValue;
685
this.countryCode = countryCode;
686
this.expectedFormat = expectedFormat;
687
}
688
689
// Getters...
690
public String getOriginalValue() { return originalValue; }
691
public String getNormalizedValue() { return normalizedValue; }
692
public String getCountryCode() { return countryCode; }
693
public String getExpectedFormat() { return expectedFormat; }
694
}
695
696
// 4. Use the constraint
697
class Contact {
698
@PhoneNumber(countryCode = "US")
699
private String phoneNumber;
700
701
// Getters and setters...
702
}
703
704
// 5. Validate and extract payload
705
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
706
Contact contact = new Contact();
707
contact.setPhoneNumber("invalid");
708
709
Set<ConstraintViolation<Contact>> violations = validator.validate(contact);
710
711
for (ConstraintViolation<Contact> violation : violations) {
712
HibernateConstraintViolation<Contact> hv =
713
violation.unwrap(HibernateConstraintViolation.class);
714
715
PhoneValidationDetails details = hv.getDynamicPayload(PhoneValidationDetails.class);
716
717
if (details != null) {
718
System.out.println("Phone validation failed:");
719
System.out.println(" Original: " + details.getOriginalValue());
720
System.out.println(" Normalized: " + details.getNormalizedValue());
721
System.out.println(" Expected format: " + details.getExpectedFormat());
722
}
723
}
724
```
725
726
## Using Constraint Validator Payload
727
728
Pass configuration data to validators at runtime.
729
730
```java
731
import org.hibernate.validator.HibernateValidatorContext;
732
import org.hibernate.validator.HibernateValidatorFactory;
733
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
734
import jakarta.validation.*;
735
736
// 1. Create payload with validation configuration
737
class ValidationConfig {
738
private boolean strictMode;
739
private String apiKey;
740
741
public ValidationConfig(boolean strictMode, String apiKey) {
742
this.strictMode = strictMode;
743
this.apiKey = apiKey;
744
}
745
746
// Getters...
747
public boolean isStrictMode() { return strictMode; }
748
public String getApiKey() { return apiKey; }
749
}
750
751
// 2. Create validator that uses payload
752
class StrictEmailValidator implements ConstraintValidator<Email, String> {
753
754
@Override
755
public boolean isValid(String value, ConstraintValidatorContext context) {
756
if (value == null) {
757
return true;
758
}
759
760
HibernateConstraintValidatorContext hibernateContext =
761
context.unwrap(HibernateConstraintValidatorContext.class);
762
763
// Get validator payload
764
ValidationConfig config = hibernateContext.getConstraintValidatorPayload(ValidationConfig.class);
765
766
if (config != null && config.isStrictMode()) {
767
// Use strict validation rules
768
return validateStrictEmail(value, config.getApiKey());
769
} else {
770
// Use lenient validation rules
771
return validateBasicEmail(value);
772
}
773
}
774
775
private boolean validateStrictEmail(String email, String apiKey) {
776
// Perform strict validation (e.g., verify domain exists via API)
777
return true; // Implementation details omitted
778
}
779
780
private boolean validateBasicEmail(String email) {
781
// Basic format validation
782
return email.contains("@");
783
}
784
}
785
786
// 3. Configure validator with payload
787
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
788
HibernateValidatorFactory hibernateFactory = factory.unwrap(HibernateValidatorFactory.class);
789
790
ValidationConfig config = new ValidationConfig(true, "api-key-123");
791
792
Validator validator = hibernateFactory.usingContext()
793
.constraintValidatorPayload(config)
794
.getValidator();
795
796
// 4. Use validator (payload is available to all validators)
797
User user = new User();
798
user.setEmail("test@example.com");
799
Set<ConstraintViolation<User>> violations = validator.validate(user);
800
```
801
802
### Enhanced Bean Performance Optimization
803
804
Marker interface for beans that provide optimized property value access, avoiding reflection overhead during validation.
805
806
```java { .api }
807
package org.hibernate.validator.engine;
808
809
/**
810
* Hibernate Validator specific marker interface for performance optimization.
811
* Beans implementing this interface provide direct property value access methods
812
* instead of using reflection.
813
*
814
* Important: When implementing this interface, provide access to ALL constrained
815
* getters and fields for the class and all its superclasses. Otherwise,
816
* IllegalArgumentException may be thrown by the validation engine.
817
*
818
* @since 6.1
819
*/
820
@Incubating
821
interface HibernateValidatorEnhancedBean {
822
/** Method name for field value access */
823
String GET_FIELD_VALUE_METHOD_NAME = "$$_hibernateValidator_getFieldValue";
824
825
/** Method name for getter value access */
826
String GET_GETTER_VALUE_METHOD_NAME = "$$_hibernateValidator_getGetterValue";
827
828
/**
829
* Get the value of a field property by name.
830
*
831
* @param name the field name
832
* @return the field value
833
* @throws IllegalArgumentException if no field exists with the given name
834
*/
835
Object $$_hibernateValidator_getFieldValue(String name);
836
837
/**
838
* Get the value returned by a getter property by name.
839
*
840
* @param name the getter name
841
* @return the getter return value
842
* @throws IllegalArgumentException if no getter exists with the given name
843
*/
844
Object $$_hibernateValidator_getGetterValue(String name);
845
}
846
```
847
848
**Usage Example:**
849
850
```java
851
import org.hibernate.validator.engine.HibernateValidatorEnhancedBean;
852
import jakarta.validation.constraints.*;
853
854
/**
855
* Enhanced bean with optimized property access.
856
* This is typically generated by bytecode enhancement tools, not manually written.
857
*/
858
public class User implements HibernateValidatorEnhancedBean {
859
@NotNull
860
@Size(min = 3, max = 50)
861
private String username;
862
863
@NotNull
864
865
private String email;
866
867
private int age;
868
869
@Min(18)
870
public int getAge() {
871
return age;
872
}
873
874
// Optimized field access - avoids reflection
875
@Override
876
public Object $$_hibernateValidator_getFieldValue(String name) {
877
switch (name) {
878
case "username":
879
return username;
880
case "email":
881
return email;
882
case "age":
883
return age;
884
default:
885
throw new IllegalArgumentException("Unknown field: " + name);
886
}
887
}
888
889
// Optimized getter access - avoids reflection
890
@Override
891
public Object $$_hibernateValidator_getGetterValue(String name) {
892
switch (name) {
893
case "age":
894
return getAge();
895
default:
896
throw new IllegalArgumentException("Unknown getter: " + name);
897
}
898
}
899
900
// Standard getters and setters...
901
}
902
903
// Usage: Enhanced beans validate faster than regular beans
904
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
905
906
User user = new User();
907
user.setUsername("jo"); // Too short
908
user.setEmail("invalid"); // Invalid email
909
910
// Validation uses optimized property access methods (no reflection)
911
Set<ConstraintViolation<User>> violations = validator.validate(user);
912
913
// Note: In practice, enhanced beans are generated by build-time tools
914
// or bytecode enhancement plugins, not manually implemented
915
```
916