创建自定义唯一约束

Creating custom unique constraints

我定义了我的@FuSsA_UniqueKey约束注释:

package com.fussa.employee.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

@Constraint(validatedBy = { UniqueIDValidator.class })
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface FuSsA_UniqueKey {

    String columnNames();

    String message() default "{Value is not unique}";

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

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

}

定义了我的注释后,我创建了一个约束验证器 UniqueIDValidator,它能够验证具有 @FuSsA_UniqueKey[=38= 的元素] 注释:

   package com.fussa.employee.util;

import java.io.Serializable;
import java.util.List;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder;
import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext;

import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

public class UniqueIDValidator implements ConstraintValidator<FuSsA_UniqueKey, Serializable> {

    @Autowired
    private SessionFactory sessionFactory;

    private Session s;

    private String uniqueField;
    private FuSsA_UniqueKey unique;

    public void initialize(FuSsA_UniqueKey unique) {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        uniqueField = unique.columnNames();
    }

    @Override
    public boolean isValid(Serializable arg0, ConstraintValidatorContext arg1) {
        String a = arg0.getClass().getSimpleName();
        String query = String.format("from %s where %s = :%s ", a, uniqueField, uniqueField);
        List<?> list = null;
        try {
            s = sessionFactory.getCurrentSession();
            s.setFlushMode(FlushMode.COMMIT);
            list = s.createQuery(query).setParameter(uniqueField, "FuSsA").list();

        } catch (HibernateException e) {
            s = sessionFactory.openSession();
            s.setFlushMode(FlushMode.COMMIT);
            list = s.createQuery(query).setParameter(uniqueField, "FuSsA").list();
        } finally {
            // this is to reset the hibernate config I think
            s.setFlushMode(FlushMode.AUTO);
        }
        if (list != null && !(list.size() > 1)) {
            return true;
        } else {
            ConstraintViolationBuilder cvb = arg1.buildConstraintViolationWithTemplate(unique.message());
            NodeBuilderDefinedContext nbdc = cvb.addNode(unique.columnNames());
            ConstraintValidatorContext cvc = nbdc.addConstraintViolation();
            cvc.disableDefaultConstraintViolation();
            return false;
        }
    }
}

我的 Controller 上的方法 saveEmployee:

    @Autowired
    EmployeeService service;

    @Autowired
    MessageSource messageSource;
    @Autowired
    private Validator validator;

  @RequestMapping(value = { "/new" }, method = RequestMethod.POST)
    public String saveEmployee(@Valid Employee employee, BindingResult result, ModelMap model) {

        if (result.hasErrors()) {
            return "registration";
        }

        Set<ConstraintViolation<Employee>> violations = validator.validate(employee);

        if (!violations.isEmpty()) {

            FieldError ssnError = new FieldError("employee", "ssn", messageSource.getMessage("non.unique.ssn",
                    new String[] { employee.getSsn() }, Locale.getDefault()));
            result.addError(ssnError);

            return "registration";
        } else {
            service.saveEmployee(employee);

            model.addAttribute("success", "Employee " + employee.getName() + " registered successfully");
            return "success";
        }
    }

编辑

我还添加到我的 @Configuration class AppConfig 这个方法,所以我可以在我的控制器上自动连接它 "not sure if its correct":

@Bean
    public Validator localValidatorFactoryBean() {
        return new LocalValidatorFactoryBean();
    }

当我尝试保存我的对象时..我遇到了这个错误:

Caused by: javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:451)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:127)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:87)
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:73)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:592)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:555)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:490)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:454)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:406)
    at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:204)
    at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:108)
    at org.springframework.validation.DataBinder.validate(DataBinder.java:866)
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.validateIfApplicable(ModelAttributeMethodProcessor.java:164)
    at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:111)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:99)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:817)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:731)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:968)
    ... 31 more
Caused by: java.lang.NullPointerException
    at com.fussa.employee.util.UniqueIDValidator.isValid(UniqueIDValidator.java:54)
    at com.fussa.employee.util.UniqueIDValidator.isValid(UniqueIDValidator.java:1)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:448)
    ... 54 more

所以在该行捕获了错误:

ConstraintViolationBuilder cvb = arg1.buildConstraintViolationWithTemplate(unique.message());

感谢您的任何建议..

我通过初始化变量 unique 解决了这个问题:

public void initialize(FuSsA_UniqueKey unique) {    
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
            this.unique = unique;
            uniqueField = unique.columnNames();
        }