Чтобы понять, почему я сохранил дочерние сущности, вот сопоставление.
У меня есть сущности Author (идентификатор, имя, книги) и Book (идентификатор, название, авторы). Их отношения - ManyToMany, поскольку у любого автора может быть несколько книг, а у любой книги может быть несколько авторов. Также у меня есть BookClient (id, name, rentDate, books) - отношения с сущностью Book - OneToMany, поскольку любой клиент может арендовать ноль для многих книг.
Author. java
@Table
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "books_authors",
joinColumns = { @JoinColumn(name = "author_id") },
inverseJoinColumns = { @JoinColumn(name = "book_id") }
)
private Set<Book> books = new HashSet<>();
Книга. java
@Entity
@Table
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
private Long id;
@Column(nullable = false)
private String title;
@ManyToMany(mappedBy = "books", fetch = FetchType.EAGER)
private Set<Author> authors = new HashSet<>();
@ManyToOne
@JoinColumn(name = "client_id")
private BookClient bookClient;
BookClient. java
@Entity
@Table
public class BookClient {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "client_id")
private Long id;
@Column(nullable = false)
private String name;
@OneToMany(mappedBy = "bookClient", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Book> books = new HashSet<>();
private LocalDate rentDate;
Некоторые бизнес-логики c за кадром: есть много книг, написанных разными авторами, которые сохраняются в БД какой-то, скажем, библиотеки. И эта библиотека дает книги клиентам. Любой новый клиент может зарегистрироваться в библиотеке, когда он / она берет книгу.
Клиенты книги сохраняются с помощью Entity Manager:
@Transactional
@Repository("bookClientDao")
public class BookClientDaoImpl implements BookClientDao {
@PersistenceContext
private EntityManager entityManager;
@Override
public void save(BookClient bookClient) {
entityManager.persist(bookClient);
}
@Override
public void saveOrUpdate(BookClient bookClient) {
if(bookClient.getId() == null) {
save(bookClient);
} else {
entityManager.merge(bookClient);
}
}
}
Вот пример того, как это может выглядеть в код:
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext("META-INF/context.xml");
AuthorDao authorDao = (AuthorDao) appContext.getBean("authorDao");
BookClientDao bookClientDao = (BookClientDao) appContext.getBean("bookClientDao");
//Create a book and its author
Book book = new Book();
book.setTitle("John Doe Book the 1st");
Author author = new Author();
author.setName("John Doe");
author.getBooks().add(book);
authorDao.save(author);
//Registering new Book Client
BookClient bookClient = new BookClient();
bookClient.setName("First Client");
bookClient.getBooks().add(book);
bookClient.setRentDate(LocalDate.now());
book.setBookClient(bookClient);
//book is expected to be updated by cascade
bookClientDao.saveOrUpdate(bookClient); //'detached entity passed to persist' occurs here
}
После выполнения этого кода я получаю исключение detached entity passed to persist
, поскольку экземпляр Book уже был сохранен ранее.
Exception in thread "main" javax.persistence.PersistenceException:
org.hibernate.PersistentObjectException: detached entity passed to persist: entity.manager.example.entity.Book
...
Caused by: org.hibernate.PersistentObjectException:
detached entity passed to persist: entity.manager.example.entity.Book
Если я сохраню BookClient berforehand, то соединение между BookClient и Книга установлена правильно, так как обе сущности существуют в БД. Но мне кажется, что это обходной путь.
Можно ли создать новый объект, подключить к нему уже сохраненную сущность и сохранить этот объект с помощью каскадного обновления всех его дочерних элементов?