LoginSignup
0
2

More than 1 year has passed since last update.

SpringBootで一意性制約のアノテーションを自作(コピペで完成)

Last updated at Posted at 2021-11-15

はじめに

SpringBootでアプリケーションを開発中に、Emailやその他ユニークな値の制約の設定の仕方がわからず、ググったところ、Springのバリデーションでは一意性のエラーチェックがないようなので、自作することになった。

ところが、自分が参考にしている記事を超える記事がなく、小1時間ほど悩んでようやく実装できたので、備忘録として残すことにした。
※Railsならdeviceが自動で実装してくれていた...

環境

・Java11
・SpringBoot2.5.6
・Spring Data JPA
・MySQL
・gradle

アノテーションを自作する

まず、下記の画像のようにcom.example.demo上で右クリックをし、新規→注釈の順でアノテーションインターフェースの雛形を作成する。
スクリーンショット 2021-11-16 6.38.02.png

次に、実際にアノテーションインターフェースのコードを書いていく。

アノテーションインターフェース

package パッケージ名;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
@Constraint(validatedBy = UnusedValidator.class) //この後に作成するバリデーションクラスを指定する
@Target({FIELD}) // 項目に対してバリデーションをかける場合はFIELDを選ぶ
@Retention(RUNTIME)
public @interface Unused {

    String message() default "すでに登録済みのメールアドレスです"; // エラーメッセージ

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    @Target({FIELD})
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        Unused[] value(); // インターフェース名[] value()とする
    }

}

Class<?>[]・・・などよくわからない記述も多いと思うが、アノテーションインターフェースはほぼほぼ定型のようなものなので、ここでの解説は割愛する。

バリデーションクラス

package com.example.demo.validate;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.springframework.beans.factory.annotation.Autowired;

import com.example.demo.entity.MUser;
import com.example.demo.service.UserService;

public class UnusedValidator implements ConstraintValidator<Unused, String> {

    @Autowired
    private UserService service;//各自で設定が必要

    public void initialize(Unused constraintAnnotation) {
    }

    public boolean isValid(String value, ConstraintValidatorContext context) {
        //各自で設定が必要
        MUser email = service.findByEmail(value).orElse(null); // ここのvalueは入力値
        if(email == null){
            return true;
        } 

        return false;
    }
}

バリデーションクラスではインターフェースをimplementsしてアノテーションの内容を記述していく。

アノテーションの処理内容としては、boolean型isValid()メソッドがアノテーションに入る。
ここで、ServiceクラスのfindByEmail()を呼び出し、条件分岐により、処理内容を分けている。

Repositoryクラス

実際の、findByEmail()メソッドの中身だが、JPAを利用して、DBからemailカラムの値を取得するようにしている。

package com.example.demo.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.demo.entity.MUser;

@Repository
public interface UserRepository extends JpaRepository<MUser, Integer> {

     public Optional<MUser> findByEmail(String email);
}

Serviceクラス

そして、ServiceクラスでfindByEmail()メソッドを定義しRepositoryのメソッドを返すようにする。

これにより、アノテーションのバリデーションクラスにDBに登録されているメールアドレスを渡すことができる。

package com.example.demo.service;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.entity.MUser;
import com.example.demo.form.SignupForm;
import com.example.demo.repository.UserRepository;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    /**メールアドレスでユーザー検索*/
    public Optional<MUser> findByEmail(String email) {      
        return userRepository.findByEmail(email);
    }

}

実装

これで後は@〇〇のようにアノテーションを実装するだけで完了だ。

@Unused
private String email;

各自、EntityなりFormなりバリデーションを実装しているクラスがあると思うので、そこで、実装するだけで完了だ。

実際に以下のusersテーブルに登録されている値を入力してみて確認してみた。
スクリーンショット 2021-11-16 7.02.33.png

以下の画像にあるように、しっかりアノテーションが実装されていることが確認できた。
スクリーンショット 2021-11-16 7.01.49.png

筆者のように「一意性制約」などピンポイントでの実装をググる方は結構いると思うが、どの記事も、アノテーションの実装の内容は理解できるが、実際にサービスクラスの中身等まで解説しているのはなく、「各自で実装しろ」と言われても・・・という状態になると思う。

筆者もそうだったため、参考になれば幸いだ。

参考文献

Spring BootでBean Validation (3) バリデーション処理を自作

Spring Boot 自作アノテーションを作成して複数項目にvalidationをかける

Spring Data JPA でのクエリー実装方法まとめ

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2