У меня есть 2 объекта, и у обоих, среди прочего, есть списки друг друга.
Класс «Почта»:
@ManyToMany(mappedBy = "posts", cascade=CascadeType.ALL)
private List<Tag> tags;
Класс «Тег»:
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable
private List<Post> posts;`
В db у меня есть таблица POSTS, таблица TAGS и TAGS_POSTS для отношения ManyToMany. Структура в базе данных и таблицы в db в порядке. Но когда я начинаю сохранять сообщение в таблице сообщений, произошла ошибка.
Это код для добавления новой записи в базу данных, тот же код отлично работает для других сущностей даже для «Тег», которые также имеют отношение ManyToMany.
private EntityManagerFactory emf;
@PersistenceUnit
public void setEmf(EntityManagerFactory emf) {
this.emf = emf;
}
public Post add(Post post) {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(post);
em.getTransaction().commit();
return post;
}
При запуске приложения я попытался заполнить базу данных фиктивными данными для тестирования. Все остальные объекты с таким же кодом работали нормально, кроме Post.
Вот код (я попытался без установки атрибута тегов, установив для него пустой ArrayList и установив его в список уже существующих тегов в db, результат такой же):
ApplicationContext ac = SpringApplication.run(PostsPortalApplication.class, args);
PostsService ps = (PostsService) ac.getBean("postsService");
Post post = new Post();
post.setId(j);
post.setDate(new Date());
post.setDescription("desc" + "/" );
post.setDislikes(5);
post.setLikes(5);
post.setLocationLat(5);
post.setLocationLong(5);
post.setPhotoUrl("URL" + "/" + j);
post.setTitle("Title" + "/" + j);
post.setUser(user);
post.setTags(new ArrayList<>());
ps.add(post);
Это сообщение об исключении (PostsService.java:38 это строка): em.persist(post);
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: jovan.sf_62_2017.postsportal.pojo.Post at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:149) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:789) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:767) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350) at com.sun.proxy.$Proxy85.persist(Unknown Source) at jovan.sf_62_2017.postsportal.services.implementations.PostsService.add(PostsService.java:38) at jovan.sf_62_2017.postsportal.PostsPortalApplication.main(PostsPortalApplication.java:67)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: jovan.sf_62_2017.postsportal.pojo.Post at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:782) ... 9 more




Вместо этого:
@ManyToMany(cascade = CascadeType.ALL) @JoinTable private List < Post > posts;
попробуйте следующее:
private List<Post> posts = new ArrayList<>();
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(
name = "posts_tags",
joinColumns = { @JoinColumn(name = "fk_tags") },
inverseJoinColumns = { @JoinColumn(name = "fk_posts") }
)
public List<Post> getPosts() {
return posts;
}
public void setPosts(List<Post> posts) {
this.posts = posts;
}
Здесь hibernate создаст промежуточную таблицу с именем (posts_tags) для сопоставления отношений «многие ко многим».
Теперь давайте упростим сервис DAO:
@PersistenceContext(unitName = "myPersistenceUnit")
private EntityManager em;
@Transactional(value = "myTransactionManager",propagation = Propagation.REQUIRED)
public Post add(Post post) {
em.persist(post);
return post;
}
прежде всего, пожалуйста, измените:
cascade=CascadeType.ALL
к :
cascade = {CascadeType.PERSIST, CascadeType.MERGE}
потому что CascadeType.REMOVE автоматически наследуется при использовании CascadeType.ALL, но удаление объекта применяется не только к таблице ссылок, но и к другой стороне ассоциации. (видеть слышать)
так что попробуйте этот код:
@ManyToMany(mappedBy = "posts",
cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Tag> tags;
@ManyToMany(cascade =
{CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(name = "post_tag",
joinColumns = {
@JoinColumn(
name = "tag_id",
referencedColumnName = "id"
)
},
inverseJoinColumns = {
@JoinColumn(
name = "post_id",
referencedColumnName = "id"
)
}
)
private List<Post> posts;
но твоя проблема .... вы пытаетесь сохранить отдельный объект ((означает, что экземпляр этого объекта сохранен в БД, но этот объект не находится в сеансе)) в БД с помощью методов сохранения:
Post post = new Post();
post.setId(j);
.
.
.
em.persist(post)
Метод persist предназначен для добавления экземпляра новая сущность (без установки идентификатора) в контекст постоянства, то есть перехода экземпляра из переходного в постоянное состояние.
Обычно мы вызываем его, когда хотим вставить запись в базу данных (сохранить экземпляр объекта) так если ваш объект имеет временное или постоянное состояние, вы можете использовать метод persist, но если ваш объект был отсоединен до того, как вы должны использовать метод слияния, чтобы сохранить его
попробуйте инициализировать вашу коллекцию
posts:private List<Post> posts = new ArrayList<>();