首页 文章

JPA eager fetch不加入

提问于
浏览
97

JPA的获取策略到底控制了什么?我无法发现渴望和懒惰之间的任何区别 . 在这两种情况下,JPA / Hibernate都不会自动加入多对一关系 .

示例:Person有一个地址 . 地址可以属于很多人 . JPA带注释的实体类看起来像:

@Entity
public class Person {
    @Id
    public Integer id;

    public String name;

    @ManyToOne(fetch=FetchType.LAZY or EAGER)
    public Address address;
}

@Entity
public class Address {
    @Id
    public Integer id;

    public String name;
}

如果我使用JPA查询:

select p from Person p where ...

JPA / Hibernate生成一个SQL查询以从Person表中进行选择,然后为 each person生成一个不同的地址查询:

select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3

这对于大型结果集来说非常糟糕 . 如果有1000个人,则会生成1001个查询(1个来自Person,1000个不同于Address) . 我知道这是因为我正在查看MySQL的查询日志 . 我的理解是,将地址的提取类型设置为eager会导致JPA / Hibernate自动使用连接进行查询 . 但是,无论获取类型如何,它仍会为关系生成不同的查询 .

只有当我明确告诉它加入时它才真正加入:

select p, a from Person p left join p.address a where ...

我在这里错过了什么吗?我现在必须手动编写每个查询的代码,以便它离开加入多对一关系 . 我正在使用Hibernate的JPA实现与MySQL .

Edit: 出现(参见Hibernate FAQ herehereFetchType 不会影响JPA查询 . 所以在我的情况下,我明确告诉它加入 .

8 回答

  • 1

    "mxc"是对的 . fetchType 只是指定了 when 应该解决的关系 .

    要通过使用外部联接来优化预先加载,您必须添加

    @Fetch(FetchMode.JOIN)
    

    到你的领域 . 这是一个特定于hibernate的注释 .

  • 7

    fetchType属性控制在获取主实体时是否立即获取带注释的字段 . 它不一定决定如何构造fetch语句,实际的sql实现取决于你使用toplink / hibernate等的提供者 .

    如果设置 fetchType=EAGER 这意味着带注释的字段将使用其值与实体中的其他字段同时填充 . 因此,如果您打开一个实体管理器来检索您的人物对象然后关闭实体管理器,则随后执行person.address将不会导致抛出延迟加载异常 .

    如果设置 fetchType=LAZY ,则仅在访问该字段时填充该字段 . 如果您已关闭实体管理器,那么如果您执行person.address,则会抛出延迟加载异常 . 要加载字段,您需要使用em.merge()将实体放回到实体管理器上下文中,然后执行字段访问,然后关闭实体管理器 .

    在构建具有客户订单集合的客户类时,您可能需要延迟加载 . 如果您在想要获取客户列表时检索了客户的每个订单,那么当您只查找客户名称和联系详细信息时,这可能是一项昂贵的数据库操作 . 最好将db访问权限保留到以后 .

    对于问题的第二部分 - 如何让hibernate生成优化的SQL?

    Hibernate应该允许您提供有关如何构建最有效查询的提示,但我怀疑您的表构造有问题 . 表中是否 Build 了关系? Hibernate可能已经决定简单的查询比连接更快,特别是如果缺少索引等 .

  • 87

    JPA没有提供关于将注释映射到选择获取策略的任何规范 . 通常,可以以下面给出的任何一种方式获取相关实体

    • SELECT =>一个根实体查询一个查询相关的映射实体/每个根实体的集合=(n 1)个查询

    • SUBSELECT =>一个根实体查询第二个查询相关映射实体/第一个查询中检索到的所有根实体的集合= 2个查询

    • JOIN =>一个查询,用于获取根实体及其所有映射的实体/集合= 1查询

    所以 SELECTJOIN 是两个极端, SUBSELECT 介于两者之间 . 可以根据她/他的域模型选择合适的策略 .

    默认情况下,JPA / EclipseLink和Hibernate都使用 SELECT . 这可以通过使用以下方式覆盖:

    @Fetch(FetchMode.JOIN) 
    @Fetch(FetchMode.SUBSELECT)
    

    在Hibernate中 . 它还允许使用 @Fetch(FetchMode.SELECT) 显式设置 SELECT 模式,可以使用批量大小调整,例如 @BatchSize(size=10) .

    EclipseLink中的相应注释是:

    @JoinFetch
    @BatchFetch
    
  • 17

    试试:

    select p from Person p left join FETCH p.address a where...
    

    它与JPA2 / EclipseLink类似,但似乎这个功能出现在JPA1 too中:

  • -1

    加入你可以做多件事(使用eclipselink)

    在jpql中

    • 你可以做左连接提取

    • 在命名查询中,您可以指定查询提示

    在TypedQuery中

    • 你可以说类似的东西

    query.setHint("eclipselink.join-fetch", "e.projects.milestones");

    • 还有批量提取提示

    query.setHint("eclipselink.batch", "e.address");

    看到

    http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html

  • 2

    我有这个问题,除了Person类有一个嵌入的键类 . 我自己的解决方案是在查询中加入他们和 remove

    @Fetch(FetchMode.JOIN)

    我的嵌入式id类:

    @Embeddable
    public class MessageRecipientId implements Serializable {
    
        @ManyToOne(targetEntity = Message.class, fetch = FetchType.LAZY)
        @JoinColumn(name="messageId")
        private Message message;
        private String governmentId;
    
        public MessageRecipientId() {
        }
    
        public Message getMessage() {
            return message;
        }
    
        public void setMessage(Message message) {
            this.message = message;
        }
    
        public String getGovernmentId() {
            return governmentId;
        }
    
        public void setGovernmentId(String governmentId) {
            this.governmentId = governmentId;
        }
    
        public MessageRecipientId(Message message, GovernmentId governmentId) {
            this.message = message;
            this.governmentId = governmentId.getValue();
        }
    
    }
    
  • 42

    如果您使用EclipseLink而不是Hibernate您可以通过"query hints"优化查询 . 请参阅Eclipse Wiki中的这篇文章:EclipseLink/Examples/JPA/QueryOptimization .

    有一章关于“加入阅读” .

  • 36

    我发生了两件事 .

    首先,你确定你的意思是ManyToOne的地址吗?这意味着多人将拥有相同的地址 . 如果为其中一个编辑它,它将被编辑为所有这些 . 这是你的意图吗? 99%的时间地址是“私人的”(从某种意义上说,它们只属于一个人) .

    其次,你对Person实体有任何其他渴望的关系吗?如果我没记错的话,Hibernate只能在一个实体上处理一个急切的关系,但这可能是过时的信息 .

    我之所以这么说是因为你对这个应该如何运作的理解在我坐的地方基本上是正确的 .

相关问题