代码之家  ›  专栏  ›  技术社区  ›  Zeemee

为什么typedquery.getresultlist()通过单独的select解析每个manytoone关联?

  •  6
  • Zeemee  · 技术社区  · 15 年前

    考虑到以下简单的实体关联: (实体A)*-1(实体B) 在数据库中用EntityA(EntityB_ID)中的外键生成。

    JPA实体正在单向映射此关系:

    @Entity
    EntityA {
        @Id
        @GeneratedValue
        private long id;
    
        @Column(nullable=false,length=250)
        private String name;
    
        @ManyToOne(optional=false)
        private EntityB entityB;
    
        ... getter/setter ...
    }
    
    @Entity
    EntityB {
        @Id
        @GeneratedValue
        private long id;
    
        @Column(nullable=false,length=250)
        private String name;
    
        ... getter/setter ...
    }
    

    如果进行简单查询:

    EntityManager em = ...;
    TypedQuery<EntityA> tq = em.createQuery("from EntityA a", EntityA.class);
    tq.getResultList();
    

    我在Hibernate的SQL调试输出中看到,每行EntityA都会执行EntityB查询:

    Hibernate: 
        select
            entitya0_.id as id8_,
            entitya0_.entityB_id as entityB3_8_,
            entitya0_.name as name8_ 
        from
            EntityA entitya0_
    Hibernate: 
        select
            entityb0_.id as id4_0_,
            entityb0_.name as name4_0_ 
        from
            EntityB entityb0_ 
        where
            entityb0_.id=?
    

    即使默认的获取策略是热切的(看起来是这样),EntityB也应该通过implizit join来获取,不是吗? 怎么了?

    但是,如果只加载一个EntityA对象,这会变得更加奇怪:

    EntityA a = em.find(EntityA.class, new Long(1));
    

    然后Hibernate似乎理解了工作:

    Hibernate: 
        select
            entitya0_.id as id1_1_,
            entitya0_.entityB_id as entityB3_1_1_,
            entitya0_.name as name1_1_,
            entityb1_.id as id12_0_,
            entityb1_.name as name12_0_ 
        from
            EntityA entitya0_ 
        inner join
            EntityB entityb1_ 
            on entitya0_.entityB_id=entityb1_.id 
        where
            entitya0_.id=?
    

    以上测试是用Hibernate 3.5和JPA 2.0进行的。

    3 回复  |  直到 15 年前
        1
  •  5
  •   Pascal Thivent    15 年前

    即使默认的获取策略是热切的(看起来是这样),EntityB也应该通过隐式连接来获取,不是吗?怎么了?

    事实上,违约 FetchType A的 ManyToOne EAGER . 但这只是说 One 侧面应加载 什么时候? 这个 Many 侧装,不是 怎样 . 方法由持久性提供者决定(JPA不允许调整策略)。

    Hibernate有一个特定的 Fetch 但是,允许优化提取模式的注释。从文档中:

    2.4.5.1. Lazy options and fetching modes

    JPA附带了获取选项 定义延迟加载和提取 但是休眠模式有很多 在此区域中设置了更多选项。到罚款 调整延迟加载和获取 策略,一些附加的 已引入注释:

    • […]

    • @Fetch :定义用于加载关联的提取策略。 FetchMode 可以是 SELECT (选择是 当关联需要时触发 待加载) SUBSELECT (仅) 可用于收藏,请使用 再选择策略-请参考 休眠参考文档 更多信息)或 JOIN (用A sql 加入 加载关联的同时 正在加载所有者实体)。 加入 覆盖任何惰性属性(一个 通过联接加载的关联 战略不能懒惰)。

    您可能需要尝试以下操作(如果不介意使用特定于提供程序的注释):

    @ManyToOne(optional=false)
    @Fetch(FetchMode.JOIN)
    private EntityB entityB;
    
        2
  •  4
  •   Zeemee    15 年前

    为当前用例工作的解决方案是在语句中包含fetch join:

    select a from entityA left join fetch a.entityB
    

    这将获取所有关联的EntityB(并重写FetchType.Lazy)。

        3
  •  0
  •   Mike Baranczak    15 年前

    首先,你为什么还要担心这个?使用ORM库的要点是,您不必处理这样的细节。如果你看到一些操作运行缓慢, 然后 您开始考虑优化查询。

    我认为Hibernate的策略是有意义的,因为这种关系很重要。这意味着查询中返回的EntityB的数量永远不会大于EntityA的数量,并且通常会更少。因此,尝试通过联接从EntityB获取数据可能意味着发送相同数据的冗余副本。