Issue
I have an entity class for user:
@Entity
@Table(name="user")
public class User {
//one-to-many relationship with borrowed books
@OneToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE,CascadeType.PERSIST, CascadeType.REFRESH },fetch=FetchType.LAZY)
@JoinColumn(name="user_id")
private List<BorrowedBooks> borrowedBooks;
//define fields
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(nullable = false, unique = true, length = 45)
private String email;
@Column(nullable = false, length = 64)
private String password;
@Column(name = "first_name", nullable = false, length = 20)
private String firstName;
@Column(name = "last_name", nullable = false, length = 20)
private String lastName;
public User() {
}
public User(Integer id, String email, String password, String firstName,
String lastName) {
this.id = id;
this.email = email;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
}
//define getters and setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public List<BorrowedBooks> getBorrowedBooks() {
return borrowedBooks;
}
public void setBorrowedBooks(List<BorrowedBooks> borrowedBooks) {
this.borrowedBooks = borrowedBooks;
}
public void addBorrowedBook(BorrowedBooks borrowedbook) {
if(borrowedBooks==null) {
borrowedBooks = new ArrayList<>();
}
borrowedBooks.add(borrowedbook);
}
//define toString method
@Override
public String toString() {
return "User [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email
+ ", password=" + password + "]";
}
}
I have another entity class borrowedBooks:
@Entity
@Table(name="borrowed_books")
public class BorrowedBooks {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id")
private Integer id;
@Column(name="borrowed_date")
private LocalDate borrowedDate;
@Column(name="book_name")
private String bookName;
@Column(name="genre")
private String genre ;
@Column(name="book_id")
private Integer bookId;
@Column(name="due_date")
private LocalDate dueDate = null;
public BorrowedBooks() {
}
public BorrowedBooks(Integer id, LocalDate borrowedDate, String bookName, String genre, Integer bookId,
LocalDate dueDate) {
this.id = id;
this.borrowedDate = borrowedDate;
this.bookName = bookName;
this.genre = genre;
this.bookId = bookId;
this.dueDate = dueDate;
// this.user = user;
// this.books = books;
}
public Integer getid() {
return id;
}
public void setid(int id) {
this.id = id;
}
public LocalDate getBorrowedDate() {
return borrowedDate;
}
public void setBorrowedDate(LocalDate borrowedDate) {
this.borrowedDate = borrowedDate;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public LocalDate getDueDate() {
return dueDate;
}
public void setDueDate(LocalDate dueDate) {
this.dueDate = dueDate;
}
}
The user is mapped to the borrowed books class with a one-to-many unidirectional mapping with the userId which is primary key in the user class as the foreign key of the borrowed books class.
I have this controller code:
@GetMapping("/returnBooks")
public String returnBooks(@RequestParam List<Integer> bookId) {
int userId = CurrentUser.getCurrentUserId(); //i am fetching user Id
User theUser = userService.findById(userId); //finding the user from the db for the corresponding user Id
List<BorrowedBooks> borrowedbooks = theUser.getBorrowedBooks(); // getting the borrowed books from the user class
for(int i=0;i<bookId.size();i++) {
if(borrowedbooks.get(i).getBookId()==bookId.get(i)) //checking if the borrowed books in the user class matches with the @RequestParam book id that i am passing from frontend
{
borrowedbooks.remove(borrowedbooks.get(i)); //removing the matching books
}
}
theUser.setBorrowedBooks(null); // setting the borrowed books array to null so I can add the modified array of borrowed books
for(BorrowedBooks borrowedBook : borrowedbooks) {
theUser.addBorrowedBook(borrowedBook); //adding the borrowed book into the array of borrowed books
}
userService.save(theUser); //saving the object back into the database with the new borrowedBooks
return "success";
}
But this only removes the user Id foreign key from the borrowed books table and leaves the rest of the data in the row the same
This is my borrowed books table
id| borrowed_date | book_name category book_id due_date user_id
1 | 2022-09-23 | Data Science at the Command Line | Tech | 1 | 2022-10-14 |null
2 | 2022-09-23 | A Practical Introduction to Python | Tech | 2 | 2022-10-14 |1
This is how I want it to look like if I remove a book of Id 1
id| borrowed_date | book_name category book_id due_date user_id
2 | 2022-09-23 | A Practical Introduction to Python | Tech | 2 | 2022-10-14 |1
Solution
You've got to think of separate types (tables) and the relationship that you've declared.
Cascading means to apply the same operation to the entities that are related to the entity in which the corresponding relationship is declared. E.g. CascadeType.REMOVE
means to delete the related borrowed books when the user is deleted.
If you just remove a book from the list and merge the user, the foreign key of the book is set to NULL and due to the CascadeType.MERGE
it's saved to the DB. The borrowed book itself still exists in the table, but doesn't reference the user anymore, so it's "orphan". To remove those orphan records automatically, you can add orphanRemoval=true
to your @OneToMany
annotation. Also have a look at the docs.
Answered By - Mihe
Answer Checked By - Pedro (JavaFixing Volunteer)