【问题标题】:JPA: auto loading of child objects doesn't work as expectedJPA:子对象的自动加载无法按预期工作
【发布时间】:2012-08-17 07:16:39
【问题描述】:

我有以下两个实体:

class Role {
  @Id Long id;
  @Column String email;
  String role;
  @ManyToOne User user;
}

class User {
  @Id Long id;
  @Column(unique=true) String email;
  String password;

  @OneToMany(mappedBy = "user")
  @OrderBy("role asc")
  List<Role> roles;
}

SQL 是:

CREATE TABLE USER (
ID BIGINT NOT NULL, 
EMAIL VARCHAR(255) UNIQUE, 
PASSWORD VARCHAR(255), 
PRIMARY KEY (ID)
)
CREATE TABLE ROLE (
ID BIGINT NOT NULL, 
EMAIL VARCHAR(255),
ROLE VARCHAR(255), 
USER_ID BIGINT, 
PRIMARY KEY (ID),
FOREIGN KEY (USER_ID) REFERENCES USER(ID)
)

以下 JUnit 在倒数第二行失败。我从孩子那里自动加载了用户(父母),但我没有从父母那里自动加载孩子。我做错了什么?

    Role r = em.createQuery("select r from Role r where r.email = :email", Role.class)
            .setParameter("email", "john@google.com")
            .getSingleResult();
    assertEquals("registered", r.role());
    assertEquals("john@google.com", r.user().email());

    User u = em.createQuery("select u from User u where u.email = :email", User.class)
            .setParameter("email", "john@google.com")
            .getSingleResult();

    assertEquals("john@google.com", u.email());
    assertEquals("asdf", u.password());

    assertEquals(1, u.roles().size()); FAILS HERE!!!
    assertEquals("registered", u.roles().get(0).role());

我正在使用 EclipseLink 2.3.2 (JPA)

【问题讨论】:

    标签: jpa persistence jpa-2.0 eclipselink


    【解决方案1】:

    有多种方法可以告诉您应该如何加载另一个和一个关系。一种是在查询级别,使用 fetch join,如下所示:

    select u from User u left join FETCH u.roles r where...
    

    另一个在关系声明级别,如下所示:

    class User {
      @Id Long id;
      @Column(unique=true) String email;
      String password;
    
      @OneToMany(mappedBy = "user", fetch=FetchType.EAGER)
      @OrderBy("role asc")
      List<Role> roles;
    }
    

    当您加载角色时,您也会在没有任何特殊注释或加入查询的情况下获得用户,原因是 JPA 中的 ManyToOne 关系默认具有 fetch=EAGER:

    http://docs.oracle.com/javaee/5/api/javax/persistence/ManyToOne.html#fetch()

    虽然一对多默认设置为 fetch=lazy:

    http://docs.oracle.com/javaee/5/api/javax/persistence/OneToMany.html#fetch()

    【讨论】:

    • 如果我将 Role 修改为也具有 FetchType.EAGER,则没有区别。事实上,我理解它的方式,以及它过去与 Hibernate 一起工作的方式,LAZY 只是意味着在调用 getRoles 方法之前不会执行获取子节点的 SQL。代理拦截该方法调用并执行 SQL 并将数据注入返回的集合中。没有?
    • 是的,差不多就是这样。诀窍是代理(实际上是 Hibernate/JPA 包装器)只有在用于获​​取它的会话仍然打开时才能执行此操作。如果没有,你会得到可怕的延迟初始化异常。更不用说使用延迟获取是 n+1 选择的经典示例(为每个延迟初始化的关系进行选择)。如果您问我,最好的办法是在查询时急切获取您需要的所有数据。
    • 好吧,我可能在这里遗漏了一些东西,但在我看来,您有一个会加载的 ManyToOne 关系和一个不会即时加载的 OneToMany 关系,默认配置。这几乎就是他们根据文档应该如何表现的方式。为了让您的 JUnit 测试通过,即在您获取根目录时加载您的 OneToMany 关系,您必须做一些额外的工作。
    猜你喜欢
    • 2017-11-20
    • 2013-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多