JPA双向关系:数据库中的结构

时间:2014-06-28 01:17:01

标签: jpa glassfish java-ee-6

我开始在Netbeans中使用带有Glassfish服务器+ Derby数据库的JPA,我对它的行为有些怀疑。我做了以下实验:我使用属性

定义了一个实体“User”
@OneToMany(cascade=ALL, mappedBy="user")
    public List<Thing> getThings() {
    return things;
}

和具有属性

的实体“Thing”
@ManyToOne
public Cook4User getUser() {
    return user;
}

然后我坚持一个用户并为其添加了一个“Thing”。一切都好,我可以看到两个表“User”和“Thing”,每个表有一个条目,第二个表有一个指示用户ID的外键。 然后我从“Thing”表中删除了元素,运行了一个select语句来恢复用户,在它上面调用了getThings()方法并且......我仍然找到了我从Thing表中删除的元素!这怎么可能?它存放在哪里?我无法在DB中看到它!谢谢你清理我的事情。

编辑:我试图找出产生问题的代码行。

@PersistenceContext
private EntityManager em;

User user = new User();
em.persist(user);
Thing thing = new Thing();
em.persist(thing);
user.getThings().add(thing);
em.remove(thing);
user = em.find(User.class, userid);
logger.log(Level.INFO, "user still contains {0} things", user.getThings().size());
\\thing is still there!

1 个答案:

答案 0 :(得分:1)

以SpringRunner和Hibernate作为JPA实现的JUnit测试形式:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-context.xml")
public class TestThings {

    private final Logger log = LoggerFactory.getLogger(TestThings.class);

    @PersistenceContext
    private EntityManager em;

    @Test
    @Transactional
    public void does_not_remove_thing() {
        Cook4User user = new Cook4User();
        em.persist(user);

        Thing thing = new Thing();
        em.persist(thing);
        user.getThings().add(thing);

        user = em.find(Cook4User.class, user.getId());
        user.getThings().forEach((t) -> log.info("1 >> Users thing: {}", t.getId()));

        em.remove(thing);
        em.flush();

        user = em.find(Cook4User.class, user.getId());
        user.getThings().forEach((t) -> log.info("2 >> Users thing: {}", t.getId()));

        assertThat(user.getThings()).isEmpty();
    }

    @Test
    @Transactional
    public void removes_thing_when_removed_from_owning_side() {
        Cook4User user = new Cook4User();
        em.persist(user);

        Thing thing = new Thing();
        em.persist(thing);
        user.getThings().add(thing);

        user = em.find(Cook4User.class, user.getId());
        user.getThings().forEach((t) -> log.info("1 >> Users thing: {}", t.getId()));

        user.getThings().remove(thing);

        user = em.find(Cook4User.class, user.getId());
        user.getThings().forEach((t) -> log.info("2 >> Users thing: {}", t.getId()));

        assertThat(user.getThings()).isEmpty();
    }
}

第一个测试does_not_remove_thing是根据您的问题而失败的,因为您经历过,这是hibernate.show_sql日志记录设置为true的测试的输出:

Hibernate: 
    insert 
    into
        Cook4User
        (id) 
    values
        (null)
Hibernate: 
    insert 
    into
        Thing
        (id, user_id) 
    values
        (null, ?)
[main] INFO  TestThings - 1 >> Users thing: 1
[main] INFO  TestThings - 2 >> Users thing: 1
java.lang.AssertionError: expecting empty but was:<[x.Thing@276]>

第二个测试removes_thing_when_removed_from_owning_side传递输出:

Hibernate: 
    insert 
    into
        Cook4User
        (id) 
    values
        (null)
Hibernate: 
    insert 
    into
        Thing
        (id, user_id) 
    values
        (null, ?)
[main] INFO  TestThings - 1 >> Users thing: 1

所以看起来将你的Thing从关系的所有方面移除(他是)是可行的方法。

虽然,我必须诚实,但我不确定为什么这样做有效,而你的方式则不然。我会理解你是否从一个独立的实体中移除了Thing但事实并非如此。此外,我在delete为某个地方em.remove(thing)致电Thing后仍然期待em.flush()查询,但没有(我添加了{{1}}来尝试强制执行此操作)。

也许其他人可以对这里正在发生的事情的更精细的机制有所了解?