Reputation: 111
I' m tring to make an example to see table output of ManyToMany relationship managed by Hibernate. Everthing seems well. However, some rows are duplicated. I do not know why. My Scenario is that I have 2 simple Classes Author & Book. Book can have more than one author and Author can have more than one book.
Let' s see classes and mysql table output.
Book.java
@Entity
@Table(name = "BOOK")
public class Book {
@SequenceGenerator(name = "SEQ_GEN_BOOK", allocationSize = 1, sequenceName = "TABLE_SEQ_GEN_BOOK", initialValue = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_GEN_BOOK")
@Id
@Column(name = "ID")
private long id;
@Column(name = "NAME", nullable = false, length = 100)
private String name;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity = Author.class)
private Set authors = new HashSet();
//setter/getters
Author.java
@Entity
@Table(name = "Author")
public class Author {
@SequenceGenerator(name = "SEQ_GEN_AUTHOR", initialValue = 1, sequenceName = "TABLE_SEQ_GEN_AUTHOR", allocationSize = 1)
@GeneratedValue(generator = "SEQ_GEN_AUTHOR", strategy = GenerationType.SEQUENCE)
@Id
@Column(name = "ID")
private long id;
@Column(name = "NAME", nullable = false, length = 100)
private String name;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "authors")
private Set<Book> books = new HashSet<Book>();
//setter/getters
main method is
public static void main(String[] args) throws InterruptedException {
Author author1 = new Author();
author1.setName("author1");
Author author2 = new Author();
author2.setName("author2");
Author author3 = new Author();
author3.setName("author3");
Book book1 = new Book();
book1.setName("book1");
Book book2 = new Book();
book2.setName("book2");
Book book3 = new Book();
book3.setName("book3");
Book book4 = new Book();
book4.setName("book4");
book1.getAuthors().add(author1);
book1.getAuthors().add(author2);
book2.getAuthors().add(author1);
book2.getAuthors().add(author2);
book2.getAuthors().add(author3);
book3.getAuthors().add(author2);
book3.getAuthors().add(author3);
book4.getAuthors().add(author3);
DbOperations.getInstance().save(book1);
DbOperations.getInstance().save(book2);
DbOperations.getInstance().save(book3);
DbOperations.getInstance().save(book4);
}
Mysql Table output is below: (i can create the same overview without having transition table)
mysql> show tables;
+------------------------+
| Tables_in_hibernatedb1 |
+------------------------+
| author |
| book |
| book_author |
| table_seq_gen_author |
| table_seq_gen_book |
+------------------------+
5 rows in set (0.00 sec)
mysql> select * from book_author;
+----------+------------+
| books_ID | authors_ID |
+----------+------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
| 2 | 5 |
| 3 | 6 |
| 3 | 7 |
| 4 | 8 |
+----------+------------+
8 rows in set (0.00 sec)
mysql> select * from book;
+----+-------+
| ID | NAME |
+----+-------+
| 1 | book1 |
| 2 | book2 |
| 3 | book3 |
| 4 | book4 |
+----+-------+
4 rows in set (0.00 sec)
mysql> select * from author;
+----+---------+
| ID | NAME |
+----+---------+
| 1 | author1 |
| 2 | author2 |
| 3 | author1 |
| 4 | author2 |
| 5 | author3 |
| 6 | author2 |
| 7 | author3 |
| 8 | author3 |
+----+---------+
8 rows in set (0.00 sec)
My Expectation is to see tables like below:
mysql> select * from book_author;
+----------+------------+
| books_ID | authors_ID |
+----------+------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 2 |
| 3 | 3 |
| 4 | 3 |
+----------+------------+
mysql> select * from book;
+----+-------+
| ID | NAME |
+----+-------+
| 1 | book1 |
| 2 | book2 |
| 3 | book3 |
| 4 | book4 |
+----+-------+
mysql> select * from author;
+----+---------+
| ID | NAME |
+----+---------+
| 1 | author1 |
| 2 | author2 |
| 3 | author3 |
+----+---------+
DBOperation.class
public Object save(Object o) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
Object merge = null;
try {
merge = session.merge(o);
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
} finally {
session.close();
}
return merge;
}
Upvotes: 1
Views: 146
Reputation: 13041
@ManyToMany
association, so as hibernate documentation suggests, both sides of this association should be synchronized. It’s good practice to provide helper methods for adding or removing child entities.@Entity
@Table(name = "BOOK")
public class Book {
@SequenceGenerator(name = "SEQ_GEN_BOOK", allocationSize = 1, sequenceName = "TABLE_SEQ_GEN_BOOK", initialValue = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_GEN_BOOK")
@Id
@Column(name = "ID")
private long id;
@Column(name = "NAME", nullable = false, length = 100)
private String name;
// do not use raw types
// see for example this https://stackoverflow.com/questions/2770321
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Author> authors = new HashSet<>();
public void addAuthor(Author author) {
authors.add( author );
author.getBooks().add( this );
}
public void removeAddress(Author author) {
authors.remove( author );
author.getBooks().remove( this );
}
@Override
public boolean equals(Object o) {
// As you use Set collections for Author and Book
// you should override equals/hashCode
// and these implementation should be based on @NaturalIds
// not on @Ids
}
@Override
public int hashCode() {
// ...
}
}
persist(entity)
, merge(entity)
should be used, to put entity back to persistence context if the entity was detached and was changed.
See this.So, you should do something like this:
Author author1 = new Author();
author1.setName("author1");
Author author2 = new Author();
author2.setName("author2");
Author author3 = new Author();
author3.setName("author3");
Book book1 = new Book();
book1.setName("book1");
Book book2 = new Book();
book2.setName("book2");
Book book3 = new Book();
book3.setName("book3");
Book book4 = new Book();
book4.setName("book4");
book1.addAuthor(author1);
book1.addAuthor(author2);
book2.addAuthor(author1);
book2.addAuthor(author2);
book2.addAuthor(author3);
book3.addAuthor(author2);
book3.addAuthor(author3);
book4.addAuthor(author3);
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.persist(book1);
session.persist(book2);
session.persist(book3);
session.persist(book4);
transaction.commit();
session.close();
Upvotes: 1
Reputation: 111
I was creating different sessions for each save method call and i was calling session.merge()
. I have changed DbOperations.getInstance().save(..)
method content to insert all given rows in the same session by calling session.save()
instead of session.merge()
. See updated method below and it works well now.
public void save(Object... obj) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
try {
for (Object o: obj) {
session.save(o);
}
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
} finally {
session.close();
}
}
Upvotes: 0