博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hibernate之关于1+N的问题
阅读量:6758 次
发布时间:2019-06-26

本文共 5233 字,大约阅读时间需要 17 分钟。

    1+N问题,也有人叫做N+1问题,至今未统一,在这里我会告诉大家我为什么称之为1+N问题!

    

    什么情况下会产生1+N问题;


   在实际的项目开发中,我们配置的一对多,或者是多对一,在查询的时候会产生一种现象。


   例如,人(Person)和组(Group)

当我们在查询(多的一方)Hibernate会直接发SQL把相关的(一的一方)也查询出来;


当然,这种情况问题说大也不是特别大,但是,当我们数据量较大,数据库的性能就是不得不考虑的事情,ok?


首先我们得弄明白为什么我list() Person的时候,会发查询Group的语句?我们知道在many-to-oneFetchType默认是EAGEREAGER的意思可以理解为急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。所以Hibenrate加载Person属性值的时候发现Person的有一个字段属性与Group有关联,那么,Hibernate默认就立刻发请求将关联对象取出。


但是相反,我们list Group的时候,我们发现就不会出现这种现象,这是因为one-to-many默认的FetchTyp默认是LAZY,懒加载的意思,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。只有我们需要的时候,比如group.getPersons().size() Hibernate检测你需要之后,才会发SQL请求!


ok,上面说了1+N问题出现的原理,那下面用程序来证明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Entity
@Table
(name=
"t_group"
)
publicclass Group {
    
private 
Integer id;
    
private 
String name;
    
private 
Set<Person> persons=newHashSet<Person>();
     
    
@OneToMany
    
public 
Set<Person> getPersons() {
       
returnpersons;
    
}
    
publicvoid setPersons(Set<Person> persons) {
       
this
.persons = persons;
    
}
    
@Id
    
@GeneratedValue
    
public 
Integer getId() {
       
returnid;
    
}
    
publicvoid setId(Integer id) {
       
this
.id = id;
    
}
    
@Column
(name=
"g_name"
)
    
public 
String getName() {
       
returnname;
    
}
    
publicvoid setName(String name) {
       
this
.name = name;
    
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Entity
@Table
(name=
"p_person"
)
publicclass Person {
    
private 
Integer id;
    
private 
String name;
    
private 
Integer age;
    
private 
Group group;
     
    
@ManyToOne
    
@JoinColumn
(name=
"group_id"
)
    
public 
Group getGroup() {
       
returngroup;
    
}
    
publicvoid setGroup(Group group) {
       
this
.group = group;
    
}
    
@Id
    
@GeneratedValue
    
public 
Integer getId() {
       
returnid;
    
}
    
publicvoid setId(Integer id) {
       
this
.id = id;
    
}
    
@Column
(name=
"p_name"
)
    
public 
String getName() {
       
returnname;
    
}
    
publicvoid setName(String name) {
       
this
.name = name;
    
}
    
@Column
(name=
"p_age"
)
    
public 
Integer getAge() {
       
returnage;
    
}
    
publicvoid setAge(Integer age) {
       
this
.age = age;
    
}
}


现在我们查询Person,看它发送的SQL语句


1
2
3
4
5
6
7
8
9
10
//我们发现,在查询Person的时候,发送的SQL语句
    
@Test
    
publicvoid findTest1(){
       
Session s=sessionFactory.getCurrentSession();
       
s.beginTransaction();
       
List<Person> persons=s.createQuery(
"from Person"
).list();
       
for
(Person person:persons){
           
System.out.println(person.getName()+
"----"
+person.getId());    }
       
s.getTransaction().commit();
    
}

查看SQL语句

(除了第一条是查询Person的语句,后面10条都是查询Group的语句)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
11:02:13,036 DEBUGSQL:111 - 
    
select
        
person0_.id 
as 
id1_,
        
person0_.p_age 
as 
p2_1_,
        
person0_.group_id 
as 
group4_1_,
        
person0_.p_name 
as 
p3_1_ 
    
from
        
p_person person0_
11:02:13,078 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,096 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,100 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,102 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,103 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,105 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,107 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,110 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,113 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
11:02:13,115 DEBUGSQL:111 - 
    
select
        
group0_.id 
as 
id0_0_,
        
group0_.g_name 
as 
g2_0_0_ 
    
from
        
t_group group0_ 
    
where
        
group0_.id=?
张三0
----1
张三1
----2
张三2
----3
张三3
----4
张三4
----5
张三5
----6
张三6
----7
张三7
----8
张三8
----9
张三9
----10

这里我只需要查询的是Person的信息,只需要一条SQL语句就够了,但是现在多发了10条查询Group的语句,所以在这里我称之1+N问题。OK?

怎么解决Hibernate的1+N问题?


前面提到FetchType对,没错。

第一种方法,就是将many-to-one的FetchType值设为 LAZY  意思是告诉Hibernate  我需要的时候,你再发SQL请求。

1
@ManyToOne
(fetch=FetchType.LAZY)

第二种方法,采用createCriteria这种事默认采用表连接的形式,也是可以解决。

第三种方法,left join fetch 做个表连接 也是可以的。

第四种方法,在一的一方加上@BatchSize  指定一个值(指定的值代表就是in()里面的值,发的SQL)也是可以的,但是不建议这么做,毕竟@BatchSize真正解决不了1+N这种问题,最多只能是少发几条SQL而已,大家有兴趣可以慢慢研究!

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
    
publicvoid findTest1(){
       
Session s=sessionFactory.getCurrentSession();
       
s.beginTransaction();
//     List<Person>persons=(List<Person>)s.createCriteria(Person.class).list();//解决方法1
       
List<Person> persons=s.createCriteria(Person.
class
).list();
    
//  List<Person>persons=s.createQuery("from Person p left join fetch p.groupg").list();//解决方法3
       
for
(Person person:persons){
           
System.out.println(person.getName()+
"----"
+person.getId());
//         System.out.println(person.getGroup().getName());
       
}
       
s.getTransaction().commit();
    
}


ok,1+N问题,是Hibernate面试最常考的题,同时也是性能优化一种常见的手段。本文详细讲解了1+N问题的原理和解决办法!有问题举手!

本文转自 小夜的传说 51CTO博客,原文链接:http://blog.51cto.com/1936625305/1570578,如需转载请自行联系原作者
你可能感兴趣的文章
Cloud Service Process Pack
查看>>
组合问题的求解
查看>>
zabbix专题:第十二章 zabbix proxy分布式监控配置
查看>>
为什么总觉得自己不适合搞IT?
查看>>
vmware克隆server2008R2造成SID冲突
查看>>
python调用zabbix api接口实时展示数据
查看>>
VMware下Windows2003R2虚拟机磁盘扩容方法
查看>>
利用Linux的文件命名规范在Windows中建立“高权限”文件
查看>>
【Go语言】【14】GO语言的接口类型
查看>>
配置CAS应用客户端
查看>>
摘抄--apache工作模式详解
查看>>
更改sybase下设备名
查看>>
不少朋友在安装IDES 4.71的过程中都遇到了下面的出错提示:
查看>>
企业的人性和狼性
查看>>
mySQL教程 第10章 事务和锁
查看>>
Hello, Kafka World
查看>>
Exchange 2010和Exchange 2016共存部署-10:配置多域名证书
查看>>
F5 配置手册 -F5 BIG-IP 10.1-2-配置-基本参数
查看>>
《统一沟通-微软-实战》-6-部署-2-中介服务器-1-定义中介服务器
查看>>
虚拟化,可实现国产化替代
查看>>