Reputation: 21
I have 2 entities, and both among other things have Lists of each other.
Class "Post":
@ManyToMany(mappedBy = "posts", cascade=CascadeType.ALL)
private List<Tag> tags;
Class "Tag":
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable
private List<Post> posts;`
In db I have "POSTS" table, "TAGS" table and "TAGS_POSTS" for ManyToMany relationship. Structure in database and tables in db are fine. But when I start to persist Post entiti in Posts table error occurred.
This is code for adding new Post to db, the same code works fine for other entities even for "Tag" which also have ManyToMany relationship.
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;
}
Now at the start of the application, I tried to populate db with some dummy data for testing. Every other entity with same code worked fine, except Post.
Here is the code (I tried without setting tags attribute, with setting it to empty ArrayList, and with setting it to list of already existing tags in db result is the same):
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);
This is exception message (PostsService.java:38 is line):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
Upvotes: 1
Views: 1615
Reputation: 66
first of all please change :
cascade=CascadeType.ALL
to :
cascade = {CascadeType.PERSIST, CascadeType.MERGE}
because The CascadeType.REMOVE is automatically inherited when using CascadeType.ALL, but the entity removal is not only applied to the link table, but to the other side of the association as well.(see hear)
so try this code :
@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;
but your problem .... you try to save a detached object((means that instance of that object has saved into the DB but that object is not in the session)) to db with persist methods :
Post post = new Post();
post.setId(j);
.
.
.
em.persist(post)
The persist method is intended for adding a new entity (without id setting to it ) instance to the persistence context, i.e. transitioning an instance from transient to persistent state.
We usually call it when we want to insert a record to the database (persist an entity instance) so if your object has transient or persist state you can use persist method but if your object was detached before you should use merge method to save it
Upvotes: 1
Reputation: 2004
Instead of this:
@ManyToMany(cascade = CascadeType.ALL) @JoinTable private List < Post > posts;
try the following:
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;
}
Here hibernate will create an intermediate table named(posts_tags) for many-to-many relationship mapping.
Now let's simplify the DAO service:
@PersistenceContext(unitName = "myPersistenceUnit")
private EntityManager em;
@Transactional(value = "myTransactionManager",propagation = Propagation.REQUIRED)
public Post add(Post post) {
em.persist(post);
return post;
}
Upvotes: 0