1

Question

Is it possible to delete a row into a join table created by @ManyToMany annotation?

Context

With this schema :

  • TAG
  • TAGS_ARTICLES
  • ARTICLE

When a tag is removed from public Set<Tag> tags (a list into ARTICLE class) corresponding rows into TAGS_ARTICLES is removed too, but not the tag into TAG table.

The only way is to create a SQL script or JPA/Hibernate allow us to do that with annotations?

Code

My current code is : article.getTags().remove(tag);

This line remove the tag from the list, but the change is not done in database.

Conclusion

I saw this post : How to delete a row in join table with JPA , but relative tag must be deleted too (not my case).

Thanks.

Edit 1 : Expected result in database

Before delete

ARTICLE

| article_id |
| a1         |
| a2         |
| a3         |

TAGS_ARTICLES

| article_id | tag_id |
| a1         | t1     |
| a1         | t2     |
| a2         | t2     |

TAG

| article_id |
| t1         |
| t2         |

After delete t1 from a1 tag list

ARTICLE

| article_id |
| a1         |
| a2         |
| a3         |

TAGS_ARTICLES

| article_id | tag_id |
| a2         | t1     |
| a2         | t2     |

TAG

| article_id |
| t1         |
| t2         |

Edit 2 : Join table code

@Entity
public class Article {
     ...
     @ManyToMany
     @JoinTable(name = "tags_articles",
        joinColumns = @JoinColumn(name = "idarticle"),
        inverseJoinColumns = @JoinColumn(name = "idtag")
     )
     private Set<Tag> tags = new HashSet<>();
     ...
}
Royce
  • 1,557
  • 5
  • 19
  • 44
  • I'm not sure what you're asking to be honest. I'm lost. You wrote "... but not the tag into TAG table". So which rows do you want to delete? – LppEdd Feb 17 '19 at 17:55
  • Sorry if I'm not clear... Only the row into the join table, so only the row into TAGS_ARTICLES. I don't want to remove the tag into TAG because it can be used by other articles. – Royce Feb 17 '19 at 17:58
  • @LppEdd I added an example in my question. – Royce Feb 17 '19 at 18:08
  • Take a look at my updated answer. – LppEdd Feb 17 '19 at 18:16

2 Answers2

2

Edit: see comments

Using this set-up should produce the wanted result

class Article {
   ...  

   @ManyToMany
   @JoinTable(...)
   private Set<Tag> tags = new HashSet<>();
}

class Tag {
   ...

   @ManyToMany(mappedBy = "tags")
   private Set<Article> articles = new HashSet<>();
}

The Article entity is taking ownership of the relationship.


Old answer.

When a tag is removed from public Set<Tag> tags (a list into ARTICLE class) the corresponding row into TAGS_ARTICLES is removed too, but not the tag into TAG table.

By this I understand that the orphaned records are not deleted. And you want to delete them. Is this correct?

You might want to try using the Hibernate specific @Cascade annotation (documentation).
Just annotate your Collection<T> field.

@ManyToMany(...)
@Cascade(CascadeType.REMOVE) // or CascadeType.DELETE
private Set<Tag> tags = new HashSet<>();

Be sure to include it from the org.hibernate.annotations package.

LppEdd
  • 20,274
  • 11
  • 84
  • 139
  • When I remove the tag from the article, the tag is still correctly removed from the list but if I do a GET request (to test, I use Postman) on the article, the tag is still present on the list `tags`. So, no change even if I add `@ManyToMany(mappedBy = "tags")`. – Royce Feb 17 '19 at 19:33
  • @N.Lamblin do you persist the entity via Entity Manager? Are you under a Transaction? – LppEdd Feb 17 '19 at 19:37
  • If I'm under a Transaction, it is not my choice. About Entity Manager, I use the package `import javax.persistence.*;`. I'm a beginner with these tools (sorry for my incompetency), so it's hard to tell you, but I didn't explicitly create an EntityManager instance. – Royce Feb 17 '19 at 19:49
  • @N.Lamblin if I understood correctly you are using Spring Repositories, right? – LppEdd Feb 17 '19 at 19:50
  • @N.Lamblin than after removing the element from the set, you should call the save() method of your entity Repository – LppEdd Feb 17 '19 at 19:53
  • Thanks a lot it's working. So, with Spring I have to use save() everytime? It is not possible to do it "automatically"? Then, I have misunderstood something ... – Royce Feb 17 '19 at 19:58
  • @N.Lamblin with Spring you need to call save() explicitly when **not** under a transaction. If you're under a transaction (see the Transactional annotation for example) Spring will persist the entity when the transaction is committed. I suggest you to read from the official Spring documentation https://docs.spring.io/spring/docs/current/spring-framework-reference/index.html I learned everything from there – LppEdd Feb 17 '19 at 20:02
  • 1
    So I'm going to rereread this documentation. Thank you again. – Royce Feb 17 '19 at 20:04
2

The behavior of entity operation is depends on ownership of the relation, which is determined by where you place the mappedBy attribute to the annotation. Entity having mappedBy is the one which is not the owner. Both side of relationship cannot be owner.

Here you need to decide the correct owner. Let say the Tag is the owner. Then when deleting a Tag the relation TAGS_ARTICLES will be updated automatically. when deleting a TAGS_ARTICLES you have to take care of deleting the relation yourself.

@Entity
public class Tag{
    @ManyToMany
    Set<Tag_Articles> articles;
    //...
}

@Entity
public class Tag_Articles{
    @ManyToMany(mappedBy="articles")
    Set<Tag> tags;
    //...
}

For the entity relationship like above, you can try something like this -

entityManager.remove(articles)
for (Tag tag: articles.tags) {
     tag.articiles.remove(articles);
}
Sai prateek
  • 11,842
  • 9
  • 51
  • 66
  • Thanks for your answer but with this answer, I have to create an entity `Tags_Articles`, is it an obligation to solve my problem? Because my wish is an automatic update. – Royce Feb 17 '19 at 19:35