18

user_nameたとえば、テーブル内で一意である必要があるフィールドがあります。

Spring / Hibernate検証を使用して検証するための最良の方法は何ですか?

4

6 に答える 6

26

考えられる解決策の1つは、カスタム@UniqueKey制約(および対応するバリデーター)を作成することです。データベース内の既存のレコードを検索するには、のインスタンスEntityManager(またはHibernate Session)をに提供しますUniqueKeyValidator

EntityManagerAwareValidator

public interface EntityManagerAwareValidator {  
     void setEntityManager(EntityManager entityManager); 
} 

ConstraintValidatorFactoryImpl

public class ConstraintValidatorFactoryImpl implements ConstraintValidatorFactory {

    private EntityManagerFactory entityManagerFactory;

    public ConstraintValidatorFactoryImpl(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Override
    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
        T instance = null;

        try {
            instance = key.newInstance();
        } catch (Exception e) { 
            // could not instantiate class
            e.printStackTrace();
        }

        if(EntityManagerAwareValidator.class.isAssignableFrom(key)) {
            EntityManagerAwareValidator validator = (EntityManagerAwareValidator) instance;
            validator.setEntityManager(entityManagerFactory.createEntityManager());
        }

        return instance;
    }
}

UniqueKey

@Constraint(validatedBy={UniqueKeyValidator.class})
@Target({ElementType.TYPE})
@Retention(RUNTIME)
public @interface UniqueKey {

    String[] columnNames();

    String message() default "{UniqueKey.message}";

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

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

    @Target({ ElementType.TYPE })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        UniqueKey[] value();
    }
}

UniqueKeyValidator

public class UniqueKeyValidator implements ConstraintValidator<UniqueKey, Serializable>, EntityManagerAwareValidator {

    private EntityManager entityManager;

    @Override
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    private String[] columnNames;

    @Override
    public void initialize(UniqueKey constraintAnnotation) {
        this.columnNames = constraintAnnotation.columnNames();

    }

    @Override
    public boolean isValid(Serializable target, ConstraintValidatorContext context) {
        Class<?> entityClass = target.getClass();

        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

        CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();

        Root<?> root = criteriaQuery.from(entityClass);

        List<Predicate> predicates = new ArrayList<Predicate> (columnNames.length);

        try {
            for(int i=0; i<columnNames.length; i++) {
                String propertyName = columnNames[i];
                PropertyDescriptor desc = new PropertyDescriptor(propertyName, entityClass);
                Method readMethod = desc.getReadMethod();
                Object propertyValue = readMethod.invoke(target);
                Predicate predicate = criteriaBuilder.equal(root.get(propertyName), propertyValue);
                predicates.add(predicate);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        criteriaQuery.where(predicates.toArray(new Predicate[predicates.size()]));

        TypedQuery<Object> typedQuery = entityManager.createQuery(criteriaQuery);

        List<Object> resultSet = typedQuery.getResultList(); 

        return resultSet.size() == 0;
    }

}

使用法

@UniqueKey(columnNames={"userName"})
// @UniqueKey(columnNames={"userName", "emailId"}) // composite unique key
//@UniqueKey.List(value = {@UniqueKey(columnNames = { "userName" }), @UniqueKey(columnNames = { "emailId" })}) // more than one unique keys
public class User implements Serializable {

    private String userName;
    private String password;
    private String emailId;

    protected User() {
        super();
    }

    public User(String userName) {
        this.userName = userName;
    }
        ....
}

テスト

public void uniqueKey() {
    EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("default");

    ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
    ValidatorContext validatorContext = validatorFactory.usingContext();
    validatorContext.constraintValidatorFactory(new ConstraintValidatorFactoryImpl(entityManagerFactory));
    Validator validator = validatorContext.getValidator();

    EntityManager em = entityManagerFactory.createEntityManager();

    User se = new User("abc", poizon);

       Set<ConstraintViolation<User>> violations = validator.validate(se);
    System.out.println("Size:- " + violations.size());

    em.getTransaction().begin();
    em.persist(se);
    em.getTransaction().commit();

        User se1 = new User("abc");

    violations = validator.validate(se1);

    System.out.println("Size:- " + violations.size());
}
于 2011-01-19T09:10:20.457 に答える
7

この目的でHibernateValidator(JSR 303)を使用するのは賢明ではないと思います。または、それはHibernateValidatorの目標ではありません。

JSR 303は、Bean検証に関するものです。これは、フィールドが正しく設定されているかどうかを確認することを意味します。しかし、必要なのは、単一のBeanよりもはるかに広い範囲です。それはどういうわけかグローバルスコープにあります(このタイプのすべてのBeanに関して)。--データベースにこの問題を処理させるべきだと思います。データベースの列に一意性制約を設定すると(たとえば、フィールドにに注釈を付けることにより@Column(unique=true))、データベースはフィールドが一意であることを確認します。

とにかく、これに本当にJSR303を使用したい場合は、独自のアノテーションと独自のバリデーターを作成する必要があります。バリデーターはデータベースにアクセスし、指定された値を持つエンティティが他にないかどうかを確認する必要があります。-しかし、適切なセッションでバリデーターからデータベースにアクセスするには、いくつかの問題があると思います。

于 2011-01-06T12:58:06.053 に答える
4

1つの可能性は、フィールドに@NaturalIdとして注釈を付けることです。

于 2011-01-06T08:45:50.940 に答える
3

@Columnとして設定できる属性を使用できますunique

于 2011-01-06T10:01:55.137 に答える
2

私はある種のトリッキーな解決策を見つけました。

まず、MySqlデータベースに独自の制約を実装しました。

CREATE TABLE XMLTAG
(
    ID INTEGER NOT NULL AUTO_INCREMENT,
    LABEL VARCHAR(64) NOT NULL,
    XPATH VARCHAR(128),
    PRIMARY KEY (ID),
    UNIQUE UQ_XMLTAG_LABEL(LABEL)
) ;

一意のラベルと「XPath」という名前のテキストフィールドで定義されたXMLタグを管理していることがわかります。

とにかく、2番目のステップは、ユーザーが不正な更新を行おうとしたときに発生したエラーを単にキャッチすることです。悪い更新は、現在のラベルを既存のラベルに置き換えようとする場合です。ラベルをそのままにしておけば問題ありません。だから、私のコントローラーでは:

    @RequestMapping(value = "/updatetag", method = RequestMethod.POST)
    public String updateTag(
            @ModelAttribute("tag") Tag tag, 
            @Valid Tag validTag,
            BindingResult result,
            ModelMap map) {

        if(result.hasErrors()) {        // you don't care : validation of other
            return "editTag";       // constraints like @NotEmpty
        }
        else {
            try {
                tagService.updateTag(tag);    // try to update
                return "redirect:/tags";   // <- if it works        
            }
            catch (DataIntegrityViolationException ex) { // if it doesn't work
                result.rejectValue("label", "Unique.tag.label"); // pass an error message to the view 
                return "editTag"; // same treatment as other validation errors
            }
        }
    }

これは@Uniqueパターンと競合する可能性がありますが、このダーティメソッドを使用して追加を有効にすることもできます。

注:まだ1つの問題があります。例外の前に他の検証エラーがキャッチされた場合、単一性に関するメッセージは表示されません。

于 2013-04-23T14:57:59.963 に答える
2

このコードは、を使用して実装された前のコードに基づいていEntityManagerます。誰かがHibernateを使用する必要がある場合に備えてSession。Hibernateを使用したカスタムアノテーションSession
@UniqueKey.java

import java.lang.annotation.*;
import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueKeyValidator.class)
@Documented
public @interface UniqueKey {
    String columnName();
    Class<?> className();
    String message() default "{UniqueKey.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

UnqieKeyValidator.java

import ch.qos.logback.classic.gaffer.PropertyUtil;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
@Transactional
@Repository
public class UniqueKeyValidator implements ConstraintValidator<UniqueKey, String> {

    @Autowired
    private SessionFactory sessionFactory;

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    private String columnName;
    private Class<?> entityClass;

    @Override
    public void initialize(UniqueKey constraintAnnotation) {
        this.columnNames = constraintAnnotation.columnNames();
        this.entityClass = constraintAnnotation.className();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        Class<?> entityClass = this.entityClass;
        System.out.println("class: " + entityClass.toString());
        Criteria criteria = getSession().createCriteria(entityClass);
        try {
                criteria.add(Restrictions.eq(this.columnName, value));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return criteria.list().size()==0;
    }
}

使用法

@UniqueKey(columnNames="userName", className = UserEntity.class)
// @UniqueKey(columnNames="userName") // unique key
于 2017-01-02T09:12:56.627 に答える