FetchMode和FetchType

原文:http://www.jianshu.com/p/23bd82a7b96e


 
 

Entity:

Entity City and Hotel, One-to-Many 双向连接.

@Entity
public class City {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String country;

    @OneToMany(mappedBy="city")
    private Set<Hotel> hotles;

    ...

}
@Entity
public class Hotel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(optional = false)
    @NaturalId
    private City city;

    @Column(nullable = false)
    @NaturalId
    private String name;

    @Column(nullable = false)
    private String address;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "hotel")
    private Set<Review> reviews;

    ...

}



Data:

insert into city(country, name) values ('Australia', 'Brisbane')
insert into city(country, name) values ('Canada', 'Montreal')
insert into city(country, name) values ('Australia', 'Melbourne')
insert into city(country, name) values ('Israel', 'Tel Aviv')

insert into hotel(city_id, name, address) values (1, 'Hotel_A', 'Street_A')
insert into hotel(city_id, name, address) values (1, 'Hotel_B', 'Street_A')
insert into hotel(city_id, name, address) values (1, 'Hotel_C', 'Street_C')
insert into hotel(city_id, name, address) values (2, 'Hotel_D', 'Street_D')
insert into hotel(city_id, name, address) values (2, 'Hotel_E', 'Street_E')
insert into hotel(city_id, name, address) values (3, 'Hotel_F', 'Street_F')
insert into hotel(city_id, name, address) values (3, 'Hotel_G', 'Street_G')
insert into hotel(city_id, name, address) values (3, 'Hotel_H', 'Street_H')
insert into hotel(city_id, name, address) values (3, 'Hotel_I', 'Street_I')
insert into hotel(city_id, name, address) values (3, 'Hotel_J', 'Street_J')
insert into hotel(city_id, name, address) values (4, 'Hotel_K', 'Street_K')



Code causes N+1:

criteria.list()发送一条sql获取所有City, 循环执行city.getHotles().size()时发送N条sql获取Hotel

    Session session = sessionFactory.openSession();
    Transaction transaction = session.beginTransaction();

    Criteria criteria = session.createCriteria(City.class);
    //one sql
    List<City> cityList = criteria.list();
    for(City city : cityList){
        //N sql
        System.out.println(city.getHotles().size());
    }

    transaction.commit();
    session.close();

output:

2017-02-25 12:00:45.630 DEBUG 5460 --- [nio-8080-exec-5] org.hibernate.SQL                        : select this_.id as id1_0_0_, this_.country as country2_0_0_, this_.name as name3_0_0_ from City this_
Hibernate: select this_.id as id1_0_0_, this_.country as country2_0_0_, this_.name as name3_0_0_ from City this_
2017-02-25 12:00:45.632 DEBUG 5460 --- [nio-8080-exec-5] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
3
2017-02-25 12:00:45.634 DEBUG 5460 --- [nio-8080-exec-5] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
2
2017-02-25 12:00:45.636 DEBUG 5460 --- [nio-8080-exec-5] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
5
2017-02-25 12:00:45.638 DEBUG 5460 --- [nio-8080-exec-5] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
1



FetchType.LAZY and @Fetch(FetchMode.SELECT):

City中加上fetch=FetchType.LAZY@Fetch(FetchMode.SELECT), 输出结果与上面相同,说明one-to-many默认设置是fetch=FetchType.LAZY@Fetch(FetchMode.SELECT)
下面四种配置等效,都是N+1条sql的懒加载:

@OneToMany(mappedBy="city")
private Set<Hotel> hotles;
@OneToMany(mappedBy="city")
@Fetch(FetchMode.SELECT)
private Set<Hotel> hotles;
@OneToMany(mappedBy="city", fetch=FetchType.LAZY)
private Set<Hotel> hotles;
@OneToMany(mappedBy="city", fetch=FetchType.LAZY)
@Fetch(FetchMode.SELECT)
private Set<Hotel> hotles;



FetchType.Eager and @Fetch(FetchMode.SELECT):

City中加上fetch=FetchType.Eager@Fetch(FetchMode.SELECT)

@OneToMany(mappedBy="city", fetch=FetchType.Eager)
@Fetch(FetchMode.SELECT)
private Set<Hotel> hotles;

output:

2017-02-25 14:13:45.455 DEBUG 5800 --- [nio-8080-exec-1] org.hibernate.SQL                        : select this_.id as id1_0_0_, this_.country as country2_0_0_, this_.name as name3_0_0_ from City this_
Hibernate: select this_.id as id1_0_0_, this_.country as country2_0_0_, this_.name as name3_0_0_ from City this_
2017-02-25 14:13:45.473 DEBUG 5800 --- [nio-8080-exec-1] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
2017-02-25 14:13:45.482 DEBUG 5800 --- [nio-8080-exec-1] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
2017-02-25 14:13:45.485 DEBUG 5800 --- [nio-8080-exec-1] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
2017-02-25 14:13:45.486 DEBUG 5800 --- [nio-8080-exec-1] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
Hibernate: select hotles0_.city_id as city_id4_0_0_, hotles0_.id as id1_1_0_, hotles0_.id as id1_1_1_, hotles0_.address as address2_1_1_, hotles0_.city_id as city_id4_1_1_, hotles0_.name as name3_1_1_ from Hotel hotles0_ where hotles0_.city_id=?
3
2
5
1

同样是N+1条sql,不过和上面情况不同的是,N条sql会在criteria.list()时执行


FetchMode.JOIN:

City中加上@Fetch(FetchMode.JOIN), 那么Hibernate将强行设置为fetch=FetchType.EAGER, 用户设置fetch=FetchType.LAZY将不会生效.
下面四种设置等效:

@OneToMany(mappedBy="city", fetch=FetchType.EAGER)
@Fetch(FetchMode.JOIN)
private Set<Hotel> hotles;
@OneToMany(mappedBy="city", fetch=FetchType.LAZY)
@Fetch(FetchMode.JOIN)
private Set<Hotel> hotles;
@OneToMany(mappedBy="city")
@Fetch(FetchMode.JOIN)
private Set<Hotel> hotles;
@OneToMany(mappedBy="city", fetch=FetchType.EAGER)
private Set<Hotel> hotles;

output:

2017-02-25 13:17:07.583 DEBUG 2964 --- [nio-8080-exec-1] org.hibernate.SQL                        : select this_.id as id1_0_1_, this_.country as country2_0_1_, this_.name as name3_0_1_, hotles2_.city_id as city_id4_0_3_, hotles2_.id as id1_1_3_, hotles2_.id as id1_1_0_, hotles2_.address as address2_1_0_, hotles2_.city_id as city_id4_1_0_, hotles2_.name as name3_1_0_ from City this_ left outer join Hotel hotles2_ on this_.id=hotles2_.city_id
Hibernate: select this_.id as id1_0_1_, this_.country as country2_0_1_, this_.name as name3_0_1_, hotles2_.city_id as city_id4_0_3_, hotles2_.id as id1_1_3_, hotles2_.id as id1_1_0_, hotles2_.address as address2_1_0_, hotles2_.city_id as city_id4_1_0_, hotles2_.name as name3_1_0_ from City this_ left outer join Hotel hotles2_ on this_.id=hotles2_.city_id
3
3
3
2
2
5
5
5
5
5
1

从输出可看出,在执行criteria.list()时通过一条sql 获取了所有的City和Hotel。
使用@Fetch(FetchMode.JOIN)需要注意的是:它在Join查询时是Full Join, 所以会有重复City出现


FetchMode.SUBSELECT:

City中加上@Fetch(FetchMode.SUBSELECT), 那么Hibernate将强行设置为fetch=FetchType.EAGER, 用户设置fetch=FetchType.LAZY将不会生效.
下面三种设置等效:

@OneToMany(mappedBy="city", fetch=FetchType.EAGER)
@Fetch(FetchMode.SUBSELECT)
private Set<Hotel> hotles;
@OneToMany(mappedBy="city", fetch=FetchType.LAZY)
@Fetch(FetchMode.SUBSELECT)
private Set<Hotel> hotles;
@OneToMany(mappedBy="city")
@Fetch(FetchMode.SUBSELECT)
private Set<Hotel> hotles;

output:

2017-02-25 13:45:06.089 DEBUG 4004 --- [nio-8080-exec-1] org.hibernate.SQL                        : select this_.id as id1_0_0_, this_.country as country2_0_0_, this_.name as name3_0_0_ from City this_
Hibernate: select this_.id as id1_0_0_, this_.country as country2_0_0_, this_.name as name3_0_0_ from City this_
2017-02-25 13:45:06.114 DEBUG 4004 --- [nio-8080-exec-1] org.hibernate.SQL                        : select hotles0_.city_id as city_id4_0_1_, hotles0_.id as id1_1_1_, hotles0_.id as id1_1_0_, hotles0_.address as address2_1_0_, hotles0_.city_id as city_id4_1_0_, hotles0_.name as name3_1_0_ from Hotel hotles0_ where hotles0_.city_id in (select this_.id from City this_)
Hibernate: select hotles0_.city_id as city_id4_0_1_, hotles0_.id as id1_1_1_, hotles0_.id as id1_1_0_, hotles0_.address as address2_1_0_, hotles0_.city_id as city_id4_1_0_, hotles0_.name as name3_1_0_ from Hotel hotles0_ where hotles0_.city_id in (select this_.id from City this_)
3
2
5
1

从输出可看出,在执行criteria.list()时通过两条sql分别获取City和Hotel。


Summary:

FetchMode\FetchTypeLazyEagerNull
SelectABA
JoinCCC
SubselectDDD
NullACA

十二种排列组合,但最后只会有四种情况,如上标注的A,B,C,D:
A. FetchMode.SELECTFetchType.LAZY,都不设置,都设置,设置任一, 都将会是情况A。情况A会出现N+1条sql,其中N条是懒加载。
B. FetchMode.SELECTFetchType.EAGER同时设置时出现B情况,同样是N+1条sql,和A情况不同的是N条sql会立即执行。
C. 只要设置了FetchMode.JOIN,不管FetchType设置成什么(FetchType.EAGER,FetchType.LAZY,不设置FetchType),都将会是C情况。同时,只设置FetchType.EAGER不设置FetchMode也将会是C情况。C情况下只产生一条立即执行的Full-Join的sql, parent数据可能会重复。
D. 只要设置了FetchMode.SUBSELECT,不管FetchType设置成什么(FetchType.EAGER,FetchType.LAZY,不设置FetchType),都将会是D情况。D情况下产生两条立即执行的sql, 分别读取parent表和child表。



作者:昵称全尼马被注册了
链接:http://www.jianshu.com/p/23bd82a7b96e
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值