Bean Validation の @Pattern の仕様
はじめに
今回は Java の Bean Validation の @Pattern
の仕様について説明する。誤った例を使っている解説が多く、それを参照したコードをレビューで見て疲弊する日々を過しているため、世の中に正しい情報が提供されることを期待するのは諦めて自ら発信していくことにした。
先日の記事もその一環の中の一つである。
TL;DL
@Pattern
のregexp
で指定する正規表現の先頭に^
と末尾に$
を付ける必要はない- ドキュメントを読んで仕様を確認しましょう
- 個人のブログや Qiita、Zenn の情報を鵜呑みにしない (自己矛盾)
誤った実装
Google で Spring Boot を使ってリクエストに @Pattern
アノテーションを使い正規表現でバリデーションをかける方法を検索すると次のような例をよく見る。
1@Value2@Builder3public class Example {45@Pattern(regexp = "^[A-Z][a-z]+$")6String name = "Name";7}
^
と $
の両方、もしくはいずれかを使っている例も見られるが ^<正規表現>$
という形式で @Pattern
の regexp
に正規表現を記述している。これは正しく動作するため、誤った実装ではないが ^
と $
を使う必要はない。
^
、$
をわざわざ付けている人は @Pattern
の仕様を理解していない可能性がある。
Jakarta Bean Validation
Java でバリデーションを実装したい場合は、Jakarta Bean Validation に準拠したライブラリを使うのが一般的でだろう。 Jakarta Bean Validation は、JSR 303、JSR 349, JSR 380 で定められた Bean Validation が移管され、 Eclipse Foundation により管理されている Bean Validation について定めた仕様である 1。
Java では Jakarta Bean Validation のように仕様のみが独立して定められ、それを実装するライブラリが複数存在することがある。例えば、Jakarta Bean Validation のリファレンス実装として Hibernate Validator がある。 Hibernate Validator を使うことが多いが、Jakarta Bean Validation に準拠した実装には Apache BVal もある。
Hibernate Validator
PatternValidator
@Pattern
アノテーションのバリデーションは PatternValidator
で行われる。
1/*2* Hibernate Validator, declare and validate application constraints3*4* License: Apache License, Version 2.05* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.6*/7package org.hibernate.validator.internal.constraintvalidators.bv;89import java.lang.invoke.MethodHandles;10import java.util.regex.Matcher;11import java.util.regex.PatternSyntaxException;12import jakarta.validation.ConstraintValidator;13import jakarta.validation.ConstraintValidatorContext;14import jakarta.validation.constraints.Pattern;1516import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;17import org.hibernate.validator.internal.engine.messageinterpolation.util.InterpolationHelper;18import org.hibernate.validator.internal.util.logging.Log;19import org.hibernate.validator.internal.util.logging.LoggerFactory;2021/**22* @author Hardy Ferentschik23*/24public class PatternValidator implements ConstraintValidator<Pattern, CharSequence> {2526private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );2728private java.util.regex.Pattern pattern;29private String escapedRegexp;3031@Override32public void initialize(Pattern parameters) {33Pattern.Flag[] flags = parameters.flags();34int intFlag = 0;35for ( Pattern.Flag flag : flags ) {36intFlag = intFlag | flag.getValue();37}3839try {40pattern = java.util.regex.Pattern.compile( parameters.regexp(), intFlag );41}42catch (PatternSyntaxException e) {43throw LOG.getInvalidRegularExpressionException( e );44}4546escapedRegexp = InterpolationHelper.escapeMessageParameter( parameters.regexp() );47}4849@Override50public boolean isValid(CharSequence value, ConstraintValidatorContext constraintValidatorContext) {51if ( value == null ) {52return true;53}5455if ( constraintValidatorContext instanceof HibernateConstraintValidatorContext ) {56constraintValidatorContext.unwrap( HibernateConstraintValidatorContext.class ).addMessageParameter( "regexp", escapedRegexp );57}5859Matcher m = pattern.matcher( value );60return m.matches();61}62}
検証成功するかどうかは Mather#matches
メソッドで判定される。
Matcher#matches
は、文字列全体が正規表現に一致するかどうかを判定するメソッドである。一部のみが一致している場合は検証は失敗する。そのため、@Pattern
アノテーションでは ^
と $
を使わなくても文字列全体が正規表現に一致すれば検証は成功する。
正しい (?) 実装
^
と $
を付けても動作としては変わらないが、ただのノイズでしかないため、不要な記述は避けるべきだろう。よって、始めに見た実装例は次のように書き換えることができる。
1@Value2@Builder3public class Example {45@Pattern(regexp = "[A-Z][a-z]+")6String name = "Name";7}
おわりに
@Pattern
の regexp
に正規表現を指定する際に ^
と $
を付ける必要はない。昔に書かれた記事では ^$
を使う例はないが、最近書かれた記事では ^$
を付けているものが多いように思う。
ある時点で誤った情報が書かれてしまい、ネットで見つけたコードをそのまま使う人が多いということだろう。ネットで見つけた情報を鵜呑みにせず、公式ドキュメントを読んで仕様を確認することが大切。
Footnotes
-
Java EE と Jakarta EE については Java EE から Jakarta EE へ を参照。 ↩