有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

hibernate Java JPA单元测试保存2项

我有一个简单的@OneToMany设置,有两个实体-ItemGroup,我遇到了一个有趣的问题。当我的集成测试创建一个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的78在输出中是重复的,因为save方法插入一个项两次


共 (1) 个答案

  1. # 1 楼答案

    您的DAOsave方法很难看,请重构它,否则会出现更多类似这样的问题。:)

    问题在于DAOsave方法中的em.merge(ne.getNamedEntityGroup())。因为NamedEntityGroupALL级联到namedEntities,所以MERGE被级联到ne这是暂时的(未保存),导致创建一个新的{}实例ne实例从namedEntities集合中删除,并在其中放置一个副本Session.merge javadoc

    Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".

    之后,还要持久化传入的ne,因此最终会得到两个持久化的NamedEntity实例:一个是由merge创建(复制)的,另一个是显式持久化的(ne

    正确的解决方案是将save重构为不包含类似if(!em.contains(ne.getNamedEntityGroup()))等代码的东西

    丑陋的快速修复方法是从if体中删除不必要的em.persist(ne)。这将使事情正常工作,但会非常混乱(传入的nesave返回后仍然是暂时的)

    一个更好的快速修复方法是加载持久的NamedEntityGroup

    NamedEntityGroup group = em.getReference(NamedEntityGroup.class, ne.getNamedEntityGroup().getId());
    ne.setNamedEntityGroup(group);