【问题标题】:JPA bidirectional relations: structure in DatabaseJPA 双向关系:数据库中的结构
【发布时间】:2014-06-28 01:17:01
【问题描述】:

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

@OneToMany(cascade=ALL, mappedBy="user")
    public List<Thing> getThings() {
    return things;
}

以及具有属性的实体“事物”

@ManyToOne
public Cook4User getUser() {
    return user;
}

然后我保留了一个用户并向其添加了一个“事物”。一切都好,我可以看到两个表“用户”和“事物”,每个表都有一个条目,第二个表有一个表示用户 ID 的外键。 然后我从“Thing”表中删除了元素,运行了一个 select 语句来恢复用户,在其上调用 getThings() 方法,然后……我仍然找到了从 Thing 表中删除的元素!这怎么可能?它存储在哪里?我在数据库中看不到它!感谢您为我解决问题。

编辑:我试图隔离产生问题的代码行。

@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!

【问题讨论】:

  • 当你说'然后我从“事物”表中删除了元素'时,你是否使用类似delete from Thing... 的 SQL 查询从实际数据库中删除了记录或通过 JPA?
  • 我用 JPA 做到了。像往常一样,我从 EJB 中的 PersistenceContext 调用 EntityManager,在 Thing 表中查找元素,然后使用 remove() 方法将其删除。然后我通过 Netbeans 中的 select 语句检查了 DB,Thing 表实际上是空的。
  • 没有代码很难帮助。您介意添加重现此问题的相关代码的 sn-ps 吗?
  • 我添加了一些代码,如果您需要更多代码,请告诉我。谢谢。

标签: jpa glassfish java-ee-6


【解决方案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,我会理解,但事实并非如此。此外,在某处为Thing 调用em.remove(thing) 后,我期待delete 查询,但没有(我添加了em.flush() 以尝试强制执行)。

也许其他人可以阐明这里发生的更精细的机制?

【讨论】:

  • 感谢您的回答,这对我来说仍然是一个谜……而且显然没有多少人在使用 JPA!
猜你喜欢
  • 2020-12-07
  • 2015-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多