Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction - spring

I have a error when I try to insert a user in the database.
I made i custom annotation which verify if the password match with the confirmation password it works when the field not matches , but when the passowrd matches i have this error :
This is my code This is my field match #Annotation :
package mereuta.marian.tennis01.annotations;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Constraint(validatedBy = FieldsValueMatchValidator.class)
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface FieldsValueMatch {
String message() default "Fields values don't match!";
String field();
String fieldMatch();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#interface List {
FieldsValueMatch[] value();
}
}
This is the Field Validator :
package mereuta.marian.tennis01.annotations;
import mereuta.marian.tennis01.model.Utilisateur;
import org.springframework.beans.BeanWrapperImpl;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class FieldsValueMatchValidator implements ConstraintValidator<FieldsValueMatch , Object> {
private String field;
private String fieldMatch;
#Override
public void initialize(FieldsValueMatch fieldsValueMatch) {
this.field=fieldsValueMatch.field();
this.fieldMatch=fieldsValueMatch.fieldMatch();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
Object fieldValue = new BeanWrapperImpl(value)
.getPropertyValue(field);
Object fieldMatchValue = new BeanWrapperImpl(value)
.getPropertyValue(fieldMatch);
if (fieldValue != null) {
return fieldValue.equals(fieldMatchValue);
} else {
return fieldMatchValue == null;
}
}
}
This is my Entity :
#FieldsValueMatch(field = "password", fieldMatch = "confirmPassword",
message = "Password do not match!")
#Entity(name = "utilisateurs")
public class Utilisateur {
#Id #GeneratedValue
#Column(name = "id_utilisateur")
private Integer id;
#NotNull
#Size(min = 4, max = 255)
#Column(name = "password")
private String password;
#Transient
#NotNull
private String confirmPassword;
This is the Controller :
#PostMapping("/addUtilisateur")
public String addUtilisateur(#Valid #ModelAttribute("utilisateur") Utilisateur utilisateur, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors() ) {
model.addAttribute("message", "le mot de passe ne correspond pas");
return "utilisateur/formRegister";
}
utilisateurMetier.creerUtilisateur(utilisateur);
return "utilisateur/utilisateurAjoute";
}
And finally the View :
<div class="container">
<form id="contact" th:action="#{addUtilisateur}" method="post" th:object="${utilisateur}">
<h3>Créer compte</h3>
<input placeholder="password" type="password" th:field="*{password}" tabindex="2" required/>
<span class="text text-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>
</fieldset>
<fieldset>
<input placeholder="password" type="password" th:field="*{confirmPassword}" tabindex="2" required/>
<span class="text text-danger" th:if="${#fields.hasErrors('confirmPassword')}"
th:errors="*{confirmPassword}" th:text="${message}"></span>
</fieldset>
For the custom annotations I find a example on : https://www.baeldung.com/spring-mvc-custom-validator
#Override
public void creerUtilisateur(Utilisateur utilisateur) {
Role role;
float credit = 0;
boolean actif = true;
role = roleRepository.getOne(3);
System.out.println(role);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
utilisateur.setPassword(encoder.encode(utilisateur.getPassword()));
utilisateur.setRole(role);
utilisateur.setCredit(credit);
utilisateur.setActif(actif);
utilisateurRepository.save(utilisateur);
}
Thank you in advance for your help

As already mentioned the ContraintViolationException is thrown inside the 'creerUtilisateur' method. So the validation of your Utilisateur bean at the time it's passed to your Spring MVC controller method (addUtilisateur(#Valid #ModelAttribute("utilisateur")...) works correctly when both fields (password, confirmPassword) have the same value. Later, you encode the password and change the value of your Utilitsateur's 'password' instance variable:
utilisateur.setPassword(encoder.encode(utilisateur.getPassword()));
Now, 'password' and 'passwordConfirm' are not equal anymore! When persisting this entity in utilisateurRepository.save(utilisateur); JPA will again bean-validate your entity before saving it to database (pre-persist). The validation gets automatically executed when JPA/Hibernate triggers a pre-persist, pre-update or pre-remove lifecycle event. And then the ContraintViolationException is thrown!
In your creerUtilisateur method simply set the encoded password for both, 'password' and 'passwordConfirm', instance variables and hereby ensure that they still pass your equality check in FieldsValueMatchValidator.isValid(Object value, ConstraintValidatorContext context):
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
final String encodedPassword = encoder.encode(utilisateur.getPassword());
utilisateur.setPassword(encodedPassword);
utilisateur.setPasswordConfirm(encodedPassword);
//...
utilisateurRepository.save(utilisateur);
You could also try to customize JPA's bean validation behaviour:
https://www.thoughts-on-java.org/automatically-validate-entities-with-hibernate-validator/
Disable Hibernate validation for Merge/Update

Related

Thymeleaf error validation: spring boot starter validation always displaying errors

build.gradle file
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
implementation 'io.hypersistence:hypersistence-utils-hibernate-60:3.1.1'
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '3.0.1'
}
UserModel file
import jakarta.persistence.*;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.*;
#Entity
#Table(name = "users")
public class UserModel implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotEmpty()
private String username;
#NotEmpty(message = "Username not be Empty!")
#Size(min = 6, max = 50)
private String password;
private List<String> authorities;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;
public UserModel() {}
public UserModel(String username, String password, List<String> authorities,boolean isAccountNonExpired, boolean isAccountNonLocked, boolean isCredentialsNonExpired, boolean isEnabled) {
this.username = username;
this.password = password;
this.authorities = authorities;
this.isAccountNonExpired = isAccountNonExpired;
this.isAccountNonLocked = isAccountNonLocked;
this.isCredentialsNonExpired = isCredentialsNonExpired;
this.isEnabled = isEnabled;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorityList = new ArrayList<>();
return authorityList;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}
#Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
#Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}
#Override
public boolean isEnabled() {
return isEnabled;
}
}
Controller file
import com.example.demo.user.UserModel;
import com.example.demo.user.UserModelRepository;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
#Controller
public class TestController {
private final UserModelRepository userModelRepository;
public TestController(UserModelRepository userModelRepository) {
this.userModelRepository = userModelRepository;
}
#GetMapping("/register")
public String showAddUserForm(UserModel userModel) {
return "register";
}
#PostMapping("/register")
public String addUser(#Valid UserModel userModel, BindingResult result, Model model) {
if (result.hasErrors()) {
return "register";
}
System.out.println(userModel);
userModelRepository.save(userModel);
model.addAttribute("users", userModelRepository.findAll());
return "home"; // TODO - This will be executed inside of our HTML
}
}
HTML file named: 'register.html'
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Register</title>
<style>
.error {
color: red;
}
</style>
</head>
<body>
<!-- ${user} Object -->
<!-- *{username} Object attribute -->
<form th:action="#{/register}" th:object="${userModel}" method="post" class="form">
<div>
<input type="text" th:field="*{username}" id="username" placeholder="username" name="username">
<!-- <p th:if="${#fields.hasErrors('username')}" th:errorclass="error" th:errors="*{username}" /> -->
<p th:if="${#fields.hasErrors('username')}" th:errorclass="error" th:errors="*{username}"> </p>
</div>
<div>
<h2> Password </h2>
<input type="password" th:field="*{password}" >
<ul>
<li th:each="error : ${#fields.errors('password')}" th:text="${error}" class="error">
</ul>
</div>
<div th:if="${#fields.hasAnyErrors()}">
<ul>
<li th:each="error : ${#fields.allErrors()}" th:text="${error}"></li>
</ul>
</div>
<input type="submit" value="Add me">
</form>
</body>
</html>
Problem
Whenever i fill in my form it always displays errors whenever i click submit. Regardless if they're filled in or not.
Why is it always displaying an error?
I'm guessing that somehow, although i'm trying to render a condition, it always turns to 'false'.
I just can't see what i'm doing wrong.
Tutorial i'm following: https://www.baeldung.com/spring-thymeleaf-error-messages
Result
The problem is your UserModel it doesn't provide setter methods and thus nop data binding will (or even can) take place.
To fix either add the setter methods or tell Spring to use direct field binding.
By adding the following method to your controller you can achieve direct field binding/access.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.initDirectFieldAccess();
}
The code above will use fields instead of properties (getter/setter pair) to do the binding. With that you can leave your UserModel unmodified and still have data binding applied.

Non bean property throwing Bean property is not readable or has an invalid getter method

So my issue is a bit different from others. The error that it is throwing is not a field name but rather the input in a form. I've never encountered this error before
Error. The contents of '' is what I key for password
org.springframework.beans.NotReadablePropertyException: Invalid
property 'Yijian#123' of bean class [com.Alex.UserPackage.User]: Bean
property 'Yijian#123' is not readable or has an invalid getter method:
Does the return type of the getter match the parameter type of the
setter? at
org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:622)
~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE] at
org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:612)
~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE] at
org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:104)
~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
Entity class
#Entity
#ValidPassword
public class User {
#Pattern(regexp="[a-zA-Z]+", message = "Enter letters only!")
private String firstName;
#Pattern(regexp="[a-zA-Z]+", message = "Enter letters only!")
private String lastName;
private String password;
private String matchingPassword;
private String passportNumber;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMatchingPassword() {
return matchingPassword;
}
public void setMatchingPassword(String matchingPassword) {
this.matchingPassword = matchingPassword;
}
}
#ValidPassword custom annotation. The error started to occur after I used the getters for password and matchingPassword
private String message;
#Override
public boolean isValid(User user, ConstraintValidatorContext context) {
String password = user.getPassword();
String matchingPassword = user.getMatchingPassword();
if (password== null || matchingPassword == null) {
return false;
}
System.out.println("PASSWORDS: " + password + matchingPassword);
boolean flag = Pattern.matches("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!#$%^&*-]).{8,}$", password);
boolean flag1 = password.equals(matchingPassword);
if ( !flag1 ) {
message = "Passwords do not match!";
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(password).addConstraintViolation();
return flag && flag1;
}
//Show default message if no special message is set
#Override
public void initialize(ValidPassword validPassword) {
message = validPassword.message();
}
Form password portion
<div class = "row">
<div class="col-sm-6 form-group">
<label>Password : </label> <Input type="password"
th:field="*{password}" th:required="required" class="form-control" />
<p th:if="${#fields.hasErrors('password')}"
th:errors="*{password}" class="alert alert-danger"></p>
</div>
You are passing the property value instead of the property name to addPropertyNode(password).
Replace the following:
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(password).addConstraintViolation();
with:
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode("password").addConstraintViolation();

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'search' available as request attribute

I'm new to Spring MVC and I have an error with a form validation and I don't know why.
This is the model:
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.Size;
import org.springframework.stereotype.Component;
#Component
public class Search implements Serializable {
#Size(max = 20)
private String userInput;
#Size(max = 10)
private String ascending;
#Size(max = 10)
private String descending;
#Temporal(TemporalType.DATE)
private Date fromDate;
#Temporal(TemporalType.DATE)
private Date toDate;
#Size(max=100)
private String genres;
public String getGenres() {
return genres;
}
public void setGenres(String genres) {
this.genres = genres;
}
public String getUserInput() {
return userInput;
}
public void setUserInput(String userInput) {
this.userInput = userInput;
}
public Date getFromDate() {
return fromDate;
}
public void setFromDate(Date fromDate) {
this.fromDate = fromDate;
}
public Date getToDate() {
return toDate;
}
public void setToDate(Date toDate) {
this.toDate = toDate;
}
}
Here is the form:
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>
<sf:form action="/newSearch" method="post" modelAttribute="search">
<sf:input path="userInput" type="text" class="input_style" id="userInput" />
<button class="search_button"><img class="search_icon" src="resources/img/search/search_icon.png" /></button>
<sf:select id="genres" path="genres" multiple="multiple">
</sf:select>
<sf:input id="fromDate" path="fromDate" />
<sf:input id="toDate" path="toDate" type="text" />
<sf:input id="ascending" path="ascending" type="radio" checked="checked" />
<sf:input id="descending" path="descending" type="radio" />
</sf:form>
and here is the Controller:
#RequestMapping(value = "/newSearch", method = RequestMethod.POST)
public String getSearch(#Valid Search search, BindingResult result, Model m) {
if(result.hasErrors()) {
return "home";
}
System.out.println("----------------Search--------------------");
System.out.println(search.getGenres());
System.out.println(search.getUserInput());
return "search";
}
The error is:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'search' available as request attribute
Add #ModelAttribute("search") before #Valid making the method's signature look like
public String getSearch(#ModelAttribute("search") #Valid Search search, BindingResult result, Model m)
Also try
<sf:form action="/newSearch" method="post" commandName="search">
instead of
<sf:form action="/newSearch" method="post" modelAttribute="search">

Spring MVC form validation

my problem is that I have a form which has html select element with some choosing option value & I want to validate those value using :
org.hibernate.validator.constraints
or
javax.validation.constraints
annotations. here is my select element:
<select name="status" id="tbSelect">
<option value="ACTIVE">ACTIVE</option>
<option value="LISTEN">LISTEN</option>
<option value="DOWN">DOWN</option>
</select>
how I can for example validate the value of the options(DOWN,LISTEN,ACTIVE) inside the select element by using the annotation validators which I mention above?
my form is like this :
<form:form action="../agents/add" method="POST" commandName="myAgent">
<form:select id="tbSelect" path="state">
<form:option value="ACTIVE" path="state">ACTIVE</form:option>
<form:option value="LISTEN" path="state">LISTEN</form:option>
<form:option value="DOWN" path="state">DOWN</form:option>
</form:select>
I have defined my controller method like this:
#RequestMapping(value = "agents/add", method = RequestMethod.POST)
public String addAgentSubmit(#ModelAttribute("myAgent") #Valid final AgentValidator agent, BindingResult result, RedirectAttributes redirect) {
if (result.hasErrors()) {
return "admin/agent/add";
}
...
}
and I also define a ModelAttribute like this:
#ModelAttribute("myAgent")
public AgentValidator getLoginForm() {
return new AgentValidator();
}
Here is my AgentValidator class also:
public class AgentValidator {
#NotEmpty(message = "your state can not be empty !")
private String state;
Since your state field looks more like an enumeration, first of all I would recommend to change state field into enum, let Spring MVC to bind that field and use only #NotNull annotation:
public class AgentValidator {
#NotNull(message = "your state can not be empty !")
private AgenState state;
Where AgentState is:
public enum AgentState {
DOWN,LISTEN,ACTIVE
}
But if for certain reasons you can't change your model, then you may use custom constraints.
Particular you need to create your annotation AgentStateConstraint:
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = AgentStateConstraintValidator.class)
#Documented
public #interface AgentStateConstraint {
String message() default "Some message";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Then you need to create validator AgentStateConstraintValidator:
public class AgentStateConstraintValidator implements ConstraintValidator<AgentStateConstraint, String> {
//Accepted values
private static final Set<String> ACCEPTED_VALUES = new HashSet<String>(
Arrays.asList(
"DOWN",
"LISTEN",
"ACTIVE"
)
);
public void initialize(AgentStateConstraint constraintAnnotation) {
}
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
return ACCEPTED_VALUES.contains(object);
}
}

Cross field validation with HibernateValidator displays no error messages

I'm validating two fields, "password" and "confirmPassword" on the form for equality using HibernateValidator as specified in this answer. The following is the constraint descriptor (validator interface).
package constraintdescriptor;
import constraintvalidator.FieldMatchValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = FieldMatchValidator.class)
#Documented
public #interface FieldMatch
{
String message() default "{constraintdescriptor.fieldmatch}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* #return The first field
*/
String first();
/**
* #return The second field
*/
String second();
/**
* Defines several <code>#FieldMatch</code> annotations on the same element
*
* #see FieldMatch
*/
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
public #interface List{
FieldMatch[] value();
}
}
The following is the constraint validator (the implementing class).
package constraintvalidator;
import constraintdescriptor.FieldMatch;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.apache.commons.beanutils.BeanUtils;
public final class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object>
{
private String firstFieldName;
private String secondFieldName;
public void initialize(final FieldMatch constraintAnnotation) {
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
//System.out.println("firstFieldName = "+firstFieldName+" secondFieldName = "+secondFieldName);
}
public boolean isValid(final Object value, final ConstraintValidatorContext cvc) {
try {
final Object firstObj = BeanUtils.getProperty(value, firstFieldName );
final Object secondObj = BeanUtils.getProperty(value, secondFieldName );
//System.out.println("firstObj = "+firstObj+" secondObj = "+secondObj);
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
}
catch (final Exception e) {
System.out.println(e.toString());
}
return true;
}
}
The following is the validator bean which is mapped with the JSP page (as specified commandName="tempBean" with the <form:form></form:form> tag).
package validatorbeans;
import constraintdescriptor.FieldMatch;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
#FieldMatch.List({
#FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match", groups={TempBean.ValidationGroup.class})
})
public final class TempBean
{
#NotEmpty(groups={ValidationGroup.class}, message="Might not be left blank.")
private String password;
#NotEmpty(groups={ValidationGroup.class}, message="Might not be left blank.")
private String confirmPassword;
public interface ValidationGroup {};
//Getters and setters
}
UPDATE
It's all working correctly and does the validation intended. Just one thing remains is to display the specified error message above the TempBean class within #FieldMatch is not being displayed i.e only one question : how to display error messages on the JSP page when validation violation occurs?
(the annotation #NotEmpty on both of the fields password and confirmPassword in the TempBean class works and displays the specified messages on violation, the thing is not happening with #FieldMatch).
I'm using validation group based on this question as specified in this blog and it works well causing no interruption in displaying error messages (as it might seem to be).
On the JSP page these two fields are specified as follows.
<form:form id="mainForm" name="mainForm" method="post" action="Temp.htm" commandName="tempBean">
<form:password path="password"/>
<font style="color: red"><form:errors path="password"/></font><br/>
<form:password path="confirmPassword"/>
<font style="color: red"><form:errors path="confirmPassword"/></font><br/>
</form:form>
Could you try your isValid method to be like this? (this is certainly working for me in live project):
public boolean isValid(final Object value, final ConstraintValidatorContext cvc){
boolean toReturn = false;
try{
final Object firstObj = BeanUtils.getProperty(value, firstFieldName );
final Object secondObj = BeanUtils.getProperty(value, secondFieldName );
//System.out.println("firstObj = "+firstObj+" secondObj = "+secondObj);
toReturn = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
}
catch (final Exception e){
System.out.println(e.toString());
}
//If the validation failed
if(!toReturn) {
cvc.disableDefaultConstraintViolation();
//In the initialiaze method you get the errorMessage: constraintAnnotation.message();
cvc.buildConstraintViolationWithTemplate(errorMessage).addNode(firstFieldName).addConstraintViolation();
}
return toReturn;
}
Also I see that you are implementing the ConstraintValidator interface with an Object, literally. It should be the backing object that you have from your form:
tempBean // the one that you specify in the commandName actually.
So you implementation should like this:
implements ConstraintValidator<FieldMatch, TempBean>
This is probably not the issue here, but as a future reference, this is how it should be.
UPDATE
Inside your FieldMatch interface/annotation you have two methods : first and second, add one more called errorMessage for example:
Class<? extends Payload>[] payload() default {};
/**
* #return The first field
*/
String first();
/**
* #return The second field
*/
String second();
/**
#return the Error Message
*/
String errorMessage
Look inside you method from the Validation class - you are getting the first and second field names there., so just add the errorMessage, like this for example:
private String firstFieldName;
private String secondFieldName;
//get the error message name
private String errorMessagename;
public void initialize(final FieldMatch constraintAnnotation)
{
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
errorMessageNAme = constraintAnnotation.errorMessage();
//System.out.println("firstFieldName = "+firstFieldName+" secondFieldName = "+secondFieldName);
}
Inside isValida get it, the same way you do for first and second field name and use it.

Resources