i'm getting crazy with this... I followed all articles from Thoughts on Java and Vlad mihalcea about mapping associations in hibernate, but i don't know why deleting in @ManyToMany, is somehow always messed up in my project about Rest services with Spring Data Jpa.
I have a basic book -> authors manytomany relationhip, that i think i mapped correctly (at least according to how they recommand in the two references quoted higher).
I override equals, and hashcode, i wrote and use the methods to manage the associations between entities, but when i try to remove books from authors (that i want to delete), i got a ConcurrentModificationException: null, even if i use an iterator.
My entities :
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Size( min = 2, max = 30)
@Column (length = 30)
private String firstName;
@Size( min = 2, max = 30)
@Column (length = 30)
private String lastName;
@ManyToMany(mappedBy = "authors")
private Set<Book> books = new HashSet<>();
public Author(@NotNull @Size(min = 2, max = 30) String firstName, @NotNull @Size(min = 2, max = 30) String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void addBook(Book book){
this.books.add(book);
book.getAuthors().add(this);
}
public void removeBook(Book book){
this.books.remove(book);
book.getAuthors().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (this.getClass() != o.getClass()) return false;
return id != null && id.equals(((Author)o).getId());
}
@Override
public int hashCode() {
return 17;
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String isbn;
@NotNull
private String title;
//Owning side
@NotNull
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(name = "book_author",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "author_id"))
private Set<Author> authors = new HashSet<>();
public Book(String title) {
this.title = title;
this.isbn = RandomStringUtils.randomAlphanumeric(10);
}
public void addAuthor(Author author){
this.authors.add(author);
author.getBooks().add(this);
}
public void removeAuthor(Author author){
this.authors.remove(author);
author.getBooks().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (this.getClass() != o.getClass()) return false;
return id != null && id.equals(((Book)o).getId());
}
@Override
public int hashCode() {
return 13;
}
}
My deleting method which is in my business layer :
public void deleteAuthor(Long id) {
Optional<Author> author = authorRepository.findById(id);
if (author.isPresent()){
Author au = author.get();
for (Book book : au.getBooks()) {
au.removeBook(book);
}
authorRepository.delete(au);
}
}
I have the exception after the first loop in the for each. It's written exactly as in the examples, so i have absolutely no idea. I wonder if it's the Spring Data Layer that makes it more complicated ? Thanks for your help !
EDIT: The exception appears just if my author own more than one book
Apparently i managed to solve the problem by adding a new hash set while iterating.
public void deleteAuthor(Long id) {
Optional<Author> author = authorRepository.findById(id);
if (author.isPresent()) {
for (Book book : new HashSet<Book>(author.get().getBooks())) {
author.get().removeBook(book);
}
authorRepository.delete(author.get());
}
}
but it's strange that i have to do that, in all examples i saw, there is nothing like that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With