Spring boot validate multiple object from map - validation

I want to validate more than one entity before create or update. I have done these simply with custom validator in Grails. I'm wondering is there any way to do custom validator in spring which will take constraints (#NotNull, #Size etc) from entity.
Example entities:
Employee:
#Entity
class Employee{
#NotNull
String firstName
#NotNull
String lastName
#NotNull
Date dob
#ManyToOne
#JoinColumn(name = "contact_id", nullable = true)
Contact contact
}
Contact:
#Entity
class Contact{
#NotNull
String mobile
#NotNull
String email
}
Controller method:
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
ResponseEntity create(#RequestBody Map<String, Object> payload) {
Employee employee = new Employee()
Contact contact = new Contact()
//assign some properties before validation
//validate employee and contact
// create employee and contact
}
JSON request:
{
"employee": {
"firstName": "abc",
"lastName": "def",
"contact": {
"mobile": "9876543210",
"email": "email#example.com"
}
}
}
I want to do custom validation like, if contact object is present, then validate, its properties. If DOB is greater than particular date, then expect some properties etc.

Related

Child table is not mapping in OneToMany relationship in JPA

I am trying to establish One to many relationship between User and Role.
One user can have many role.
Here is the code for User class
#Entity
public class User {
#Id
private int id;
private String name;
private String password;
private String email;
private String phoneNo;
#OneToMany(
targetEntity = Role.class,
mappedBy = "user",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY
)
private Set<Role> roles;
// Getters, setters and Constructor
Code for the Role class
#Entity
public class Role {
#Id
#GeneratedValue
private int roleId;
private String role;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
// Getters, setters and Constructor
POST request on Postman is
{
"id":101,
"name": "rahul",
"password": "456",
"email": "rahul#gmail.com",
"phoneNo": "1234561234",
"role": [{
"role":"USER"
}]
}
Code on Configuration part
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http)throws Exception
{
http.csrf().disable();
}
#Bean
public BCryptPasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
}
Code On Controller part
#RestController
public class AdminController {
#Autowired
UserRepository userRepo;
#Autowired
BCryptPasswordEncoder encryptPassword;
#PostMapping("/admin/add")
public String addUserByAdmin(#RequestBody User user)
{
String pass = user.getPassword();
String encrypt = encryptPassword.encode(pass);
user.setPassword(encrypt);
userRepo.save(user);
return "User added Successfully";
}
}
Role table connection to database through Jpa
public interface RoleRepository extends JpaRepository<Role, Integer> {
}
User table connection to database through Jpa
public interface UserRepository extends JpaRepository<User,Integer>{
}
Here problem is User table is mapped properly but Role table is not getting mapped.
roleId role user_id
NULL NULL NULL
Where I am wrong ? Could anyone help me ?
On your controller method below, have you tried debugging the incoming request User object?
Anyways, I have below points here:
First, looking into your request body, the field for your roles is named role while your User object has a field roles thus, I'm pretty sure it is null during your processing since it will not be deserialized there due to mismatch field names. Try changing your request body to something like this:
{
"id":101,
"name": "rahul",
"password": "456",
"email": "rahul#gmail.com",
"phoneNo": "1234561234",
"roles": [{
"role":"USER"
}]
}
Second, if you check your database, the roles will be persisted however your foreign key user_id is null. This is expected. The cascade you did on the User object will only means that (since you use CascadeType.ALL) once you save the User object the save operation will also be cascaded to the Role object however, JPA still needs to know the relationship thus you have to set the user for each role object. Hence, you can update your controller method to something below:
#PostMapping("/admin/add")
public String addUserByAdmin(#RequestBody User user)
{
String pass = user.getPassword();
String encrypt = encryptPassword.encode(pass);
user.setPassword(encrypt);
// JPA needs to know this relationship...
user.getRoles().forEach(role -> role.setUser(user));
userRepo.save(user);
return "User added Successfully";
}
Now you can try and see that your expected behavior should now be happening.
Additional recommendations:
Why are we passing ID field on the user request? You can just remove that from your request body and use below to auto-generate your IDs to avoid Unique index or primary key violation exceptions on all of your entities:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
You can also remove the targetEntity = Role.class on the mapping as it is only used for generics and for your case clearly you are not using generics for Set. Update your User object for roles mapping:
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Role> roles;
Lastly, it is better if you can wrap your incoming payload to a DTO since you would not want to expose your entity/model to your API but I am thinking this is just for your test environment.
You need to flush the changes to the database when using save(), try this instead:
userRepo.saveAndFlush(user);

How to add new fields / values to an existing entity in Spring Data JPA

How can I add new fields or values or properties to an existing entity and store them in the same table?
Customer entity already exists and have fields as
- id
- name
- lastName
Want to add contactNumber (as seen in the api structure below) to this existing Customer entity. Don't want a new entity for contactNumber
The expected request body is as below:
{
"id": "int auto generated",
"name": "String",
"lasName": "String",
"contactNumber":
{
"mobile":"long",
"office":"long",
"home":"long"
}
}
How can this be achieved ? Checked some blogs related to mapstruct but not getting proper solution.
You can use #Embeddable :
#Embeddable
public class ContactNumber {
private Long mobile;
private Long office;
private Long home;
// getters, setters...
}
Customer Entity:
#Entity
public class Customer {
#Id
private Long id;
private String name;
private String lastName;
#Embedded
private ContactNumber contactNumber;
// getters, setters...
}
With this mapping three columns(mobile, office, home) will be added to the Customer table.
You can simply save the Customer with the request body in the question using (#RequestBody Customer customer) parameter:
#PostMapping(value="/customers")
public void saveCustomers(#RequestBody Customer customer) {
customerRepository.save(customer);
}
For more information take a look at here.

Spring Boot List of Object Bean Validation

I have a Bean,
#Data
#NoArgsConstructor
public final class PersonRequest {
#NotNull
#JsonProperty("nameList")
private List<Person> nameList;
}
and Person POJO,
#Data
public class Sensor {
#NotNull
#JsonProperty("id")
private int id;
#NotNull
#JsonProperty("name")
#Min(1)
private String name;
}
I am sending JSON request and added #Valid in my controller. I am sending request as below,
{
"nameList": [
{
"id": 1,
"name": "John"
},
{
"id": 2,
"name": "Alex"
}
]
}
When i send request without id and name not validating. I tried using #Valid private List<Person> nameList; also but no luck. I use Spring boot 2.3.2.
UPDATED:
when i add one more attribute, this also say bad request when i pass date in request.
#NotNull
#JsonProperty("startTime")
#DateTimeFormat(pattern = "yyyy-MM-dd'T'hh:mm:ss", iso =
DateTimeFormat.ISO.DATE_TIME)
#Valid
private LocalDateTime startTime;
The #Valid annotation in your controller triggers the validation of the PersonRequest object, passed as request body. To validate also the Person objects contained in PersonRequest, you need to annotate that field with #Valid too.
#Data
#NoArgsConstructor
public final class PersonRequest {
#NotNull
#JsonProperty("nameList")
#Valid
private List<Person> nameList;
}

Use unique id to deserialize object via json

I have the following Spring JPA POJO:
#Entity
public class Employee {
private String firstname;
private String lastname;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
}
Without going into details, it is easy via Spring MVC to deserialize the following JSON to Employee object instance
{
"id": 1,
"firstName": "Peter",
"lastName": "Jones"
}
My questios: since each employee has a unique id, is it possible to only send only the employee id via JSON, and somehow via Spring JPA, and Jackson magic to retrieve the rest of the information
{
"id": 1
}
The function that would deserialize JSON to object would be:
#RequestMapping(method = RequestMethod.POST, path = "/update")
public void updateFilter(#RequestBody Employee emp) {
employeerepo.save(emp);
}
Technically, I can write a function to do the mapping and such, but I have a feeling that someone else had this problem before and there is clean and elegant way to do it without much coding

Using Entity Graph with DTO Projection using ResultTransformer returns null value

Hello I am new in jpa + criteria API + hibernate..
I have doubt related to use of ResultTransformer in jpa criteria API.
I am having two entity Department and Employee. one to many mapping between department and employee. i want to use entitygraph with DTO projection
1. Department
#Entity
#NamedEntityGraph(name = "departmentWithEmployee", attributeNodes = #NamedAttributeNode("setOfEmployee"))
#Table(name = "tblDepartment")
public class Department {
#Id
private String id;
private String name;
#OneToMany(mappedBy = "department")
private Set<Employee> setOfEmployee;
//....getter & setter
}
2. Employee
#Entity
#Table(name = "tblEmployee")
public class Employee {
#Id
private String id;
#ManyToOne
#JsonIgnore
private Department department;
private String firstName;
private String lastName;
//...getter & setter
}
DepartmentDTO.java
public class DepartmentDTO implements Serializable {
private String id;
private String name;
private Set<EmployeeDTO> setOfEmployee;
//... getter & setter..
}
I am executing query with entity graph and I want to get all departments from database and serialize with DepartmentDTO.java
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<DepartmentDTO> criteria = builder.createQuery(DepartmentDTO.class);
Root root = criteria.from(Department.class);
criteria.select(root);
EntityGraph graph = entityManager.getEntityGraph("departmentWithEmployee");
List<DepartmentDTO> list = entityManager.createQuery(criteria).setHint("javax.persistence.fetchgraph", graph)
.unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(DepartmentDTO.class)).list();
when i will get size of list it will give me correct result but it will give list of department with null value like
(In database i am having total 3 departments)
Output :
[
{
"id": null,
"name": null,
"setOfEmployee": null
},
{
"id": null,
"name": null,
"setOfEmployee": null
},
{
"id": null,
"name": null,
"setOfEmployee": null
}
]
I am getting all fields with null value.
So what is the issue here , is any mistake in use of ResultTransformer? Or is any better way to execute this query where I can get records using DTO ..?

Resources