我有以下模式(缩写)
Comment id, content, createdBy
Attribute id, key, value (unique constraint on key, value)
CommentAttribute id, comment_id, attribute_id
所以这是一个相当简单的模式。
我已将其与Comment和Attribute实体的最简单实体进行映射,所以我不会在此处发布代码。
CommentAttribute如下
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "comment_attributes")
public class CommentAttribute {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Cascade(value = {CascadeType.ALL})
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "comment_id", nullable = false)
private Comment comment;
@Cascade(value = {CascadeType.ALL})
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "attribute_id", nullable = false)
private Attribute attribute;
public Long getId() {
return id;
}
public CommentAttribute setId(final Long id) {
this.id = id;
return this;
}
public Comment getComment() {
return comment;
}
public CommentAttribute setComment(final Comment comment) {
this.comment = comment;
return this;
}
public Attribute getAttribute() {
return attribute;
}
public CommentAttribute setAttribute(final Attribute attribute) {
this.attribute = attribute;
return this;
}
}
目的是用户将添加具有一个或多个属性的评论。类似于下面的GraphQL缩写
addComment(content: "a comment", [{name: "threadId" value: "thread1"}])
我正在使用Spring JPA和Hibernate,因此我想对以上模型进行建模,以便轻松将记录添加到链接表中。我进行了如下测试:
@Test
public void whenAddingTwoCommentsWithSameAttributesThenNoDuplicateCreated() {
Comment comment = new Comment();
comment.setCreatedBy("user1");
comment.setContent("some test comment");
Attribute attribute = new Attribute();
attribute.setKey("threadId");
attribute.setValue("thread1");
CommentAttribute commentAttribute = new CommentAttribute();
commentAttribute.setComment(comment);
commentAttribute.setAttribute(attribute);
commentAttributeRepository.saveAndFlush(commentAttribute);
Comment comment2 = new Comment();
comment2.setCreatedBy("user1");
comment2.setContent("some test comment2");
Attribute attribute2 = new Attribute();
attribute2.setKey("threadId");
attribute2.setValue("thread1");
attribute2.setTenantId("customer1");
CommentAttribute commentAttribute2 = new CommentAttribute();
commentAttribute2.setComment(comment2);
commentAttribute2.setAttribute(attribute2);
commentAttributeRepository.saveAndFlush(commentAttribute2);
final List<CommentAttribute> all = commentAttributeRepository.findAll();
assertThat(all).hasSize(2);
assertThat(all.get(0).getComment().getContent()).isEqualTo("some test comment");
assertThat(all.get(0).getAttribute().getValue()).isEqualTo("thread1");
assertThat(all.get(1).getComment().getContent()).isEqualTo("some test comment2");
assertThat(all.get(1).getAttribute().getValue()).isEqualTo("thread1");
}
因此attribute2变量实际上是唯一的。当保存commentAttribute2时,我在属性表上遇到了唯一的约束冲突,这并不奇怪,因为Hibernate试图插入一条新记录。
我希望Hibernate使用现有的属性记录(如果存在),否则创建一个新记录并使用它。有一些使用注释配置的方式吗?如果没有,我是否必须查找属性实体,并且仅在找不到一个新实体时创建一个新实体?
答案 0 :(得分:1)
请考虑一下,如果首先获取注释的属性并将其留给属性进行自我识别,那么JPA会做正确的事情。另外,您不需要手动创建联接表,JPA也会为您完成。
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@ManyToMany
private Set<Attribute> attributes;
// getters, setters
}
和
@Entity
public class Attribute {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
// getters, setters,
// AND hashCode and equals using the id field
}
然后,第二个插入操作不执行任何操作,因为equals
方法检查ID所标识的属性已经存在于集合中。您要做的就是获取当前属性集以及现有注释。
tx.begin();
Comment c = new Comment();
Attribute a = new Attribute();
em.persist(a);
c.setAttributes(new HashSet<>());
c.getAttributes().add(a);
em.persist(c);
tx.commit();
// to remove everything from cache
em.clear();
// this does nothing except a select since the attribute is already in the set of attributes
// and in fact the `em.find` does not issue a select in this case because
// the attribute gets loaded into the cache from the Comment select.
tx.begin();
Comment c2 = em.createQuery("select c from Comment c left join fetch c.attributes where c.id = 2", Comment.class).getSingleResult();
Attribute a2 = em.find(Attribute.class, 1);
c2.getAttributes().add(a2);
tx.commit();