我一直在努力与链接表中的附加列建立多对多关系。
这些是我的实体:
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Post {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonIgnore
private List<PostTag> tags = new ArrayList<>();
//getters and setters
public void addTag(Tag tag){
PostTag postTag = new PostTag(this, tag);
tags.add(postTag);
tag.getPosts().add(postTag);
}
public void removeTag(Tag tag) {
for (Iterator<PostTag> iterator = tags.iterator();
iterator.hasNext(); ) {
PostTag postTag = iterator.next();
if (postTag.getPost().equals(this) &&
postTag.getTag().equals(tag)) {
iterator.remove();
postTag.getTag().getPosts().remove(postTag);
postTag.setPost(null);
postTag.setTag(null);
}
}
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Post post = (Post) o;
return id == post.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Tag {
@Id
@GeneratedValue
private Long id;
private String comment;
@OneToMany(mappedBy = "tag", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonIgnore
private List<PostTag> posts = new ArrayList<>();
//getters and setters
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Tag that = (Tag) o;
return id == that.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
@Entity(name = "PostTag")
@Table(name = "post_tag")
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class PostTag {
@EmbeddedId
private PostTagId id;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("postId")
private Post post;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("tagId")
private Tag tag;
private Integer impact;
public FacilityParticipant(Post post, Tag tag) {
this.post = post;
this.tag = tag;
this.id = new PostTagId(post.getId(), tag.getId());
}
//getters and setters
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PostTag that = (PostTag) o;
return Objects.equals(post, that.post) && Objects.equals(tag, that.tag);
}
@Override
public int hashCode() {
return Objects.hash(post, tag);
}
}
@Embeddable
public class PostTagId implements Serializable {
private Long postId;
private Long tagId;
//getters setters
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PostTagId that = (PostTagId) o;
return Objects.equals(postId, that.postId) && Objects.equals(tagId, that.tagId);
}
@Override
public int hashCode() {
return Objects.hash(postId, tagId);
}
}
我有一个映射到许多标签的帖子实体和一个映射到许多帖子的标签。链接表是PostTag,其中包含双方的映射以及附加列“ impact”。链接表的PK映射到可嵌入表PostTagId,该表包含Post和Tag的PK。
当我尝试插入新实体时,请执行以下操作:
Tag tag1 = new Tag();
Tag tag2 = new Tag();
repository.save(tag1);
repository.save(tag2);
Post post1 = new Post();
Post post2 = new Post();
post1.addTag(tag1);
post1.addTag(tag2);
post2.addTag(tag1);
repository.save(post1);
repository.save(post2);
尝试插入这些项目时,出现错误无法将NULL插入(“ POST_TAG”。“ ID”)
我尝试过的任何方法,要么伴随其他错误,要么就重新出现。
该模型中的某些内容很可能是不正确的,但是我真的无法弄清楚它出了什么问题。
整个建模基于本文The best way to ...
任何帮助将不胜感激。
谢谢
答案 0 :(得分:2)
spring-data-jpa是JPA之上的一层。每个实体都有其自己的存储库,您必须处理该存储库。我已经看过上面提到的教程,它是针对JPA的,并且还将ID设置为null,这似乎有点不正确,可能是导致错误的原因。我没那么近看。为了处理spring-data-jpa中的问题,您需要为链接表提供单独的存储库。
@Entity
public class Post {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostTag> tags;
@Entity
public class Tag {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "tag", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostTag> posts;
@Entity
public class PostTag {
@EmbeddedId
private PostTagId id = new PostTagId();
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("postId")
private Post post;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("tagId")
private Tag tag;
public PostTag() {}
public PostTag(Post post, Tag tag) {
this.post = post;
this.tag = tag;
}
@SuppressWarnings("serial")
@Embeddable
public class PostTagId implements Serializable {
private Long postId;
private Long tagId;
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
PostTagId that = (PostTagId) o;
return Objects.equals(postId, that.postId) && Objects.equals(tagId, that.tagId);
}
@Override
public int hashCode() {
return Objects.hash(postId, tagId);
}
并使用它,如上所示:
@Transactional
private void update() {
System.out.println("Step 1");
Tag tag1 = new Tag();
Post post1 = new Post();
PostTag p1t1 = new PostTag(post1, tag1);
tagRepo.save(tag1);
postRepo.save(post1);
postTagRepo.save(p1t1);
System.out.println("Step 2");
Tag tag2 = new Tag();
Post post2 = new Post();
PostTag p2t2 = new PostTag(post2, tag2);
postRepo.save(post2);
tagRepo.save(tag2);
postTagRepo.save(p2t2);
System.out.println("Step 3");
tag2 = tagRepo.getOneWithPosts(2L);
tag2.getPosts().add(new PostTag(post1, tag2));
tagRepo.save(tag2);
System.out.println("Step 4 -- better");
PostTag p2t1 = new PostTag(post2, tag1);
postTagRepo.save(p2t1);
}
请注意,更改很少。我没有明确设置PostTagId
的ID。这些由持久层(在这种情况下为休眠)处理。
还请注意,由于已设置PostTag
,因此可以使用其自己的存储库显式更新CascadeType.ALL
条目,也可以通过从列表中添加和删除它们来进行更新,如图所示。对spring-data-jpa使用CascadeType.ALL
的问题在于,即使您预取了连接表实体,spring-data-jpa仍将再次执行此操作。尝试通过CascadeType.ALL
更新新实体的关系是有问题的。
没有CascadeType
或posts
列表(应为Set)都不是该关系的所有者,因此,在持久性和仅用于查询结果。
在阅读tags
关系时,由于没有PostTag
,因此需要专门获取它们。 FetchType.EAGER
的问题是开销,如果您不希望联接,并且同时将其放在FetchType.EAGER
和Tag
上,那么您将创建一个递归获取来获取所有{{ 1}}和Post
进行任何查询。
Tags
最后,始终检查日志。请注意,创建关联需要spring-data-jpa(我认为是JPA)读取现有表以查看该关系是新建的还是更新的。无论您自己创建和保存Posts
还是预取列表,都会发生这种情况。 JPA有单独的合并,我认为您可以更有效地使用它。
@Query("select t from Tag t left outer join fetch t.posts tps left outer join fetch tps.post where t.id = :id")
Tag getOneWithPosts(@Param("id") Long id);