2024-08-12

Bean Validation @Pattern Specification

Programming
Notes
This article was translated by GPT-5.2-Codex. The original is here.

Introduction

This article explains the specification of Java Bean Validation's @Pattern. I keep seeing many explanations that use incorrect examples, and reviewing code that references them is exhausting, so I decided to stop expecting the world to provide correct information and to publish it myself.

The recent article is also part of that effort.

TL;DR

  • You do not need to add ^ at the beginning and $ at the end of the regular expression specified in @Pattern's regexp
  • Read the docs and confirm the specification
  • Do not blindly trust personal blogs, Qiita, or Zenn (self-contradiction)

Incorrect implementation

If you search Google for how to apply regex validation with the @Pattern annotation in Spring Boot requests, you often see examples like this.

1
@Value
2
@Builder
3
public class Example {
4
5
@Pattern(regexp = "^[A-Z][a-z]+$")
6
String name = "Name";
7
}

You can find examples that use both ^ and $, or only one of them, but they write regex for @Pattern's regexp in the form ^<regex>$. This works and is not incorrect, but you do not need to use ^ and $.

People who deliberately add ^ and $ may not understand the @Pattern specification.

Jakarta Bean Validation

If you want to implement validation in Java, it is common to use a library compliant with Jakarta Bean Validation. Jakarta Bean Validation is the specification for Bean Validation, transferred from JSR 303, JSR 349, and JSR 380, and managed by the Eclipse Foundation. 1

In Java, there are cases where only the specification is defined independently, and multiple libraries implement it. For example, Hibernate Validator is the reference implementation for Jakarta Bean Validation. Hibernate Validator is commonly used, but there is also Apache BVal as a Jakarta Bean Validation-compliant implementation.

Hibernate Validator

PatternValidator

Validation for the @Pattern annotation is performed by PatternValidator.

1
/*
2
* Hibernate Validator, declare and validate application constraints
3
*
4
* License: Apache License, Version 2.0
5
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6
*/
7
package org.hibernate.validator.internal.constraintvalidators.bv;
8
9
import java.lang.invoke.MethodHandles;
10
import java.util.regex.Matcher;
11
import java.util.regex.PatternSyntaxException;
12
import jakarta.validation.ConstraintValidator;
13
import jakarta.validation.ConstraintValidatorContext;
14
import jakarta.validation.constraints.Pattern;
15
16
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
17
import org.hibernate.validator.internal.engine.messageinterpolation.util.InterpolationHelper;
18
import org.hibernate.validator.internal.util.logging.Log;
19
import org.hibernate.validator.internal.util.logging.LoggerFactory;
20
21
/**
22
* @author Hardy Ferentschik
23
*/
24
public class PatternValidator implements ConstraintValidator<Pattern, CharSequence> {
25
26
\tprivate static final Log LOG = LoggerFactory.make(MethodHandles.lookup());
27
28
\tprivate java.util.regex.Pattern pattern;
29
\tprivate String escapedRegexp;
30
31
\t@Override
32
\tpublic void initialize(Pattern parameters) {
33
\t\tPattern.Flag[] flags = parameters.flags();
34
\t\tint intFlag = 0;
35
\t\tfor (Pattern.Flag flag : flags) {
36
\t\t\tintFlag = intFlag | flag.getValue();
37
\t\t}
38
39
\t\ttry {
40
\t\t\tpattern = java.util.regex.Pattern.compile(parameters.regexp(), intFlag);
41
\t\t}
42
\t\tcatch (PatternSyntaxException e) {
43
\t\t\tthrow LOG.getInvalidRegularExpressionException(e);
44
\t\t}
45
46
\t\tescapedRegexp = InterpolationHelper.escapeMessageParameter(parameters.regexp());
47
\t}
48
49
\t@Override
50
\tpublic boolean isValid(CharSequence value, ConstraintValidatorContext constraintValidatorContext) {
51
\t\tif (value == null) {
52
\t\t\treturn true;
53
\t\t}
54
55
\t\tif (constraintValidatorContext instanceof HibernateConstraintValidatorContext) {
56
\t\t\tconstraintValidatorContext.unwrap(HibernateConstraintValidatorContext.class).addMessageParameter("regexp", escapedRegexp);
57
\t\t}
58
59
\t\tMatcher m = pattern.matcher(value);
60
\t\t
61
\t\treturn m.matches();
62
\t}
63
}

Whether validation succeeds is determined by the Matcher#matches method. Matcher#matches checks whether the entire string matches the regular expression. If only part of the string matches, validation fails. Therefore, for the @Pattern annotation, validation succeeds if the entire string matches the regex even without ^ and $.

Correct (?) implementation

Even though adding ^ and $ does not change behavior, it is just noise, so it should be avoided. Thus the earlier example can be rewritten like this.

1
@Value
2
@Builder
3
public class Example {
4
5
@Pattern(regexp = "[A-Z][a-z]+")
6
String name = "Name";
7
}

Conclusion

When specifying a regular expression for @Pattern's regexp, you do not need to add ^ and $. Older articles do not use ^$, but many recent articles seem to add them.

At some point incorrect information was written, and many people likely use code they find online as-is. Do not blindly trust information found online; it is important to read official documentation and confirm the specification.

Footnotes

  1. For Java EE and Jakarta EE, see From Java EE to Jakarta EE.

Amazon アソシエイトについて

この記事には Amazon アソシエイトのリンクが含まれています。Amazonのアソシエイトとして、SuzumiyaAoba は適格販売により収入を得ています。