Ivan Smorodin
Ivan Smorodin

Reputation: 161

How to create a constraint validator for multiple fields through annotation in spring 4.*

How to create a validator restrictions for more fields through annotation in spring 4.* for example

@UniqueValidator

@Entity
@Table(name = "persons")
@UniqueValidator(message="Peson already exist",columns={"name","lastName"})
public class {
    @Column
    private String name;
    @Column
    private String lastName;
}

roughly speaking..

select ... from persons where name='qwerty' and lastName='asdfgh'

Upvotes: 4

Views: 6591

Answers (1)

user2066936
user2066936

Reputation:

Here's one way to do it in Spring MVC and JSR 303 validation:

Create a constraint annotation:

package com.awgtek.model.validation;

import java.lang.annotation.Documented;
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;

/**
 * The target field should be unique in the data store.
 */
@Documented
@Constraint(validatedBy = UniqueNameValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueName {

    /**
     * @return the error message template
     */
    String message() default "{com.awgtek.model.validation.UniqueName.message}";

    /**
     * @return the groups the constraint belongs to
     */
    Class<?>[] groups() default {};

    /**
     * @return the payload associated to the constraint
     */
    Class<? extends Payload>[] payload() default {};

    /**
     * @return a class an instance of which will be used to check the existence of a name.
     */
    Class<? extends NameExistenceChecker> nameExistenceChecker();
}

Apply this annotation at the class level to the model class:

package com.awgtek.model;

import com.awgtek.model.validation.MyNameExistenceChecker;
import com.awgtek.model.validation.UniqueName;
import com.awgtek.model.validation.UniqueNameConstraint;

@UniqueName(groups = UniqueNameConstraint.class, 
nameExistenceChecker = MyNameExistenceChecker.class)
public class ConfigurationAttribute {

    private String name;

    private String lastName;

    // getters and setters omitted

 }

The UniqueNameValidator:

package com.awgtek.model.validation;

import com.awgtek.service.MyService;

import javax.inject.Inject;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class UniqueNameValidator implements ConstraintValidator<UniqueName, Object> {

    @Inject
    private NameExistenceCheckerFactory nameExistenceCheckerFactory;

    private NameExistenceChecker nameExistenceChecker;

    @Override
    public void initialize(UniqueName constraintAnnotation) {
        nameExistenceChecker = nameExistenceCheckerFactory.get(constraintAnnotation.nameExistenceChecker());
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        return !nameExistenceChecker.nameExists(value);
    }
}

The UniqueNameValidator then depends on a couple of classes:

package com.awgtek.model.validation;

import javax.inject.Inject;

import org.springframework.stereotype.Component;

@Component
public class NameExistenceCheckerFactory {

    @Inject
    private NamespaceNameExistenceChecker namespaceNameExistenceChecker;

    @Inject
    private MyNameExistenceChecker myNameExistenceChecker;

    public NameExistenceChecker get(Class<? extends NameExistenceChecker> clazz) {
        if (clazz.equals(MyNameExistenceChecker.class)) {
            return myNameExistenceChecker;
        }
        throw new IllegalStateException("Unknown NameExistenceChecker");
    }
}

package com.awgtek.model.validation;

public interface NameExistenceChecker {

    boolean nameExists(Object objectWithName);
}

The implementation class that actually looks up whether the item exists in the database (via a service):

package com.awgtek.model.validation;

import com.awgtek.model.MyModelClass;
import com.awgtek.service.MyService;

import javax.inject.Inject;

import org.springframework.stereotype.Component;

@Component
public class AttributeNameExistenceChecker implements NameExistenceChecker {

    @Inject
    private MyService myService;

    @Override
    public boolean nameExists(Object objectWithName) {
        MyModelClass myObject = (MyModelClass) objectWithName;
        return myService.itemAlreadyExists(myObject.getName(), myObject.getLastName());
    }
}

Upvotes: 2

Related Questions