Jovan0042
Jovan0042

Reputation: 21

Persist ManyToMany entities in H2 throws exception

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

Answers (2)

Solmaz Oskouie
Solmaz Oskouie

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

sam
sam

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

Related Questions