Alternative Annotations to Java Bean Validation Without Using Hibernate - java

I am working on a project that I need to put some limitations/constrains on the fields of the models(e.g. "String name" field should not exceed 10 characters) . I can only find Java Bean Validation API for this job. However as I see, it is used with Hibernate and Spring Framework.
Unfortunately, an ORM like Hibernate was not used in the project. We are using DAO pattern and JDBI for database operations.
Is there any alternative annotations on Java which helps to put constrains on the fields like Bean Validation does( and hopefully works with magic like Lombok does)? I need basically Size, Min/Max and NonNull annotations.
Basically something like this:
class User {
#Size(max = 10)
String name;
}

karelss already answered, you can also use javax.validation.constraints package here maven link. Here is possible implementation and test code (not perfect one).
User.java
import javax.validation.constraints.Size;
class User {
#Size(max = 10)
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
UserTest.java
import static org.junit.Assert.assertEquals;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.junit.Test;
public class UserTest {
#Test
public void test() {
User user = new User();
// set name over 10
user.setName("12345678912");
// validate the input
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(user);
for (ConstraintViolation v : violations) {
String key = "";
if (v.getPropertyPath() != null) {
key = v.getPropertyPath().toString();
assertEquals("name", key);
assertEquals("size must be between 0 and 10", v.getMessage());
}
}
assertEquals(1, violations.size());
}
}

Java Bean Validation API is the right tool for this job, but as you say is an api, if you are using an application server, you will have different implementations and you can use whatever you want, it's not linked to hibernate or spring, what you see are different providers of the api implementatión. This api works with objects, you can annotate any object with it.
If you don't want to include dependencies you can implement this validations in a compatible way using your own annotations like here
Java 7 Bean validation API

Related

How to validate variables of a class/bean class while assigning values with the setter of the class

I am very new to Java and Spring Boot and trying to implement validation on the variable of a class as shown below and wants to validate each of the variables while initializing the value using the setter of the class.
import javax.validation.constraints.Digits;
import javax.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class LoanRequestBean {
#Size(max = 8)
String uniqueRefBancs;
#Digits(integer=2)
Integer loanId;
#Size(max = 20)
String loanIban;
}
The variable initialization is done by the setter as shown below.
import org.springframework.stereotype.Component;
#Component
public class BancsRequestRecords {
private String[] fieldsArray;
#Valid
LoanRequestBean loanRequest = new LoanRequestBean();
public loanRequestBean RequestParser(String message) {
fieldsArray = message.split("¬");
loanRequest.setUniqueRefBancs(fieldsArray[0]);
loanRequest.setLoanId(Integer.valueOf( fieldsArray[1]));
loanRequest.setLoanIban(fieldsArray[2]);
return loanRequest;
}
}
When I try to invoke Requestparser method with message as 2028110600000001000752¬123456¬FI9080706050403020 in the argument of the method, the values are not getting validated. eg: uniqueRefBancs has max length of 8 but still it does not throw any error while initialization. Could you please help me if it's the right approach to validate the variables or any any other way to achieve it?
Bean Validation is performed when the #Valid or #Validated annotations are used on a method parameter. Further details here. You could use a method to validate your loanRequestBean. To check the result of validation you can use BindingResult. Edit: Unrelated but I would consider renmaing loanRequestBean to LoanRequestBean to follow java class name conventions.
You would need to programmatically trigger the validation of your loanRequest object as follows:
import org.springframework.stereotype.Component;
#Component
public class BancsRequestRecords {
private LoanRequestBean loanRequest = new LoanRequestBean();
private Validator validator;
public BancsRequestRecords(Validator validator) {
this.validator = validator;
}
public loanRequestBean RequestParser(String message) {
String[] fieldsArray = message.split("¬");
loanRequest.setUniqueRefBancs(fieldsArray[0]);
loanRequest.setLoanId(Integer.valueOf( fieldsArray[1]));
loanRequest.setLoanIban(fieldsArray[2]);
Set<ConstraintViolation<LoanRequestBean>> violations = validator.validate(loanRequest);
return loanRequest;
}
}
You can read more about this at https://www.baeldung.com/javax-validation#programmatic.

How to get query parameter name from ConstraintViolationException

I have a service method:
#GetMapping(path = "/api/some/path", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getWhatever(#RequestParam(value = "page-number", defaultValue = "0") #Min(0) Integer pageNumber, ...
If the caller of an API doesn't submit a proper value for page-number query parameter, javax.ConstraintViolationexception is being raised. The message of the exception would read smth like:
getWhatever.pageNumber must be equal or greater than 0
In the response body, I would like to have this message instead:
page-number must be equal or greater than 0
I want my message to have the name of a query parameter, not the name of the argument. IMHO, including the name of the argument is exposing the implementation details.
The problem is, I cannot find an object that is carrying query parameter name. Seems like the ConstraintViolationException doesn't have it.
I am running my app in spring-boot.
Any help would be appreciated.
P.S.: I have been to the other similar threads that claim to solve the problem, none of them actually do in reality.
Here is how I made it work in spring-boot 2.0.3:
I had to override and disable ValidationAutoConfiguration in spring-boot:
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validator;
#Configuration
public class ValidationConfiguration {
public ValidationConfiguration() {
}
#Bean
public static LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setParameterNameDiscoverer(new CustomParamNamesDiscoverer());
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
#Bean
public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment, #Lazy Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
boolean proxyTargetClass = (Boolean) environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
processor.setValidator(validator);
return processor;
}
}
CustomParamNamesDiscoverer sits in the same package and it is a pretty much a copy-paste of DefaultParameterNameDiscoverer, spring-boot's default implementation of param name discoverer:
import org.springframework.core.*;
import org.springframework.util.ClassUtils;
public class CustomParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
private static final boolean kotlinPresent = ClassUtils.isPresent("kotlin.Unit", CustomParameterNameDiscoverer.class.getClassLoader());
public CustomParameterNameDiscoverer() {
if (kotlinPresent) {
this.addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
}
this.addDiscoverer(new ReqParamNamesDiscoverer());
this.addDiscoverer(new StandardReflectionParameterNameDiscoverer());
this.addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}
}
I wanted it to remain pretty much intact (you can see even kotlin checks in there) with the only addition:
I am adding an instance of ReqParamNamesDiscoverer to the linked lists of discoverers. Note that the order of addition does matter here.
Here is the source code:
import com.google.common.base.Strings;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RequestParam;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class ReqParamNamesDiscoverer implements ParameterNameDiscoverer {
public ReqParamNamesDiscoverer() {
}
#Override
#Nullable
public String[] getParameterNames(Method method) {
return doGetParameterNames(method);
}
#Override
#Nullable
public String[] getParameterNames(Constructor<?> constructor) {
return doGetParameterNames(constructor);
}
#Nullable
private static String[] doGetParameterNames(Executable executable) {
Parameter[] parameters = executable.getParameters();
String[] parameterNames = new String[parameters.length];
for (int i = 0; i < parameters.length; ++i) {
Parameter param = parameters[i];
if (!param.isNamePresent()) {
return null;
}
String paramName = param.getName();
if (param.isAnnotationPresent(RequestParam.class)) {
RequestParam requestParamAnnotation = param.getAnnotation(RequestParam.class);
if (!Strings.isNullOrEmpty(requestParamAnnotation.value())) {
paramName = requestParamAnnotation.value();
}
}
parameterNames[i] = paramName;
}
return parameterNames;
}
}
If parameter is annotated with RequestParam annotation, I am retrieving the value attribute and return it as a parameter name.
The next thing was disabling auto validation config, somehow, it doesn't work without it. This annotation does the trick though:
#SpringBootApplication(exclude = {ValidationAutoConfiguration.class})
Also, you need to have a custom handler for your ConstraintValidationException :
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(ConstraintViolationException.class)
public ErrorDTO handleConstraintViolationException(ConstraintViolationException ex) {
Map<String, Collection<String>> errors = new LinkedHashMap<>();
ex.getConstraintViolations().forEach(constraintViolation -> {
String queryParamPath = constraintViolation.getPropertyPath().toString();
log.debug("queryParamPath = {}", queryParamPath);
String queryParam = queryParamPath.contains(".") ?
queryParamPath.substring(queryParamPath.indexOf(".") + 1) :
queryParamPath;
String errorMessage = constraintViolation.getMessage();
Collection<String> perQueryParamErrors = errors.getOrDefault(queryParam, new ArrayList<>());
perQueryParamErrors.add(errorMessage);
errors.put(queryParam, perQueryParamErrors);
});
return validationException(new ValidationException("queryParameter", errors));
}
ValidationException stuff is my custom way of dealing with validation errors, in a nutshell, it produces an error DTO, which will be serialized into JSON with all the validation error messages.
Add a custom message to #Min annotation like this
#Min(value=0, message="page-number must be equal or greater than {value}")
Right now, you cannot do it (well, except if you define a custom message for each annotation but I suppose that's not what you want).
Funnily enough, someone worked recently on something very similar: https://github.com/hibernate/hibernate-validator/pull/1029 .
This work has been merged to the master branch but I haven't released a new 6.1 alpha containing this work yet. It's a matter of days.
That being said, we had properties in mind and now that you ask that, we should probably generalize that to more things, method parameters included.
Now that we have the general idea, it shouldn't be too much work to generalize it, I think.
I'll discuss this with the contributor and the rest of the team and get back to you.
I don't think getting the name of the query parameter is possible but would like to be proven wrong if somebody knows a way.
As Dmitry Bogdanovich says, having a custom message is the easiest and only way I know how to do something close to what you need. If you say you don't want to clutter your code with these messages, you can just do this:
Add a ValidationMessages.properties file in your resources folder. Here you can just say:
page_number.min=page-number must be equal or greater than {value}
Now you can use the annotation and write:
#Min(value = 0, message = "{page_number.min}")
This way you have a single source to change anything about the message when needed.

Spring JPA REST sort by nested property

I have entity Market and Event. Market entity has a column:
#ManyToOne(fetch = FetchType.EAGER)
private Event event;
Next I have a repository:
public interface MarketRepository extends PagingAndSortingRepository<Market, Long> {
}
and a projection:
#Projection(name="expanded", types={Market.class})
public interface ExpandedMarket {
public String getName();
public Event getEvent();
}
using REST query /api/markets?projection=expanded&sort=name,asc I get successfully the list of markets with nested event properties ordered by market's name:
{
"_embedded" : {
"markets" : [ {
"name" : "Match Odds",
"event" : {
"id" : 1,
"name" : "Watford vs Crystal Palace"
},
...
}, {
"name" : "Match Odds",
"event" : {
"id" : 2,
"name" : "Arsenal vs West Brom",
},
...
},
...
}
}
But what I need is to get list of markets ordered by event's name, I tried the query /api/markets?projection=expanded&sort=event.name,asc but it didn't work. What should I do to make it work?
Based on the Spring Data JPA documentation 4.4.3. Property Expressions
... you can use _ inside your method name to manually define traversal points...
You can put the underscore in your REST query as follows:
/api/markets?projection=expanded&sort=event_name,asc
Just downgrade spring.data.‌​rest.webmvc to Hopper release
<spring.data.jpa.version>1.10.10.RELEASE</spring.data.jpa.ve‌​rsion>
<spring.data.‌​rest.webmvc.version>‌​2.5.10.RELEASE</spri‌​ng.data.rest.webmvc.‌​version>
projection=expanded&sort=event.name,asc // works
projection=expanded&sort=event_name,asc // this works too
Thanks #Alan Hay comment on this question
Ordering by nested properties works fine for me in the Hopper release but I did experience the following bug in an RC version of the Ingalls release.bug in an RC version of the Ingalls release. This is reported as being fixed,
jira issue - Sorting by an embedded property no longer works in Ingalls RC1
BTW, I tried v3.0.0.M3 that reported that fixed but not working with me.
We had a case when we wanted to sort by fields which were in linked entity (it was one-to-one relationship). Initially, we used example based on https://stackoverflow.com/a/54517551 to search by linked fields.
So the workaround/hack in our case was to supply custom sort and pageable parameters.
Below is the example:
#org.springframework.data.rest.webmvc.RepositoryRestController
public class FilteringController {
private final EntityRepository repository;
#RequestMapping(value = "/entities",
method = RequestMethod.GET)
public ResponseEntity<?> filter(
Entity entity,
org.springframework.data.domain.Pageable page,
org.springframework.data.web.PagedResourcesAssembler assembler,
org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler entityAssembler,
org.springframework.web.context.request.ServletWebRequest webRequest
) {
Method enclosingMethod = new Object() {}.getClass().getEnclosingMethod();
Sort sort = new org.springframework.data.web.SortHandlerMethodArgumentResolver().resolveArgument(
new org.springframework.core.MethodParameter(enclosingMethod, 0), null, webRequest, null
);
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
Example example = Example.of(entity, matcher);
Page<?> result = this.repository.findAll(example, PageRequest.of(
page.getPageNumber(),
page.getPageSize(),
sort
));
PagedModel search = assembler.toModel(result, entityAssembler);
search.add(linkTo(FilteringController.class)
.slash("entities/search")
.withRel("search"));
return ResponseEntity.ok(search);
}
}
Used version of Spring boot: 2.3.8.RELEASE
We had also the repository for Entity and used projection:
#RepositoryRestResource
public interface JpaEntityRepository extends JpaRepository<Entity, Long> {
}
Your MarketRepository could have a named query like :
public interface MarketRepository exten PagingAndSortingRepository<Market, Long> {
Page<Market> findAllByEventByName(String name, Page pageable);
}
You can get your name param from the url with #RequestParam
This page has an idea that works. The idea is to use a controller on top of the repository, and apply the projection separately.
Here's a piece of code that works (SpringBoot 2.2.4)
import ro.vdinulescu.AssignmentsOverviewProjection;
import ro.vdinulescu.repository.AssignmentRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.PagedModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RepositoryRestController
public class AssignmentController {
#Autowired
private AssignmentRepository assignmentRepository;
#Autowired
private ProjectionFactory projectionFactory;
#Autowired
private PagedResourcesAssembler<AssignmentsOverviewProjection> resourceAssembler;
#GetMapping("/assignments")
public PagedModel<EntityModel<AssignmentsOverviewProjection>> listAssignments(#RequestParam(required = false) String search,
#RequestParam(required = false) String sort,
Pageable pageable) {
// Spring creates the Pageable object correctly for simple properties,
// but for nested properties we need to fix it manually
pageable = fixPageableSort(pageable, sort, Set.of("client.firstName", "client.age"));
Page<Assignment> assignments = assignmentRepository.filter(search, pageable);
Page<AssignmentsOverviewProjection> projectedAssignments = assignments.map(assignment -> projectionFactory.createProjection(
AssignmentsOverviewProjection.class,
assignment));
return resourceAssembler.toModel(projectedAssignments);
}
private Pageable fixPageableSort(Pageable pageable, String sortStr, Set<String> allowedProperties) {
if (!pageable.getSort().equals(Sort.unsorted())) {
return pageable;
}
Sort sort = parseSortString(sortStr, allowedProperties);
if (sort == null) {
return pageable;
}
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
}
private Sort parseSortString(String sortStr, Set<String> allowedProperties) {
if (StringUtils.isBlank(sortStr)) {
return null;
}
String[] split = sortStr.split(",");
if (split.length == 1) {
if (!allowedProperties.contains(split[0])) {
return null;
}
return Sort.by(split[0]);
} else if (split.length == 2) {
if (!allowedProperties.contains(split[0])) {
return null;
}
return Sort.by(Sort.Direction.fromString(split[1]), split[0]);
} else {
return null;
}
}
}
From Spring Data REST documentation:
Sorting by linkable associations (that is, links to top-level resources) is not supported.
https://docs.spring.io/spring-data/rest/docs/current/reference/html/#paging-and-sorting.sorting
An alternative that I found was use #ResResource(exported=false).
This is not valid (expecially for legacy Spring Data REST projects) because avoid that the resource/entity will be loaded HTTP links:
JacksonBinder
BeanDeserializerBuilder updateBuilder throws
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of ' com...' no String-argument constructor/factory method to deserialize from String value
I tried activate sort by linkable associations with help of annotations but without success because we need always need override the mappPropertyPath method of JacksonMappingAwareSortTranslator.SortTranslator detect the annotation:
if (associations.isLinkableAssociation(persistentProperty)) {
if(!persistentProperty.isAnnotationPresent(SortByLinkableAssociation.class)) {
return Collections.emptyList();
}
}
Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface SortByLinkableAssociation {
}
At your project incluide #SortByLinkableAssociation at linkable associations that whats sort.
#ManyToOne(fetch = FetchType.EAGER)
#SortByLinkableAssociation
private Event event;
Really I didn't find a clear and success solution to this issue but decide to expose it to let think about it or even Spring team take in consideration to include at nexts releases.

validating json using constraints annotations is not working

I am trying to validate a json when deserialize it using constraints annotations but it doesn't seem to work. for example
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class SimpleFooRequest {
#NotNull
#JsonProperty(value = "count")
#Min(value = 100,message = "must be min 10")
private Integer count;
#NotNull
private String name;
public String getName() {return name; }
public void setName(String name) { this.name = name; }
public Integer getCount() {return count;}
public void setCount(Integer count) {this.count = count;}
}
when trying to unmarshal the json
#Test
public void testFooRequest() throws Exception
{
String fooJson = {"count":-10}
ObjectMapper mapper = new ObjectMapper()
SimpleFooRequest request = mapper.readValue(fooJson,SimpleFooRequest.class);
assert request.getCount().equals(-10);//expected fail but it pass !!!
}
it seems to pass although I expected that the assertion will fail due to the count min value limitation . I also tried to put the annotations on the setters but got same results
I have <mvc:annotation-driven /> and hibernate-validator-4.3.1.Final.jar in my dependencies
I know that I can create custom validator but I thought that for simple validations I can use the annotations .
what do I need to do to make it work ?
Annotations are just meta-data. They're not programs that can perform any validation. The validator is the program that will make use of the meta-data. In a container, the container will manage the validator. In basic unit tests or standalones, you will need to manage the validator yourself.
That being said, even a validator cannot stop the object from being created/populated (as you seem to be asserting in your test). It just validates that the value is valid. If not, the validator throws an exception and/or lists some constraint violations. Either the container, or you should handle what to do with the exceptions/violations.
And AFAIK, there's no ObjectMapper configuration for automatic JSR-303 support while deserializing
A better unit test, might look something like
/* ----- Using the Hibernate Validator implementation ----- */
#Test
public void testInvalidCount() throws Exception {
final String json = "{\"count\":-10, \"name\":\"Stack\"}";
ObjectMapper mapper = new ObjectMapper();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
SimpleFooRequest request = mapper.readValue(
json, SimpleFooRequest.class);
Set<ConstraintViolation<SimpleFooRequest>> violations
= validator.validate(request);
Assert.assertEquals(1, violations.size());
Assert.assertEquals("must be min 10",
violations.iterator().next().getMessage());
}
See Getting started with Hibernate Validator for some more examples
How about validating at the setter method?
Jackson calls your setters if exists...

Marking a pair of #ManyToOne related properties as unique. Possible?

I have the typical example, where a POST has many TAGS, and a TAG has many POSTs.
Instead of using a typical #ManyToMany, I use a domain object in the middle, called TAGPOST, which also allows me to have useful data in there, such as when a post was tagged with a given tag, etc. Each POST, and TAG resp, is in a #OneToMany relationship with a TAGPOST.
The specific requirement is that a post cannot have the same tag included twice, therefore the TAGPOST.post and TAGPOST.tag pair must always be unique. Normally, I would do that by making a composite primary key pair in the table, responsible for storing TAGPOST objects.
AFAIK, there is no way to express this unique constraint. I have marked jpa.ddl=update, which means that every time I move the application to a new environment, I will have to go and manually fix this in the DB. This is very inconvenient, and error prone, especially when unit testing, because then the database is created and dropped more or less in every iteration.
I was even thinking to do the check manually on #PrePersist, or even move the check in a business layer, say, create a PostService.
What do I do? Am I missing something that Play has by default? Some clever annotation to express the uniqueness of the #ManyToOne properties of the TAGPOST class?
FYI: I am using Play 1.2.5
EDIT: The TAGPOST class looks like this:
#Entity
public class TagPost extends Model {
#ManyToOne
public Tag tag;
#ManyToOne
public Post post;
public Date dateAdded;
...
}
I wrote a custom Check for db uniqueness. Maybe you should customize it.
DBUnique.java
package models.check;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import net.sf.oval.configuration.annotation.Constraint;
import play.db.jpa.GenericModel;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER })
#Constraint(checkWith = DbUniqueCheck.class)
public #interface DBUnique {
String message() default DbUniqueCheck.mes;
Class<? extends GenericModel> modelClass();
String field() default ""; // field name will be used
}
DbUniqueCheck.java
package models.check;
import net.sf.oval.Validator;
import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
import net.sf.oval.context.FieldContext;
import net.sf.oval.context.OValContext;
import org.apache.commons.lang.StringUtils;
import play.db.jpa.GenericModel.JPAQuery;
#SuppressWarnings("serial")
public class DbUniqueCheck extends AbstractAnnotationCheck<DBUnique> {
final static String mes = "validation.dbunique";
DBUnique dbUnique;
#Override
public void configure(DBUnique dBUnique) {
this.dbUnique = dBUnique;
setMessage(dBUnique.message());
}
public boolean isSatisfied(Object validatedObject, Object value, OValContext context, Validator validator) {
try {
String field = dbUnique.field();
if (field == null || field.isEmpty()) {
field = ((FieldContext) context).getField().getName();
}
JPAQuery q = (JPAQuery) dbUnique.modelClass().getMethod("find", String.class, Object[].class)
.invoke(null, "by" + StringUtils.capitalize(field), new Object[] { value });
return q.first() == null;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
usage : link to gist
it simply checks the given field for given class instance is unique in db. Maybe you should make something like these..

Categories

Resources