FetchMode Join vs SubSelect

后端 未结 4 1620
闹比i
闹比i 2020-12-05 05:28

I have two tables Employee and Department following are the entity classes for both of them

Department.java
@Entity
@Table(name = \"DEPARTMENT\")
public clas         


        
4条回答
  •  北荒
    北荒 (楼主)
    2020-12-05 06:02

    Planky said

    (1) This is grossly misleading. (2) The subselect will not fetch your whole database into memory. The linked article is about a quirk where subselect (3) ignores paging commands from the parent, (4) but it is still a subselect.

    1. After your comment I investigated again about FetchMode.SUBSELECT and I found out that my answer is not entirely correct.
    2. This was an hypothetical situation where the hydration of each entity that was being entirely loaded into memory (Employee in this case) will ending hydrating many other entities. The true problem is loading the entire table being sub-selected if that table contains thousands of rows (even if each one of those doesn't fetch eagerly other entities from other tables).
    3. I don't know what you mean with paging commands from the parent.
    4. Yes, it is still a subselect, but I don't know what you are trying to point out with this.

    The console output that you've posted about fetchmode.subselect is curious because this is not the way that is supposed to work.

    This is true but only when there is more than Department entity hidrated (what means more than one employees collection uninitialized), I've tested it with 3.6.10.Final and 4.3.8.Final In scenarios 2.2 (FetchMode.SUBSELECT hidrating 2 of 3 Departments) and 3.2 (FetchMode.SUBSELECT hidrating all Departments), SubselectFetch.toSubselectString returns the following (the links to Hibernate classes are taken from the 4.3.8.Final tag):

    select this_.DEPARTMENT_ID from SUBSELECT_DEPARTMENT this_
    

    This subquery is after used to build the where clause by OneToManyJoinWalker.initStatementString ending with

    employees0_.DEPARTMENT_ID in (select this_.DEPARTMENT_ID from SUBSELECT_DEPARTMENT this_)
    

    Then the where clause is added in CollectionJoinWalker.whereString ending with

    select employees0_.DEPARTMENT_ID as DEPARTMENT3_2_1_, employees0_.EMPLOYEE_ID as EMPLOYEE1_1_, employees0_.EMPLOYEE_ID as EMPLOYEE1_3_0_, employees0_.DEPARTMENT_ID as DEPARTMENT3_3_0_, employees0_.EMPLOYEE_NAME as EMPLOYEE2_3_0_ from SUBSELECT_EMPLOYEE employees0_ where employees0_.DEPARTMENT_ID in (select this_.DEPARTMENT_ID from SUBSELECT_DEPARTMENT this_)
    

    Whit this query, in both cases all Employees are being retrieved and hydrated. This is clearly an issue in scenario 2.2 because we are hydrating only Departments 1 and 2 but also hydrating all Employees even if they don't belong to those Departments (in this case Employees of Department 3).

    If there is only one Department entity hydrated in the session with its employees collection uninitialized, then the query is like the one eatSleepCode wrote. Check scenario 1.2

    select subselectd0_.department_id as departme1_2_0_, subselectd0_.department_name as departme2_2_0_, subselectd0_.location as location3_2_0_ from subselect_department subselectd0_ where subselectd0_.department_id=?
    

    From FetchStyle

        /**
         * Performs a separate SQL select to load the indicated data.  This can either be eager (the second select is
         * issued immediately) or lazy (the second select is delayed until the data is needed).
         */
        SELECT,
        /**
         * Inherently an eager style of fetching.  The data to be fetched is obtained as part of an SQL join.
         */
        JOIN,
        /**
         * Initializes a number of indicated data items (entities or collections) in a series of grouped sql selects
         * using an in-style sql restriction to define the batch size.  Again, can be either eager or lazy.
         */
        BATCH,
        /**
         * Performs fetching of associated data (currently limited to only collections) based on the sql restriction
         * used to load the owner.  Again, can be either eager or lazy.
         */
        SUBSELECT
    

    Until now, I couldn't resolve what this Javadoc means with:

    based on the sql restriction used to load the owner

    UPDATE Planky said:

    Instead, it's just going to load the table at the worst, and even then, only if your initial query didn't have a where clause. So I would say that using subselect queries can unexpectedly load the whole table if you're LIMITing the results and you don't have any WHERE criteria.

    This is true and it is a very important detail that I've tested in the new scenario 4.2

    The query generated to fetch employees is

    select employees0_.department_id as departme3_4_1_, employees0_.employee_id as employee1_5_1_, employees0_.employee_id as employee1_5_0_, employees0_.department_id as departme3_5_0_, employees0_.employee_name as employee2_5_0_ from subselect_employee employees0_ where employees0_.department_id in (select this_.department_id from subselect_department this_ where this_.department_name>=?)
    

    The subquery inside the where clause contains the original restriction this_.department_name>=?, avoiding the load of all Employees. This is what the javadoc means with

    based on the sql restriction used to load the owner

    All what I've said about FetchMode.JOIN and the differences with FetchMode.SUBSELECT remains true (and also applies for FetchMode.SELECT).

提交回复
热议问题