Is there a way to update and send the name to the response after calling the save method in spring? - spring-boot

There are employees and department class as below.
Employee
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne
private Department department;
}
Department
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
I want to save the employee and return the name of the department together.
Service
public ResponseEntity<Employee> create(Employee employee) throws URISyntaxException {
Employee savedEmployee = repository.save(employee);
URI location = new URI(String.format("/employee/%s", savedEmployee.getId()));
return ResponseEntity.created(location).body(savedEmployee);
}
Controller
#PostMapping
public ResponseEntity<Employee> postEmployee(#RequestBody Employee employee) throws URISyntaxException {
return service.create(employee);
}
I want to receive the response as below.
Request Body :
{
"name":"David",
"department":{
"id":1
}
}
Actual Response Body:
{
"id": 1,
"name": "David",
"department": {
"id": 1,
"name": null
}
}
Wanted Response Body :
{
"id": 1,
"name": "David",
"department": {
"id": 1,
"name": "HR"
}
}
Is there an easy way?

Related

how can i avoid duplicate field in jackson in serialisation

I'm using Jackson to serialise My Rest Api
POJO :
#Data
#Entity
#NoArgsConstructor
#AllArgsConstructor
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
public class Project implements Serializable {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String projectName;
private String resource;
#ManyToMany
private List<Collaborator> collaborators;
#JsonIgnore
#OneToMany(mappedBy = "project",cascade = CascadeType.ALL)
private List<Task> tasks;
public Project(String projectName, String resource) {
this.projectName = projectName;
this.resource = resource;
}
}
output:
{
"id": 1,
"dateDebut": "2022-05-31T13:14:39.091+00:00",
"dateFin": "2022-05-31T13:14:39.091+00:00",
"project": {
"Project": {
"id": 2,
"projectName": "project Suivi Activite 2",
"resource": "resource 2",
"collaborators": []
}
},
"collaborator": null,
"days": []
}
how can i avoid field "Project" inside "project" ?
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME) ?

How to fix update process in Spring Boot (One-to-Many , One-to-One) via Postman?

I have a problem about updating the movie.
I wrote a function that is named for "update" in MovieService.
Here is that function which is shown below.
public void update(Long id,Movie movie) {
boolean isUpdatingEmployee = (movie.getId() == id);
if (isUpdatingEmployee) {
Movie existingMovie = movieRepository.findById(movie.getId()).get();
existingMovie.setId(id);
existingMovie.setName(movie.getName());
existingMovie.setRating(movie.getRating());
existingMovie.setDirector(movie.getDirector());
existingMovie.setGenres(movie.getGenres());
existingMovie.setCreatedAt(movie.getCreatedAt());
movieRepository.save(existingMovie);
}
}
When ı try to update a movie after saving it, I got this kind of JSON result and that's why the update process cannot be done.
http://localhost:8082/api/v1/movie/update/1
Body Request
{
"name": "MovieC",
"genres": [
{
"name" : "Adventure"
},
{
"name" : "Action"
}
],
"createdAt": "2021-04-28",
"rating" : 9,
"director" : {
"name" : "Director 2"
}
}
The result of JSON after updating the process.
{
"id": null,
"name": "MovieC",
"genres": [
{
"id": null,
"name": "Action"
},
{
"id": null,
"name": "Adventure"
}
],
"rating": 9.0,
"createdAt": "2021-04-28",
"director": {
"id": null,
"name": "Director 2"
}
}
Here is my Movie entity which is shown below.
#Entity
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
public class Movie implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#JsonManagedReference
#OneToMany(mappedBy="movie",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<Genre> genres;
private Double rating;
private LocalDate createdAt;
#ManyToOne(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn
private Director director;
}
Here is my Director entity which is shown below.
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties({"movies"})
public class Director implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NonNull
private String name;
#OneToMany(mappedBy="director",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<Movie> movies;
}
Here is my Genre entity which is shown below.
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#NoArgsConstructor
#JsonIgnoreProperties({"movie"})
public class Genre implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NonNull
private String name;
#JsonBackReference
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn
private Movie movie;
}
Here is my sample project link : Project Link
How can I fix it?
As per your code, this is your request:
http://localhost:8082/api/v1/movie/update/1
{
"name": "MovieC",
"genres": [
{
"name" : "Adventure"
},
{
"name" : "Action"
}
],
"createdAt": "2021-04-28",
"rating" : 9,
"director" : {
"name" : "Director 2"
}
}
Now consider this snippet from your code:
public void update(Long id,Movie movie) {
boolean isUpdatingEmployee = (movie.getId() == id);
if (isUpdatingEmployee) {
...
Your id will be 1 as you've set this in your path variable.
However, movie.getId() will be null since I don't see it in your RequestBody.
And so:
isUpdatingEmployee = (movie.getId() == id)`
isUpdatingEmployee = ( null == 1)
isUpdatingEmployee = false
this will always give you false so I don't think this will enter in your update logic.
I think the problem because you are returning the same object movie you passed in the body of the post method in the controller - https://github.com/Rapter1990/springboothazelcast/blob/3157f354a628d418cccb99cfdbd188f594c24e9c/src/main/java/com/springboot/hazelcast/controller/MovieController.java#L64
You should rewrite it to something like this:
#PostMapping("/save")
public Movie saveMovie(#RequestBody Movie movie) throws ParseException {
LOG.info("MovieController | Saving Movie.");
return movieService.save(movie);
}
Here is the link to CRUDRepository javadocs:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html#save-S-

JPA Repository many to one

I have Student entity and Course entity. This is #ManyToOne relationship i.e. Student may attend only one course at a time, but courses may have multiple students.
#Entity
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String studentId;
private String firstName;
private String secondName;
#ManyToOne
#JoinColumn(name = "course_id")
//#JsonIgnore
private Course course;
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String courseName;
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "course", orphanRemoval = true, targetEntity = Student.class)
private List<Student> students = new ArrayList<>();
I post my data with the following json:
{
"id": 1,
"courseName": "course134",
"students" : [
{
"id" : 1,
"studentId": "123",
"firstName": "John1",
"secondName": "Name1"
},
{
"id" : 2,
"studentId": "1234567",
"firstName": "John2",
"secondName": "Name2"
}
then, as I get courses I receive:
{
"id": 1,
"courseName": "course134",
"students": []
}
How to list Students attending specific course?
I made up a Query in StudentRepository
#Query("SELECT s from Student s where s.id = :courseName")
Optional<Student> getStudentByCourseName(String courseName);
Still not working.
this is my Repository code:
#Repository
public interface CourseRepository extends JpaRepository<Course, Long> {
Optional<Course> findCourseByCourseName(String courseName);
#Query("SELECT c.students FROM Course c WHERE c.courseName = :courseName")
Optional<Student> getStudentsByCourseName(String courseName);
}
this is my Service method
public Optional<Student> findStudentByCourse(String courseName){
return courseRepository.getStudentsByCourseName(courseName);
}
and finally my Controller:
#GetMapping("/student/course/{courseName}")
public ResponseEntity<Student> findCoursesWithStudentId(#PathVariable String courseName) {
Optional<Student> byCourseName = studentService.findStudentByCourse(courseName);
if (byCourseName.isPresent()) {
return ResponseEntity.ok(byCourseName.get());
} else {
return ResponseEntity.notFound().build();
}
}
You should query the Course table, not the Student table. Also, the query will return the list, not just one entity, so change your method's return type also...
#Query("SELECT c.students FROM Course c WHERE c.courseName = :courseName")
List<Student> getStudentsByCourseName(String courseName) {}
edit
You can always do it like so:
Excute the simple method:
Course findByCourseName(String courseName) {}
and then just get its Students by a simple:
course.getStudents();

Can't save a OneToMany/ManyToOne relationship in Spring Data REST request

I am using Spring Data JPA and Spring Data Rest.
When making a REST request to persist an entity, I get the next error:
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value
My data model has the following entities:
Contract:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
public class Contract implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY,
mappedBy="contract"
)
private List<Participation> participants = new ArrayList<Participation>();
private String name;
}
Participation:
#Entity
public class Participation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false) //By default the column will be CONTRACT_ID
private Contract contract;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false)
private Contact contact;
private String clauses;
}
Contact:
#Entity
public class Contact implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String emailAddress;
}
I have 2 JPARepositories:
public interface ContractRepository extends JpaRepository<Contract, Long> {
List<Contract> findByNameContainsIgnoreCase(String name);
}
public interface ContactRepository extends JpaRepository<Contact, Long> {
}
To save a new Contract with a couple of participations, I am doing the next steps in Postman:
Create a Contract and get its href:
Request: POST http://localhost:8080/api/contracts
Body:
{
"name": "Contract1"
}
The response is successful:
201 Created
{
"name": "Contract1",
"participants": [],
"_links": {
"self": {
"href": "http://localhost:8080/api/contracts/4"
},
"contract": {
"href": "http://localhost:8080/api/contracts/4"
},
}
}
So far so good. Now that I have the contract persisted, I am adding participants:
Contact 1 already exists in the data base.
Request: PATCH http://localhost:8080/api/contracts/4
Body:
{
"participants": [
{
"clauses": "Bla bla bla",
"contact": {
"href": "http://localhost:8080/api/contacts/1"
},
"contract": {
"href": "http://localhost:8080/api/contracts/4"
}
}
]
}
When executing this request the system complains on the field/fk contract:
{
"cause": {
"cause": null,
"message": "not-null property references a null or transient value : com.xxx.xxx.model.Participation.contract"
},
"message": "not-null property references a null or transient value : com.xxx.xxx.model.Participation.contract; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : com.xxx.xxx.model.Participation.contract"
}
I tried several ways to reference the contract in the participation, like:
"contract": "http://localhost:8080/api/contracts/4"
No luck. For some reason the system is leaving the field empty instead of using the foreing key of the entity created in step 1.
What am I doing wrong?
The problem can be solved by:
Add a new repository ParticipationRepository (extends JpaRepository);
Create first a Contract without Participations:
POST http://localhost:8080/api/contracts { "name": "Contract1" }
Response:
201 Created
{
"name": "Contract1",
"_links": {
"self": {
"href": "http://localhost:8080/api/contracts/3"
},
"contract": {
"href": "http://localhost:8080/api/contracts/3"
},
"participants": {
"href": "http://localhost:8080/api/contracts/3/participants"
}
}
}
Create a Participation and use the URI from the just created Contract to set the FK. Assume Contact 1 already exists in the Data Base.
POST http://localhost:8080/api/participations {
"clauses": "bla, bla, bla",
"contract": "http://localhost:8080/api/contracts/3",
"contact": "http://localhost:8080/api/contacts/1" }
Response:
201 Created
{
"clauses": "bla, bla, bla",
"_links": {
"self": {
"href": "http://localhost:8080/api/participations/5"
},
"participation": {
"href": "http://localhost:8080/api/participations/5"
},
"contract": {
"href": "http://localhost:8080/api/participations/5/contract"
},
"contact": {
"href": "http://localhost:8080/api/contacts/5/contact"
}
}
}

JPA #OneToMany get latest record by date from Join

Getting stuck trying to fetch the latest record from a Join
I have the following classes
Author
#Entity
#Table(name = "author")
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "name")
private String name;
#OneToMany
#JoinColumn(name = "author_id", referencedColumnName = "id")
#OrderBy("id Desc")
private List<Book> books;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}
Book
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "author_id")
private Integer authorId;
#Column(name = "date_published")
private Date datePublished;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Integer getAuthorId() {
return authorId;
}
public void setAuthorId(Integer authorId) {
this.authorId = authorId;
}
public Date getDatePublished() {
return datePublished;
}
public void setDatePublished(Date datePublished) {
this.datePublished = datePublished;
}
}
Repository
#Repository
public interface AuthorRepository extends
JpaRepository<Author, Long> {
public Page<Author> findALL(int id, Pageable pageable);
}
Current results
{
"id": 1,
"name": "James",
"books":[
{
"id": 1,
"name": "book1",
"datePublished": '12/12/2012'
},
{
"id": 1,
"name": "book2",
"datePublished": '01/02/2013'
}]
},
{
"id": 2,
"name": "Tim",
"books":[
{
"id": 5,
"name": "book5",
"datePublished": '12/12/2014'
},{
"id": 6,
"name": "book6",
"datePublished": '01/02/2015'
}]
}
Expected Result
{
"id": 1,
"name": "James",
"books":[
{
"id": 1,
"name": "book2",
"datePublished": '01/02/2013'
}]
},
{
"id": 2,
"name": "Tim",
"books":[
{
"id": 6,
"name": "book6",
"datePublished": '01/02/2015'
}]
}
From this a list of Authors are being returned with all their respective books.
Question is how can JPA assist me to pick only the latest book from the collection based on date published.
If you are using hibernate you can achieve this using #JoinFormula to map the latest record by date. Something like:
#ManyToOne(fetch = FetchType.LAZY)
#JoinFormula("(" +
"SELECT b.id " +
"FROM book b " +
"WHERE b.author_id = id " +
"ORDER BY b.date_published DESC " +
"LIMIT 1" +
")")
private Book latestBook;
I had similar problem. The other solution is with #Where annotation:
#OneToMany
#JoinColumn(name = "author_id", referencedColumnName = "id")
#Where(clause = "date_published = (SELECT MAX(book.date_published) " +
"FROM book" +
"where author_id = book.author_id)")
#OrderBy("datePublished Desc")
private List<Book> books;
My post on stack: Get applications with their latest status only
if you want to get last book for each author , you can add transient field to get it :
#Entity
#Table(name = "author")
public class Author {
.......
#Transient
private Book lastBook;
#PostLoad
private void setLastBook() {
if(books==null || books,isEmpty())
this.lastBook=null;
}else{
this.lastBook=books.get(0);
}
}
or make it one to one and save it in db by same method annotated with #PostPersist and #PostUpdate. it will save in db automatically last book
the answer by #Seldo97 is correct, but he missed the space between "book" and "where" in the Select query, which will throw an error.
so basically it should be
#OneToMany
#JoinColumn(name = "author_id", referencedColumnName = "id")
#Where(clause = "date_published = (SELECT MAX(book.date_published) " +
"FROM book" +
" where author_id = book.author_id)")
#OrderBy("datePublished Desc")
private List<Book> books;
author_id -> basically refers to foreign key column name in the child.
entity
date_published -> this refers to the column by which we want to
sort(in this case the date column name).
so, the above query will take the record with the latest date and put it in the list object:
List<book> book;

Resources