11 Şubat 2020 Salı

JPA ManyToMany İlişki - Ara Tablo Kullanan

Giriş
Açıklaması şöyle
Always use @JoinColum on unidirectional one-2-many relationships, otherwise JPA will create a linking table instead of storing foreign keys in many side of the relationship.
Şeklen şöyle


Örnek
Şöyle yaparız
@Entity
public class Person {
 ...
  @ManyToMany
  private List<Address> addresses;
}

@Entity
public class Address {
  ...
}
Tablo yapısı şöyledir
Person -> Ara Tablo -> Address.
Sadece Person sınıfında @ManyToMany anotasyonu kullanılır. Address sınıfında kullanılmaz. 

Silme
- Eğer Person'dan Address silinirse sadece ara tablo kaydı silinir. Address silinmez.
- Eğer Person silinirse hem Person hem de ara tablo kaydı silinir. Address silinmez.

Owning Side
Owning side için açıklama şöyle
When implementing many-to-many bi-directional associations in JPA, we must define an “owning side.” To mark an entity as the “owning side,” we should mark its @ManyToMany collection with the @JoinTable annotation. It means that Hibernate will track changes in this collection and update the junction table in the database accordingly.

Before selecting an entity to “own” the association, we should consider data usage. For our application, we can assume that users will search for posts and create and update them more often than tags. Therefore, setting the Post entity as the “owning side” makes more sense. So, if we look at the Post entity code above, we’ll see that the tags collection is annotated with @JoinTable. This is precisely what we need.
Synchronized Methods
Açıklama şöyle
One more thing that we need to remember is that we need to keep the bidirectional association synchronized. E.g., when we add a Tag instance to a tags set in a PostWe should not forget to add this Post to the corresponding posts set in the Tag entity. The same logic applies to the Tag removal process. To implement this approach, we add synchronization methods to both entities.
Bu örnek için tam açıklama şöyle
  1. In the synchronization methods, consider usage in a non-transactional context. Use Hibernate.isInitialized to check if a collection on the other side is initialized.
  2. Prefer Set collection type to define a many-to-many association – Hibernate generates better SQL for data updates. Remember that synchronization methods cause collections initialization for both sides in the association.
  3. Consider using List instead of Set collection type in the many-to-many association on the “non-owning” association side to save an extra select query.
  4. Remember that using List as a collection type on the “owning” side in many-to-many associations causes complete links recreation in the junction table.
  5. Synchronization methods for the “owning” side entity are optional, but we’d recommend implementing them for the “non-owning” side with all precautions listed above.

Şöyle yaparız
@Entity @Table(name = "post") public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id; @Column(name = "title") private String title; @ManyToMany @JoinTable(name = "post_tag", joinColumns = @JoinColumn(name = "post_id"), inverseJoinColumns = @JoinColumn(name = "tag_id")) private Set<Tag> tags = new LinkedHashSet<>(); public void addTag(Tag tag) { tags.add(tag); if (Hibernate.isInitialized(tag.getPosts())) { tag.getPosts().add(this); } } public void removeTag(Tag tag) { tags.remove(tag); if (Hibernate.isInitialized(tag.getPosts())) { tag.getPosts().remove(this); } } }
Diğer taraf için şöyle yaparız
@Entity @Table(name = "tag") public class Tag { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false) private Long id; @Column(name = "name") private String name; @ManyToMany(mappedBy = "tags") private List<Post> posts = new ArrayList<>(); public void addPost(Post post) { posts.add(post); if (Hibernate.isInitialized(post.getTags())) { post.getTags().add(this); } } public void removePost(Post post) { posts.remove(post); if (Hibernate.isInitialized(post.getTags())) { post.getTags().remove(this); } } }
Spring kullanıyorsak şöyle yaparız
public interface PostRepository extends JpaRepository<Post, Long> { @EntityGraph(attributePaths="tags") Optional<Post> findPostWithTagsById(Long id); } public interface TagRepository extends JpaRepository<Tag, Long> { }




Hiç yorum yok:

Yorum Gönder