hibernate Java JPA单元测试保存2项
我有一个简单的@OneToMany
设置,有两个实体-Item
和Group
,我遇到了一个有趣的问题。当我的集成测试创建一个Group
,向其中添加一个Item
并保存Group
时,Item
的两个实例最终被保存。你知道为什么吗?我正在使用Hibernate,如果有必要的话:
The Item entity
@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; }
}
The Group entity
@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);
}
}
The DAO class
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);
}
The test method
@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]
Id的7
和8
在输出中是重复的,因为save
方法插入一个项两次
# 1 楼答案
您的DAO
save
方法很难看,请重构它,否则会出现更多类似这样的问题。:)问题在于DAO}实例
save
方法中的em.merge(ne.getNamedEntityGroup())
。因为NamedEntityGroup
将ALL
级联到namedEntities
,所以MERGE
被级联到ne
这是暂时的(未保存),导致创建一个新的{ne
实例从namedEntities
集合中删除,并在其中放置一个副本Session.merge javadoc:之后,还要持久化传入的
ne
,因此最终会得到两个持久化的NamedEntity
实例:一个是由merge
创建(复制)的,另一个是显式持久化的(ne
)正确的解决方案是将
save
重构为不包含类似if(!em.contains(ne.getNamedEntityGroup()))
等代码的东西丑陋的快速修复方法是从
if
体中删除不必要的em.persist(ne)
。这将使事情正常工作,但会非常混乱(传入的ne
在save
返回后仍然是暂时的)一个更好的快速修复方法是加载持久的
NamedEntityGroup
: