Spring MVC bean validation issue - java

I have 2 bean as follows. Student bean validation is fine. But the inner bean Address validation has no effect at all. Although there is no validation for Address.pincode, but comes validation message as studentAddress.pincode Value must be an integer.
Why is it happening? Well I'm new in Spring, pleas explain in details. Thanks in advance!
public class Student {
#Size(min=2, max=30)
private String studentName;
#Size(min=3, max=30)
private String studentHobby;
#NotNull
private int studentMobile;
#Past
private Date studentDOB;
private ArrayList<String> studentSkills;
private Address studentAddress;
}
public class Address {
#Size(min=4, max=50)
private String country;
#Size(min=4, max=50)
private String city;
private String street;
private int pincode;
}

If you are using using Hibernate Validator 4.0.0+, you can do a "cascade validation" like so :
#Valid
private Address studentAddress;

Related

Is which the best request parameter mapping strategy in Spring Framework?

I am Java web developer, usually develop Spring MVC.
I have been using #RequestMapping or #RequestParam for mapping to hashMap at Controller.
It is a terrible way. I should always cast type when using value.
But nowadays I try to use #ModelAttribute to write clean code at Controller.
However, there are some problem.
case 1) make DTO for each EndPoint.
We can make DTO for each EndPoint, but DTO will have many duplicated property.
#Getter
#Setter
#ToString
class GetUserInfoDTO {
private String id;
private String name;
}
#Getter
#Setter
#ToString
class PostUserInfoDTO {
private String name;
private Integer age;
private String address;
private String gender;
private String email;
private Date joinDate;
}
in controller,
#GetMapping("/user")
public ResultDTO getUserInfo (#ModelAttribute GetUserInfoDTO){
...
return ResultDTO;
}
#PostMapping("/user")
public ResultDTO postUserInfo (#ModelAttribute PostUserInfoDTO){
...
return ResultDTO;
}
In this case, we can apply independent validation strategy for each End-Point.
for example..
#Getter
#Setter
#ToString
class GetUserInfoDTO {
#NotNull
private String id;
private String name;
}
#Getter
#Setter
#ToString
class PostUserInfoDTO {
#NotNull
private String name;
#NotNull
private Integer age;
#NotEmpty
private String address;
private String gender;
private String email;
private Date joinDate;
}
like this.
But so many model classes made, and so many duplicated property exists.
case 2. make common DTO for each Controller.
We can make DTO for each Controller, and reuse them.
#Getter
#Setter
#ToString
class UserInfoDTO {
private String id;
private String name;
private Integer age;
private String address;
private String gender;
private String email;
private Date joinDate;
}
#GetMapping("/user")
public ResultDTO getUserInfo (#ModelAttribute UserInfoDTO){
//I want only id, name
...
return ResultDTO;
}
#PostMapping("/user")
public ResultDTO postUserInfo (#ModelAttribute UserInfoDTO){
...
return ResultDTO;
}
But In this case, we can only pass specific properties.
If someone send other parameter than id and name, we can't notice. ( 400 error not occur )
Code assistance can't recommend us specific properties that use at single end-point.
I don't like these cases.
In first case, I should make so many models and It's management will be so hard.
Second case, unnecessary properties exists and it hard to validate for each end-point.
Which way is the best?
Or Can you recommend another way for mapping request parameter to model object?

How to set Nested Object with Mongotemplate Spring

I'm still new in mongodb, If I have a class like below and I want to set a property Role which is Object Type Property, how can I achieve it ? please check the class below
#Document(collection="User")
public class UserBean {
#Id
private String id;
private String userName;
private String password;
private RoleBean role;
}
#Document(collection="Role")
public class RoleBean {
#Id
private String id;
private String roleID;
private String roleName;
}
I need to set the UserBean's role property. So what is the best way to achieve it? Thanks.
Spring Mongo Template not saving the list of custom objects to MongoDb please see if this quation is already answered, or answer satisfies your requirements.

How to map different country/state codes to a base entity via JPA annotations?

I have an entity with the following fields:
private Date dateOfBirth;
private String cityOfBirth;
private Long birthStateCodeId;
private Long birthCountryCodeId;
private Boolean isUSCitizen;
private Long citizenshipCountryCodeId;
private String address1;
private String address2;
private String addressCity;
private Long addressStateCodeId;
private Long addressCountryCodeId;
private String postalCode;
As you can see from the above snippet, I have
2 properties (birthStateCodeId, addressStateCodeId) where I use a state code from a StateCodes table, and
3 properties (birthCountryCodeId, citizenshipCountryCodeId, and addressCountryCodeId) where I use a country code from a CountryCodes table.
Using JPA (with Hibernate as persistence provider), how do I map the above 5 properties (2 state codes and 3 country codes) to the two separate tables StateCodes and CountryCodes?
You could achieve it like this:
#Entity
public class PersonIdentification {
// primary key
#Id // and other annotations, see JPA Spec or tutorials
private long id;
// regular attributes
private Date dateOfBirth;
private String cityOfBirth;
private Boolean isUSCitizen;
private String address1;
private String address2;
private String addressCity;
private String postalCode;
#ManyToOne
private StateCode birthStateCode;
#ManyToOne
private StateCode addressStateCode;
#ManyToOne
private CountryCode birthCountryCode;
#ManyToOne
private CountryCode addressCountryCode;
#ManyToOne
private CountryCode citizenshipCountryCode;
// setter & getter methods as needed...
}
Next, define entity classes for both "Code" types as such:
#Entity
public class StateCode {
// primary key
#Id // and other annotations, see JPA Spec or tutorials
private long id;
private String code;
private String stateName;
// other attributes of interest
// setter & getter methods as needed...
}
#Entity
public class CountryCode {
// primary key
#Id // and other annotations, see JPA Spec or tutorials
private long id;
private String code;
private String countryName;
// other attributes of interest
// setter & getter methods as needed...
}
To reduce CnP code (as with the generic aspect of primary key handling (#Id) you can check this answer. It gives you detailed hints on how handle such cases more efficiently by introducing an AbstractEntity via the #MappedSuperClass annotation.
Hope it helps

Custom bean validation

I'm developing an application using spring-boot. I want to validate the user bean using JSR annotation. The problem is that I have some fields that depend on the value of others. For example when status="user_pr" I have to make the address, county, and phoneNumber as mandatory.
this my bean:
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
#NotNull(message = "required")
private String status;
#JsonProperty("first_name")
#NotNull(message = "required")
private String firstName;
#NotNull(message = "required")
private String name;
#NotNull(message = "required")
#Pattern(message = "Email not valid", regexp = "^([\\w\\.\\-_]+)?\\w+#[\\w-_]+(\\.\\w+){1,}$")
private String mailAddress;
private String country;
private String phoneNumber;
#JsonProperty("address")
private Address billingAddress;
}
Would you have any ideas ?
Best regards
I had the same problem a couple of weeks ago, I created my own validation annotation with a custom validation logic. You can find the showcase project in my repository: ConditionalValidator.
If you have any questions, just ask.

Only using #JsonIgnore during serialization, but not deserialization

I have a user object that is sent to and from the server. When I send out the user object, I don't want to send the hashed password to the client. So, I added #JsonIgnore on the password property, but this also blocks it from being deserialized into the password that makes it hard to sign up users when they don't have a password.
How can I only get #JsonIgnore to apply to serialization and not deserialization? I'm using Spring JSONView, so I don't have a ton of control over the ObjectMapper.
Things I've tried:
Add #JsonIgnore to the property
Add #JsonIgnore on the getter method only
Exactly how to do this depends on the version of Jackson that you're using. This changed around version 1.9, before that, you could do this by adding #JsonIgnore to the getter.
Which you've tried:
Add #JsonIgnore on the getter method only
Do this, and also add a specific #JsonProperty annotation for your JSON "password" field name to the setter method for the password on your object.
More recent versions of Jackson have added READ_ONLY and WRITE_ONLY annotation arguments for JsonProperty. So you could also do something like:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
Docs can be found here.
In order to accomplish this, all that we need is two annotations:
#JsonIgnore
#JsonProperty
Use #JsonIgnore on the class member and its getter, and #JsonProperty on its setter. A sample illustration would help to do this:
class User {
// More fields here
#JsonIgnore
private String password;
#JsonIgnore
public String getPassword() {
return password;
}
#JsonProperty
public void setPassword(final String password) {
this.password = password;
}
}
Since version 2.6: a more intuitive way is to use the com.fasterxml.jackson.annotation.JsonProperty annotation on the field:
#JsonProperty(access = Access.WRITE_ONLY)
private String myField;
Even if a getter exists, the field value is excluded from serialization.
JavaDoc says:
/**
* Access setting that means that the property may only be written (set)
* for deserialization,
* but will not be read (get) on serialization, that is, the value of the property
* is not included in serialization.
*/
WRITE_ONLY
In case you need it the other way around, just use Access.READ_ONLY.
In my case, I have Jackson automatically (de)serializing objects that I return from a Spring MVC controller (I am using #RestController with Spring 4.1.6). I had to use com.fasterxml.jackson.annotation.JsonIgnore instead of org.codehaus.jackson.annotate.JsonIgnore, as otherwise, it simply did nothing.
Another easy way to handle this is to use the argument allowSetters=truein the annotation. This will allow the password to be deserialized into your dto but it will not serialize it into a response body that uses contains object.
example:
#JsonIgnoreProperties(allowSetters = true, value = {"bar"})
class Pojo{
String foo;
String bar;
}
Both foo and bar are populated in the object, but only foo is written into a response body.
"user": {
"firstName": "Musa",
"lastName": "Aliyev",
"email": "klaudi2012#gmail.com",
"passwordIn": "98989898", (or encoded version in front if we not using https)
"country": "Azeribaijan",
"phone": "+994707702747"
}
#CrossOrigin(methods=RequestMethod.POST)
#RequestMapping("/public/register")
public #ResponseBody MsgKit registerNewUsert(#RequestBody User u){
root.registerUser(u);
return new MsgKit("registered");
}
#Service
#Transactional
public class RootBsn {
#Autowired UserRepository userRepo;
public void registerUser(User u) throws Exception{
u.setPassword(u.getPasswordIn());
//Generate some salt and setPassword (encoded - salt+password)
User u=userRepo.save(u);
System.out.println("Registration information saved");
}
}
#Entity
#JsonIgnoreProperties({"recordDate","modificationDate","status","createdBy","modifiedBy","salt","password"})
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String country;
#Column(name="CREATED_BY")
private String createdBy;
private String email;
#Column(name="FIRST_NAME")
private String firstName;
#Column(name="LAST_LOGIN_DATE")
private Timestamp lastLoginDate;
#Column(name="LAST_NAME")
private String lastName;
#Column(name="MODIFICATION_DATE")
private Timestamp modificationDate;
#Column(name="MODIFIED_BY")
private String modifiedBy;
private String password;
#Transient
private String passwordIn;
private String phone;
#Column(name="RECORD_DATE")
private Timestamp recordDate;
private String salt;
private String status;
#Column(name="USER_STATUS")
private String userStatus;
public User() {
}
// getters and setters
}
You can use #JsonIgnoreProperties at class level and put variables you want to igonre in json in "value" parameter.Worked for me fine.
#JsonIgnoreProperties(value = { "myVariable1","myVariable2" })
public class MyClass {
private int myVariable1;,
private int myVariable2;
}
You can also do like:
#JsonIgnore
#JsonProperty(access = Access.WRITE_ONLY)
private String password;
It's worked for me
I was looking for something similar. I still wanted my property serialized but wanted to alter the value using a different getter. In the below example, I'm deserializing the real password but serializing to a masked password. Here's how to do it:
public class User() {
private static final String PASSWORD_MASK = "*********";
#JsonIgnore
private String password;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public String setPassword(String password) {
if (!password.equals(PASSWORD_MASK) {
this.password = password;
}
}
public String getPassword() {
return password;
}
#JsonProperty("password")
public String getPasswordMasked() {
return PASSWORD_MASK;
}
}
The ideal solution would be to use DTO (data transfer object)

Categories