【问题标题】:Mapping one-to-many with no duplicates映射一对多,没有重复
【发布时间】:2018-02-12 21:12:54
【问题描述】:

我正在尝试在 Jpa Spring JPA (Hibernate) 中执行我认为的“一对多”关系,其中我有三个表(Post、Tag、Post_tag)。

一篇文章可能有多个标签,并且多个文章可能共享同一个标签以及其他标签。在标签重叠的地方,标签表中应该只有一个该标签的实例。

结构有点像以下:

帖子:

  • 身份证
  • 姓名

post_tag:

  • post_id
  • tag_id

标签:

  • 身份证
  • 标签

我的理解是,从 Post 的角度来看,这是一个“多对一”的关系,而从标签的角度来看,这是一个“一对多”的关系。目前我已经设法使用多对一关系来完成这项工作,但是这会产生许多具有不同 ID 的相同标签,并使数据库变得混乱,从而减慢速度并使我在 redis 中缓存结果以尝试加快查询速度这是一个临时修复!

我目前拥有的是:

@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;

还有标签表:

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

谢谢!

【问题讨论】:

  • 这是多对多映射
  • Set字段怎么可能是@ManyToOne???不能。

标签: java mysql spring hibernate jpa


【解决方案1】:

问题是您的帖子引用了您的标签表。应该是您的帖子引用您的 Post_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;
}

上面有单独的帖子和标签实体以及引用两者的PostTag实体。

现在您可以在 for 循环中调用 post.getPostTagList() 以获取帖子的所有标签,例如:

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

需要注意的一点是,休眠默认情况下会延迟提取,因此列表可能会出错。您必须编写查询以从存储库中进行急切获取,或者您获取数据

【讨论】:

    【解决方案2】:

    您的 scnerio 实际上是多对多关系。你最好用 @ManyToMany。如下所示。

    @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   
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多