This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Join Fetch

    In a POJO, you can use @JoinFetch on a field or class to filter associated entities. The difference from @Join is that this allows fetching all lazy-related data at once.

    Single Level Fetch

    For example, consider a Customer entity that has a one-to-many relationship with an Order entity:

    @Entity
    class Customer {
    
      @OneToMany(fetch = LAZY, cascade = ALL)
      @JoinColumn(name = "order_id")
      private Collection<Order> orders;
    }
    
    @Entity
    class Order {
      
      private String itemName;
    }
    

    If you want to fetch Order data when retrieving Customer, you can do:

    @Data
    @JoinFetch(paths = "orders")
    class CustomerOrderCriteria {
    
      @Spec
      String name;
    }
    

    The executed SQL will be like:

    select distinct 
      customer0_.* ...,
      orders1_.* ...
    from customer customer0_ 
    inner outer join orders orders1_ on customer0_.id=orders1_.order_id 
    where customer0_.name=?
    

    You can see that orders1_.* is also included in the select fields.

    Join Behavior

    To better align with most usage scenarios, the default behavior is as follows:

    • The default join type is INNER.
    • Duplicate results are removed (distinct).

    You can modify the default behavior by configuring @JoinFetch#joinType or @JoinFetch#distinct, for example:

    @JoinFetch(joinType = JoinType.RIGHT, distinct = false)
    

    With Clause

    @JoinFetch also allows filtering data. For example, if you want to filter both customer names and order names, define a POJO as follows:

    @Data
    @JoinFetch(path = "orders", alias = "o")
    public class CustomerOrderCriteria {
    
      @Spec
      String name;
    
      @Spec(path = "o.itemName", value = In.class)
      Collection<String> items;
    }
    

    The executed SQL will be like:

    select distinct 
      customer0_.* ...,
      orders1_.* ...
    from customer customer0_ 
    inner outer join orders orders1_ on customer0_.id=orders1_.order_id 
    where customer0_.name=? and orders1_.item_name in (?)
    

    @JoinFetch can also be used at the field level, for example:

    @Data
    public class CustomerOrderCriteria {
    
      @Spec
      String name;
    
      @JoinFetch(path = "orders", alias = "o")
      @Spec(path = "o.itemName", value = In.class)
      Collection<String> items;
    }
    

    Multi Level Fetches

    You can use @JoinFetches to define multi-level fetches. For example, if the Order entity has a many-to-many relationship with a Tag entity:

    @Entity
    class Customer {
    
      @OneToMany(cascade = ALL, fetch = LAZY)
      @JoinColumn(name = "order_id")
      private Set<Order> orders;
    }
    
    @Entity
    class Order {
        
      @ManyToMany(cascade = ALL, fetch = LAZY)
      private Set<Tag> tags;
    }
    
    @Entity
    class Tag {
    
      private String name;
    }
    

    If you want to query customers who purchased items belonging to specific categories, define a POJO as follows:

    @Data
    class CustomerOrderTagCriteria {
    
      @JoinFetches({
        @JoinFetch(path = "orders", alias = "o"),
        @JoinFetch(path = "o.tags", alias = "t")
      })
      @Spec(path = "t.name", value = In.class)
      Collection<String> tags;
    }
    

    The executed SQL will be like:

    select distinct 
      customer0_.* ...,
      orders1_.* ...,
      tag3_.* ...
    from customer customer0_ 
    inner join orders orders1_ on customer0_.id=orders1_.order_id 
    inner join orders_tags tags2_ on orders1_.id=tags2_.order_id 
    inner join tag tag3_ on tags2_.tags_id=tag3_.id 
    where tag3_.name in (?)
    

    @JoinFetches can also be used at the class level, making the aliases available across fields in the same object. For example:

    @Data
    @JoinFetches({
      @JoinFetch(path = "orders", alias = "o"),
      @JoinFetch(path = "o.tags", alias = "t")
    })
    public class CustomerOrderCriteria {
    
      @Spec(path = "o.itemName", value = In.class)
      Collection<String> items;
    
      @Spec(path = "t.name", value = In.class)
      Collection<String> tags;
    }
    

    Fetches Order

    Annotations are processed in order, so @JoinFetches must be defined in the correct sequence.

    For example, in the previous scenario, the following order is incorrect:

    @Data
    class CustomerOrderTagCriteria {
    
      @JoinFetches({
        @JoinFetch(path = "o.tags", alias = "t"), // "o" alias will be not exist during processing this @Join
        @JoinFetch(path = "orders", alias = "o")
      })
      @Spec(path = "t.name", value = In.class)
      Collection<String> tagNames;
    }
    

    Alias

    The usage rules for @JoinFetch#alias are as follows:

    • It is shared within the same POJO
    • It cannot be declared multiple times within the same POJO
    • If not provided, the default alias is @JoinFetch#path
    • If it contains ., it will be replaced with _

    For example:

    @JoinFetches({
      @JoinFetch(path = "orders"), // default alias is "orders"
      @JoinFetch(path = "orders.tags") // default alias is "orders_tags"
    })
    @Spec(path = "orders_tags.name", value = In.class)