开发者

Why does TypedQuery.getResultList() resolve every ManyToOne association with a seperate SELECT?

开发者 https://www.devze.com 2023-01-24 08:41 出处:网络
Considering the following simple entity association: (EntityA) *-1 (EntityB) made in database with a foreign key in EntityA (entityB_id).

Considering the following simple entity association: (EntityA) *-1 (EntityB) made in database with a foreign key in EntityA (entityB_id).

The JPA Entities are mapping this relationship unidirectional:

@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 ...
}

If a simple query is made:

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

I see in the SQL debug output of Hibernate, that an EntityB query is done for every row of EntityA:

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=?

Even if the default fetch strategy is EAGER (which seems to be the case), EntityB should be fetched via implizit join, shouldn't it? What is wrong?

But it's getting even more weird - if only a single EntityA object is loaded:

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

Then Hibernate seems to understand the job:

Hibernate: 
    select
        entitya0_.id as id1_1_,
        entitya开发者_如何学运维0_.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=?

The above tests have been made with Hibernate 3.5 and JPA 2.0.


Even if the default fetch strategy is EAGER (which seems to be the case), EntityB should be fetched via implicit join, shouldn't it? What is wrong?

Indeed, the default FetchType of a ManyToOne is EAGER. But this just says that One side should get loaded when the Many side is loaded, not how. The how is left at the discretion of the persistence provider (and JPA doesn't allow to tune the strategy).

Hibernate has a specific Fetch annotations allowing to tune the fetching mode though. From the documentation:

2.4.5.1. Lazy options and fetching modes

JPA comes with the fetch option to define lazy loading and fetching modes, however Hibernate has a much more option set in this area. To fine tune the lazy loading and fetching strategies, some additional annotations have been introduced:

  • [...]

  • @Fetch: defines the fetching strategy used to load the association. FetchMode can be SELECT (a select is triggered when the association needs to be loaded), SUBSELECT (only available for collections, use a subselect strategy - please refers to the Hibernate Reference Documentation for more information) or JOIN (use a SQL JOIN to load the association while loading the owner entity). JOIN overrides any lazy attribute (an association loaded through a JOIN strategy cannot be lazy).

You might want to try the following (if you don't mind using provider specific annotations):

@ManyToOne(optional=false)
@Fetch(FetchMode.JOIN)
private EntityB entityB;


The solution that worked for the current use case is to include a fetch join in the statement:

select a from entityA left join fetch a.entityB

This will fetch all associated EntityBs (and override a FetchType.LAZY).


First of all, why are you even worrying about this? The point of using an ORM library is so you don't have to deal with details like this. If you see that some operation is running slowly, then you start thinking about optimizing queries.

I think that Hibernate's strategy makes sense, since the relationship is ManyToOne. This means that the number of EntityBs returned in the query will never be greater than the number of EntityAs, and will usually be less. So trying to fetch the data from EntityB via a join would probably mean sending redundant copies of the same data.

0

精彩评论

暂无评论...
验证码 换一张
取 消