我有一个简单的 {{#each filteredOffers as |offer|}} ... {{/each}}
设置,包含2个实体 - @OneToMany
和Item
,并且我遇到了一个有趣的问题。当我的集成测试创建Group
时,向其添加Group
并保存Item
,Group
的2个实例最终会被保存。有什么想法吗?我正在使用Hibernate,如果重要的话:
项目实体
Item
群组实体
@Entity
public class NamedEntity implements java.io.Serializable {
Long id;
@NotNull
String name;
NamedEntityGroup namedEntityGroup;
NamedEntityType type;
@Enumerated(EnumType.STRING)
public NamedEntityType getType() { return type; }
@Id
@GeneratedValue
public Long getId() { return id; }
public String getName() { return name; }
@JoinColumn(name = "NamedEntityGroupId")
@JsonBackReference
@ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
public NamedEntityGroup getNamedEntityGroup() { return this.namedEntityGroup; }
}
DAO 类
@Entity
public class NamedEntityGroup implements Serializable {
Long id;
String name;
List<NamedEntity> namedEntities;
@Id
@GeneratedValue
public Long getId() { return this.id; }
public String getName() { return this.name; }
@JsonManagedReference
@OneToMany(cascade = { CascadeType.ALL }, mappedBy = "namedEntityGroup", fetch = FetchType.EAGER)
public List<NamedEntity> getNamedEntities() { return this.namedEntities; }
public void addNamedEntity(NamedEntity ne) {
if (this.namedEntities == null) {
this.namedEntities = new ArrayList<NamedEntity>();
}
if (!namedEntities.contains(ne)) {
this.namedEntities.add(ne);
}
ne.setNEG(this);
}
}
测试方法
public void save(NamedEntity ne) throws EntityValidationException {
validate(ne);
if(ne.getNamedEntityGroup() != null) {
if(!em.contains(ne.getNamedEntityGroup())) {
ne.setNamedEntityGroup(em.merge(ne.getNamedEntityGroup()));
em.persist(ne);
return;
}
}
em.persist(ne);
}
这是输出:
@Test
public void testAddNamedEntityToExistingGroup() throws EntityValidationException {
int neSize = ed.getAllNamedEntities().size();
NamedEntityGroup neg = ed.getAllNamedEntityGroups().iterator().next();
assertNotNull(neg);
assertTrue(neg.getNamedEntities().size() == 0);
em.detach(neg);
NamedEntity n = new NamedEntity();
n.setName("Hello");
neg.addNamedEntity(n);
n.setNamedEntityGroup(neg);
n.setType(NamedEntityType.DEFAULT);
ed.save(n);
for(NamedEntity e : ed.getAllNamedEntities()) {
L.error("Entity: {}", e);
}
assertTrue("Size is " + ed.getAllNamedEntities().size() + " but it should be " + (neSize + 1) + " the group has "
+ neg.getNamedEntities().size() + " entities (should be 1) " + ed.getAllNamedEntities(), neSize + 1 == ed.getAllNamedEntities().size());
}
我的Entity: NamedEntity [id=1, name=Super Mario Brothers, namedEntityGroup=null, type=null]
Entity: NamedEntity [id=3, name=Mario Kart, namedEntityGroup=null, type=null]
Entity: NamedEntity [id=5, name=F-Zero, namedEntityGroup=null, type=null]
Entity: NamedEntity [id=7, name=Hello, namedEntityGroup=NamedEntityGroup [id=2, name=Super Mario Brothers, COUNT(entity)=1], type=DEFAULT]
Entity: NamedEntity [id=8, name=Hello, namedEntityGroup=NamedEntityGroup [id=2, name=Super Mario Brothers, COUNT(entity)=1], type=DEFAULT]
和7
在输出中是重复的,因为8
方法正在插入一个项目2次。
答案 0 :(得分:1)
您的DAO save
方法很难看,重构它,否则会出现更多像这样的问题。 :)
问题出在DAO em.merge(ne.getNamedEntityGroup())
方法的save
中。由于NamedEntityGroup
级联ALL
到namedEntities
,MERGE
级联到ne
,这是暂时的(未保存),导致新要创建的NamedEntity
实例。 ne
实例已从namedEntities
集合中删除,并将其副本放入其中。 Session.merge javadoc:
使用。将给定对象的状态复制到持久对象上 相同的标识符如果当前没有持久化实例 与会话相关联,它将被加载。返回持久性 实例。 如果给定的实例未保存,保存和。的副本 将它作为新的持久化实例返回。给定的实例 没有与会话相关联。此操作级联到 如果关联使用cascade =“merge”映射关联实例。
之后你也坚持传入ne
,所以你最终得到了两个持久的NamedEntity
个实例:一个由merge
创建(复制)的实例和你明确持有的实例({ {1}})。
正确的解决方案是将ne
重构为不包含save
等类似代码的内容。
丑陋的快速解决方法是从if(!em.contains(ne.getNamedEntityGroup()))
正文中删除不必要的em.persist(ne)
。这将使事情有效,但会非常混乱(在if
返回后传入的ne
仍然是暂时的。
更好的快速解决方法是加载持久性save
:
NamedEntityGroup