Home Mapping one-to-many with no duplicates
Reply: 2

Mapping one-to-many with no duplicates

Patrick
1#
Patrick Published in 2018-02-12 21:12:54Z

I'm attempting to do what I believe is a 'one-to-many' relationship in Jpa Spring JPA (Hibernate) where by I have three tables (Post, Tag, Post_tag).

One post may have many tags, and multiple posts may share the same tag as well as other tags. Where tags overlap, there should only be one instance of that tag in the tag table.

The structure goes a little like the following:

Post:

  • id
  • name

Post_tag:

  • post_id
  • tag_id

Tag:

  • id
  • tag

My understanding is that this is a 'many-to-one' relation from the Post's perspective, and a 'one-to-many' from the Tags perspective. Currently I've managed to make this work using a Many-To-One relationship, however this is producing many identical tags with different IDs and cluttering the database slowing it down and making me result in caching results in redis an attempt to speed up queries which is a temporary fix!

What I currently have is:

@Table(name="Post")
public class Post implements Serializable {

...

@ManyToOne(targetEntity = Tags.class, cascade = {CascadeType.ALL, CascadeType.MERGE})
@JoinTable(name = "post_tags",
            joinColumns = {@JoinColumn(name = "post_id")},
            inverseJoinColumns = {@JoinColumn(name = "tag_id", unique=true)})
@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="post_tags")
    public Set<Tags> tags;

And the tags table:

@Column(name = "tag", unique = true)
private String tag;

Thanks!

Yohannes Gebremariam
2#
Yohannes Gebremariam Reply to 2018-02-12 21:29:46Z

Your scnerio is actually a many to many relationship. You better use @ManyToMany. Something like below.

@Entity
@Table(name = "Tag")

public class Tag{ 
    @ManyToMany(cascade = { CascadeType.ALL })
    @JoinTable(
        name = "Post_Tag", 
        joinColumns = { @JoinColumn(name = "tag_id") }, 
        inverseJoinColumns = { @JoinColumn(name = "post_id") }
    )
    Set<Post> posts = new HashSet<>();

    // standard constructor/getters/setters
}

@Entity
@Table(name = "Post")
public class Post{    


    @ManyToMany(mappedBy = "posts")

    private Set<Tag> tags= new HashSet<>();

    // standard constructors/getters/setters   
}
locus2k
3#
locus2k Reply to 2018-02-12 21:29:45Z

Problem is your Post is referencing your Tag table. Should be your post reference your Post_Tag table to get the tag:

@Entity
@Table(name = "post")
public class Post implements Serializable {

  private static final long serialVersionUID = 1L;
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Basic(optional = false)
  @Column(name = "id")
  private Integer id;
  @Column(name = "post")
  private String post;
  @OneToMany(cascade = CascadeType.ALL, mappedBy = "postId")
  private List<PostTag> postTagList;
}

@Entity
@Table(name = "tag")
public class Tag implements Serializable {

  private static final long serialVersionUID = 1L;
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Basic(optional = false)
  @Column(name = "id")
  private Integer id;
  @Column(name = "tag")
  private String tag;
  @OneToMany(cascade = CascadeType.ALL, mappedBy = "tagId")
  private List<PostTag> postTagList;
}


@Entity
@Table(name = "post_tag")
public class PostTag implements Serializable {

  private static final long serialVersionUID = 1L;
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Basic(optional = false)
  @Column(name = "id")
  private Integer id;
  @JoinColumn(name = "post_id", referencedColumnName = "id")
  @ManyToOne(optional = false)
  private Post postId;
  @JoinColumn(name = "tag_id", referencedColumnName = "id")
  @ManyToOne(optional = false)
  private Tag tagId;
}

The above has individual post and tag entities along with the PostTag entity which references both.

Now you can call post.getPostTagList() in a for loop to get all the tags to for the post for instance:

for (PostTag postTag : post.getPostTagList()) {
  Tag tag = postTag.getTag();
  String tagValue = tag.getTag();
}

One thing to note, hibernate by default lazy fetches so the list might get an error. You will have to write the query to do an eager fetch from your repository or however you fetch your data

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.298408 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO