or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconstraint-validation.mdconstraints.mdindex.mdmessage-interpolation.mdpath-navigation.mdprogrammatic-config.mdresource-loading.mdspi.md

constraint-validation.mddocs/

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

@Email

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