shivam sharma
shivam sharma

Reputation: 51

How should I remove data from Join table in JPA

I was trying to create a Library management system for that I have created two entities Student and Booksboth are connected using the @ManytoMany relation so I had used another join table where I store the Id of the book and Id of the student when a student Issues a book from Library but when the student returns the book the row contains both the ID's in the join table should be deleted but When I am trying to do so Either all the data from the Join table get deleted or my student and book got deleted Here is my code for that Please suggest to me what I am doing wrong here and what should be the best practice

Student Entity

package com.Library.LibraryManagement.Entity;

import java.sql.Timestamp;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name="student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private Integer id;
    @Column(name="first_name")
    private String firstName;
    @Column(name="last_name")
    private String lastName;
    @Column(name="student_email")
    private String email;
    @Column(name="student_course")
    private Integer course;
    @Column(name="date_of_birth")
    private String dateOfBirth;
    @Column(name="student_status")
    private Integer status;
    @Column(name="added_at")
    private Timestamp addedAt;
    
    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name="student_books",
                joinColumns=@JoinColumn(name = "student_id"),
                inverseJoinColumns = @JoinColumn(name="books_id"))
    private List<Books> books;
    
    
    public Student() {
    //Empty Constructor 
    }

    public Student(String firstName, String lastName, String email, Integer course,String dateOfBirth,Integer status,Timestamp addedAt) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
        this.course = course;
        this.dateOfBirth = dateOfBirth;
        this.status = status;
        this.addedAt = addedAt;
    }

    
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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 String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getCourse() {
        return course;
    }

    public void setCourse(Integer course) {
        this.course = course;
    }

    public String getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(String dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }
    
    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Timestamp getAddedAt() {
        return addedAt;
    }

    public void setAddedAt(Timestamp addedAt) {
        this.addedAt = addedAt;
    }

    public List<Books> getBooks() {
        return books;
    }

    public void setBooks(List<Books> books) {
        this.books = books;
    }
    
    public void removeBook(Books boo) {
        this.books.remove(boo);
        boo.getStudents().remove(this);
    }

    

    @Override
    public String toString() {
        return "{\"id\":\"" + id + "\", \"firstName\":\"" + firstName + "\", \"lastName\":\"" + lastName
                + "\", \"email\":\"" + email + "\", \"course\":\"" + course + "\", \"dateOfBirth\":\"" + dateOfBirth
                + "\", \"status\":\"" + status + "\", \"addedAt\":\"" + addedAt + "\"}";
    }

}

Books Entity

package com.Library.LibraryManagement.Entity;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name="books")
public class Books {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private Integer id;
    @Column(name="book_name")
    private String bookName;
    @Column(name="book_publisher")
    private String bookPublisherName;
    @Column(name="book_description")
    private String bookDescription;
    @Column(name="book_language")
    private String bookLanguage;
    @Column(name="book_in_stock")
    private Integer bookInStock;
    @Column(name="added_at")
    private Timestamp addedAt;
    @Column(name="deleted")
    private Integer deleted;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name="student_books",
                joinColumns=@JoinColumn(name = "books_id"),
                inverseJoinColumns = @JoinColumn(name="student_id"))
    private List<Student> students;
    
    public Books() {
        
    }
    
    public Books(String bookName, String bookPublisherName, String bookDescription, String bookLanguage,Timestamp addedAt,
            Integer bookInStock,Integer deleted) {
        this.bookName = bookName;
        this.bookPublisherName = bookPublisherName;
        this.bookDescription = bookDescription;
        this.bookLanguage = bookLanguage;
        this.bookInStock = bookInStock;
        this.addedAt =addedAt;
        this.deleted = deleted;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getBookPublisherName() {
        return bookPublisherName;
    }

    public void setBookPublisherName(String bookPublisherName) {
        this.bookPublisherName = bookPublisherName;
    }

    public String getBookDescription() {
        return bookDescription;
    }

    public void setBookDescription(String bookDescription) {
        this.bookDescription = bookDescription;
    }

    public String getBookLanguage() {
        return bookLanguage;
    }

    public void setBookLanguage(String bookLanguage) {
        this.bookLanguage = bookLanguage;
    }

    public Integer getBookInStock() {
        return bookInStock;
    }

    public void setBookInStock(Integer bookInStock) {
        this.bookInStock = bookInStock;
    }
    
    public Timestamp getAddedAt() {
        return addedAt;
    }

    public void setAddedAt(Timestamp addedAt) {
        this.addedAt = addedAt;
    }

    public Integer getDeleted() {
        return deleted;
    }

    public void setDeleted(Integer deleted) {
        this.deleted = deleted;
    }
    
    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }
    
    
    public void removeBook(Student stu) {
        this.students.remove(stu);
        stu.getBooks().remove(this);
    }
    

    @Override
    public String toString() {
        return "{\"id\":\"" + id + "\", \"bookName\":\"" + bookName + "\", \"bookPublisherName\":\"" + bookPublisherName
                + "\", \"bookDescription\":\"" + bookDescription + "\", \"bookLanguage\":\"" + bookLanguage
                + "\", \"bookInStock\":\"" + bookInStock + "\", \"addedAt\":\"" + addedAt + "\", \"deleted\":\""
                + deleted + "\"}";
    }
    
    //Convience MEthods to help with adding the student
    public void addStudent(Student theStudent) {
        if(students ==null) {
            students = new ArrayList<>();
        }
        students.add(theStudent);
    }
}

Method used to Revome Data from Join table

    @Override
    @Transactional
    public void returnBok(Student student, Books book) {
        try {
            if(student !=null && book !=null) {
//              Student stu = book.getStudents(.); 
//                  book.removeBook(student);
                for(Books books : student.getBooks()) {
                    student.removeBook(book);
                }
                entityManage.remove(student);
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

Upvotes: 0

Views: 2733

Answers (4)

Davide D&#39;Alto
Davide D&#39;Alto

Reputation: 8206

That's because you are using entityManager.remove. That command will delete a student and all the books associated to it because of the cascade.

If you only want to remove a book from the association,the returnBook method should look something like this:

@Transactional
public void returnBook(Student student, Books book) {
    try {
         if(student !=null && book !=null) {
             Student entityStudent = entityManager.getReference( Student.class, student.getId())
             Book entityBook = entityManager.getReference( Book.class, book.getId())
             entityStudent.removeBook(entityBook);
        }
    }
...
}

Changes will be propagated to the db during commit or the flush of the session.

Note that I've added the getReference() calls because I don't know if student and book are managed entities. If they are, it's not necessary to use getReference().

Assuming that this is what removeBook looks like:

    public void removeBook(Book book) {
        this.books.remove( book );
        book.getStudents().remove( this );
    }

As described in the Hibernate ORM documentation, it's important to update both side of the association.

Also the mapping of the association on one of the entities should be:

@ManyToMany(mappedBy = "...")

You should decide which entity owns the association.

By the way, are you sure the mapping you are trying to achieve is not the one described in the Hibernate ORM documentation as Example 172. Bidirectional many-to-many with link entity?

Upvotes: 1

hdha
hdha

Reputation: 94

Instead of using your method of returnBooke use this :

 @Override
    @Transactional
    public void returnBok(Student student, Books book) {
        try {
            if (student != null && book != null) {
                student.getBooks().removeIf(books -> {
                    return Objects.equals(book.getId(), books.getId());
                });
                entityManager.save(student);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Because in above method you are removing whole student object entityManage.remove(student); , thats why your student is delete and due to cascade = CascadeType.ALL in the mapping your book object is also deleted.

One more suggestion. Instead of populating Getter, Setter and Constructor.

Use Lombok plugin :

https://projectlombok.org/setup/maven

https://www.baeldung.com/lombok-ide

Upvotes: -1

John Bollinger
John Bollinger

Reputation: 180201

Your many-to-many relationship is not mapped correctly.

The documentation says this:

Every many-to-many association has two sides, the owning side and the non-owning, or inverse, side. The join table is specified on the owning side. If the association is bidirectional, either side may be designated as the owning side. If the relationship is bidirectional, the non-owning side must use the mappedBy element of the ManyToMany annotation to specify the relationship field or property of the owning side.

(emphasis added).

Your annotations map the relationship as if both sides owned it, which is incorrect.

Once you choose an owning side, you modify the relationship by updating the relationship field on the owning side and saving the modified owning entity. You have other answers showing how to do that. You probably want also to either make corresponding changes to the non-owning side or to refresh the non-owning entity to bring the relationship field on that side into sync.

Upvotes: 2

Antoniossss
Antoniossss

Reputation: 32517

It sohuld be

student.getBooks().remove(book);

entityManger.save(student)

Upvotes: 0

Related Questions