【问题标题】:JPA EAGER fetch works only if server is restartedJPA EAGER fetch 仅在服务器重新启动时才有效
【发布时间】:2013-03-11 20:59:12
【问题描述】:

大家晚上好,这是我在 Stack Overflow 上的第一篇文章。 我最近才被介绍到 Java 6 EE,特别是作为 JSF 2.1 框架的一部分的 JPA,我现在面临着一个奇怪的行为,我希望你能帮助我理解。 在我们的项目(使用 NetBeans 7.2 开发)中,我们有几个一对多的关系,我们希望以与导航多对一相同的方式导航它们。事实是,相反,只有在重新启动应用程序服务器(Glassfish 3.1.2)之后,我们才能让它们按照我们的意愿工作,此外,这种行为只会持续到下一次部署;这意味着我们每次应用修改时都需要重新启动 Glassfish... 以下是一些代码摘录,可帮助您了解我们的情况。

这代表我们的主要实体(Person),除其他外,它与 Email 和 Phone 是一对多的关系,并且与 AccountType 是多对一的关系

@Entity
public class Person implements Serializable {
    //
    //private non-collection fields including id 
    //

    @OneToMany(mappedBy="person", fetch=FetchType.EAGER)
    private Collection<Email> personEmails;

    @OneToMany(mappedBy="person")
    private Collection<Phone> personPhones;

    @ManyToOne
    private AccountType accountType;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

这些是电子邮件...

@Entity
public class Email implements Serializable {
    //
    //private non-collection fields including id 
    //

    private String address;

    @ManyToOne
    private Person person;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

...电话...

@Entity
public class Phone implements Serializable {
    //
    //private non-collection fields including id 
    //

    private String number;

    @ManyToOne
    private Person person;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

...和帐户类型

@Entity
public class AccounType implements Serializable {
    //
    //private non-collection fields including id 
    //

    private String name;

    @OneToMany(mappedBy="accountType")
    private Collection<Person> persons;

    //
    // getter and setter, hashCode, isEqual and toString
    //
}

然后我们设置了一个示例页面来测试 Person 中的三个字段是如何实际获取的。

这代表xhtml页面...

<h:form id="form">

    <h:panelGrid columns="2">

        <h:outputLabel value="forename" />
        <h:outputLabel value="#{meBean.currentUser.forename}" />

        <h:outputLabel value="emails" />
        <h:outputLabel value="#{meBean.currentUser.personEmails.size()}" />

        <h:outputLabel value="phones" />
        <h:outputLabel value="#{meBean.currentUser.personPhones}" />

        <h:outputLabel value="accountType" />
        <h:outputLabel value="#{meBean.currentUser.accountType.name}" />

    </h:panelGrid>

</h:form>

...这是控制器

@ManagedBean
@RequestScoped
public class MeBean {

    @EJB
    private PersonFacade personFacade;
    private Person currentUser;

    public MeBean() {
        init();
    }

    @PostConstruct
    private void init() {
        // Hard-coding user details        
        try {
            this.currentUser = this.personFacade.getFromUsername("user1");
            this.currentUser.getPersonPhones().isEmpty();
        } catch (Exception e) {
        }
    }

    public Person getCurrentUser() {
        return currentUser;
    }

    public void setCurrentUser(Person currentUser) {
        this.currentUser = currentUser;
    }
}

现在,只有在重新启动应用程序服务器后立即访问页面时,我们才能得到预期的结果。

forename    Alice
emails      2
phones      {[sums.groupa.entities.Phone[id=38]]}
accountType Student

如果我们修改任何东西(除了视图)并保存,在不可避免的部署之后,结果就会不同。

forename    Alice
emails      0
phones      {[]}
accountType Student

为什么会发生这种情况,我们该如何避免?

提前致谢。

AG

几个贡献者(我要感谢他们的快速回复)要求实现 PersonFacade。

public Person getFromUsername(String username)
    {
        try
        {
            Query q = em.createQuery("SELECT p FROM Person p LEFT JOIN FETCH p.personEmails WHERE UPPER(p.username) = :username");
            q.setParameter("username", username.toUpperCase());
            return (Person) q.getSingleResult();
        }
        catch (Exception ex)
        {
            Logger.getLogger(PersonFacade.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

如您所见,我尝试按照建议使用 FETCH JOIN,但查询得到的结果太多:它应该只获取一个代表 Alice 的 Person 实例,并在 personEmails 字段中包含两个 Email 实例,但我怀疑它是获取两个不同的 Person 实例,每个实例都附加了不同的 Email 实例。

原来的查询如下:

SELECT p FROM Person p WHERE UPPER(p.username) = :username

再次感谢。

AG

【问题讨论】:

  • “如果我们修改任何东西”背后隐藏着什么?你修改什么以及如何修改?门面如何得到人并返回?
  • 我想要的“如果我们修改任何东西”实际上是对托管 bean、会话 bean、实体 bean 或外观所做的每一次修改。这会导致 NetBeans 重新部署项目,并且由于某些原因,这会使急切的 fetch 类型“无效”。对于外观如何实现的问题,我将编辑帖子。谢谢

标签: jpa glassfish fetch restart eager


【解决方案1】:

我不知道你是如何编写 personFacade 并获得 Person 实体的。

但我猜你使用了查询。

如果您使用的是 JPQL,请尝试 fetch join。

【讨论】:

  • 应该需要 JPQL 吗?我会认为 FetchType.EAGER 会排序吗? (尽管 FetchType.LAZY 也应该如此。)
  • JPQL 不是必需的。我相信使用 find() 会让你得到你想要的行为。我的意思是,如果您使用的是 JPQL,请尝试 fetch。
  • 我已经尝试了 Tony Lang 的建议,但结果太多了,前提是我在 JPQL 查询中正确使用了“FETCH JOIN”。谢谢
  • JPQL 的 fetch join 总是返回重复的结果,这是设计,不是误用。请选择 DISTINCT。
  • 非常感谢 Tony,现在使用 SELECT DISTINCT 和 JOIN FETCH 查询也可以正常工作,而无需指定字段提取类型。
猜你喜欢
  • 1970-01-01
  • 2019-08-23
  • 1970-01-01
  • 1970-01-01
  • 2020-08-11
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
相关资源
最近更新 更多