https://github.com/mmh891113/myblog.git

Hibernate学习之检索策略

检索数据时的2个问题
不浪费内存:当hibernate从数据库中加载customer对象时,如果同时加载所有关联的order对象,
而程序实际上仅仅需要访问customer对象,那么这些关联的order对象就白白浪费许多内存。

更高的查询效率:发送尽可能少的sql语句。

类级别的检索策略

类级别的检索策略包括立即检索和延迟检索,默认是延迟检索。
立即检索:立即加载检索方法指定的对象。
延迟检索:延迟加载检索方法指定的对象,在使用具体的属性时,再进行加载。

类级别的检索策略可以通过class元素的lazy属性进行设置,设置为true或者false。
这2个策略何时使用呢?如果程序加载一个对象的目的是为了访问它的属性,可以采用立即检索。
如果程序加载一个持久化对象的目的仅仅为了获取他的引用,可以采取延迟加载。

注意:延迟加载有可能出现懒加载异常。

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package com.mamh.hibernate.demo.entities;

import java.util.HashSet;
import java.util.Set;

public class Customer {
private Integer customerId;
private String customerName;

private Set<Order> orders = new HashSet<Order>();

public Customer() {
}

public Customer(String name) {
this.customerName = name;
}


public Integer getCustomerId() {
return customerId;
}

public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}

public String getCustomerName() {
return customerName;
}

public void setCustomerName(String customerName) {
this.customerName = customerName;
}

public Set<Order> getOrders() {
return orders;
}

public void setOrders(Set<Order> orders) {
this.orders = orders;
}

@Override
public String toString() {
return "Customer{" +
"customerId=" + customerId +
", customerName='" + customerName + '\'' +
'}';
}
}

package com.mamh.hibernate.demo.entities;

public class Order {
private Integer orderId;
private String orderName;


private Customer customer;

public Integer getOrderId() {
return orderId;
}

public void setOrderId(Integer orderId) {
this.orderId = orderId;
}

public String getOrderName() {
return orderName;
}

public void setOrderName(String orderName) {
this.orderName = orderName;
}

public Customer getCustomer() {
return customer;
}

public void setCustomer(Customer customer) {
this.customer = customer;
}

@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", orderName='" + orderName + '\'' +
", customer=" + customer +
'}' + '\n';
}
}



<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mamh.hibernate.demo.entities">

<class name="Order" table="hb_order" schema="mage">
<id name="orderId">
<column name="hb_order_id" sql-type="int(11)"/>
<generator class="native"/>
</id>
<property name="orderName" type="string">
<column name="hb_order_name"/>
</property>

<!-- 映射多对一的关联关系 property不再试用这个多对一的关系了
name是多这一端关联的那个一那一端属性名称
class的类全名
column是多的一端这边数据表的一个列名,一般是外键
-->
<many-to-one name="customer" class="Customer" column="customer_id"/>


</class>
</hibernate-mapping>




测试类级别的检索策略

1
2
3
4
5
6
7
8
@Test
public void testClassLevelStrategy(){
//类级别的检索策略

Customer customer = (Customer) session.load(Customer.class, 1);
System.out.println(customer.getClass());

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
延迟加载lazy="true"
<hibernate-mapping package="com.mamh.hibernate.demo.entities">

<class name="Customer" table="hb_customer" schema="mage" lazy="true">
<id name="customerId">
<column name="hb_customer_id" sql-type="int(11)"/>
<generator class="native"/>
</id>
<property name="customerName" type="string">
<column name="hb_customer_name"/>
</property>

<set name="orders" table="hb_order" inverse="true" cascade="save-update" order-by="hb_order_id">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>
</class>
</hibernate-mapping>

输出:
class com.mamh.hibernate.demo.entities.Customer_$$_javassist_1
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
立即加载lazy="false"
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mamh.hibernate.demo.entities">

<class name="Customer" table="hb_customer" schema="mage" lazy="false">
<id name="customerId">
<column name="hb_customer_id" sql-type="int(11)"/>
<generator class="native"/>
</id>
<property name="customerName" type="string">
<column name="hb_customer_name"/>
</property>

<set name="orders" table="hb_order" inverse="true" cascade="save-update" order-by="hb_order_id">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>
</class>
</hibernate-mapping>

输出:
Hibernate:
select
customer0_.hb_customer_id as hb1_2_0_,
customer0_.hb_customer_name as hb2_2_0_
from
mage.hb_customer customer0_
where
customer0_.hb_customer_id=?
class com.mamh.hibernate.demo.entities.Customer
=destroy=

一对多和多对多的检索策略

1对多 或 多对多 的集合属性默认是使用懒加载检索策略的。
可以通过设置set元素的lazy属性来重新设置检索策略。默认是true,并不建议设置为false。
set中的lazy还可以取extra,增强型的延迟加载。取该值会尽可能的延迟集合的初始化。

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testOne2ManyLevelStrategy() {
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getCustomerName());

//1. 1对多 或 多对多 的集合属性默认是使用懒加载检索策略的。
//2. 可以通过设置set元素的lazy属性来重新设置检索策略。

//System.out.println(customer.getOrders());

}
1
2
3
4
5
6
7
8
9
10
11
12
13
延迟加载:
如果不访问任何order对象是不会取加载它的。
Hibernate:
select
customer0_.hb_customer_id as hb1_2_0_,
customer0_.hb_customer_name as hb2_2_0_
from
mage.hb_customer customer0_
where
customer0_.hb_customer_id=?
aa

Process finished with exit code 0
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
延迟加载,
如果 有这样的 调用 customer中的集合order属性的话就会加载oder对象。
System.out.println(customer.getOrders());
Hibernate:
select
customer0_.hb_customer_id as hb1_2_0_,
customer0_.hb_customer_name as hb2_2_0_
from
mage.hb_customer customer0_
where
customer0_.hb_customer_id=?
aa
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id=?
order by
orders0_.hb_order_id
[Order{orderId=1, orderName='aa', customer=Customer{customerId=1, customerName='aa'}}
, Order{orderId=2, orderName='bb', customer=Customer{customerId=1, customerName='aa'}}
, Order{orderId=3, orderName='cc', customer=Customer{customerId=1, customerName='aa'}}
]

Process finished with exit code 0
1
2
3
4
5
6
7
可以通过设置customer.hbm.xml对应的set元素的lazy属性来更改这个。
这里lazy="false"就表示立即加载了。

<set name="orders" table="hb_order" inverse="true" cascade="save-update" order-by="hb_order_id" lazy="false">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>
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
    @Test
public void testOne2ManyLevelStrategy() {
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getCustomerName());
}

这里lazy="false"就表示立即加载了。
Hibernate:
select
customer0_.hb_customer_id as hb1_2_0_,
customer0_.hb_customer_name as hb2_2_0_
from
mage.hb_customer customer0_
where
customer0_.hb_customer_id=?
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id=?
order by
orders0_.hb_order_id
aa

Process finished with exit code 0
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
这里lazy="extra"增强型延迟加载。     
@Test
public void testOne2ManyLevelStrategy() {
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getCustomerName());

//1. 1对多 或 多对多 的集合属性默认是使用懒加载检索策略的。
//2. 可以通过设置set元素的lazy属性来重新设置检索策略。


System.out.println(customer.getOrders().size());

}
<set name="orders" table="hb_order" inverse="true" cascade="save-update" order-by="hb_order_id" lazy="extra">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>


这里访问了order集合的size,但是并没去初始化整个order集合,而是采用了sql语句来获取了集合的个数。

Hibernate:
select
customer0_.hb_customer_id as hb1_2_0_,
customer0_.hb_customer_name as hb2_2_0_
from
mage.hb_customer customer0_
where
customer0_.hb_customer_id=?
aa
Hibernate:
select count(hb_order_id) from mage.hb_order where customer_id =?
3

直接手动调用初始化集合对象

1
Hibernate.initialize(customer.getOrders());

大部分取默认值,lazy= “true”

set元素的batch-size属性

这个可以设定一次初始化集合的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    @Test
public void testBatchSize(){
List<Customer> customers = session.createQuery("from Customer ").list();

System.out.println(customers.size());

}


Hibernate:
select
customer0_.hb_customer_id as hb1_2_,
customer0_.hb_customer_name as hb2_2_
from
mage.hb_customer customer0_
4
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
   @Test
public void testBatchSize() {
List<Customer> customers = session.createQuery("from Customer ").list();

System.out.println(customers.size());
for (Customer customer : customers) {
if (customer.getOrders() != null) {
System.out.println(customer.getOrders().size());
}
}

}

Hibernate:
select
customer0_.hb_customer_id as hb1_2_,
customer0_.hb_customer_name as hb2_2_
from
mage.hb_customer customer0_
4

Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id=?
order by
orders0_.hb_order_id
3
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id=?
order by
orders0_.hb_order_id
3
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id=?
order by
orders0_.hb_order_id
3

Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id=?
order by
orders0_.hb_order_id
0

Process finished with exit code 0
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
这里batch-size设置为4, batch-size="4",这样能检索select语句的个数,之前是4条,现在只有一条了。

<set name="orders" table="hb_order" inverse="true" cascade="save-update" order-by="hb_order_id" lazy="true" batch-size="4">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>

Hibernate:
select
customer0_.hb_customer_id as hb1_2_,
customer0_.hb_customer_name as hb2_2_
from
mage.hb_customer customer0_
4
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in (
?, ?, ?, ?
)
order by
orders0_.hb_order_id
3
3
3
0

Process finished with exit code 0

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
这里batch-size设置为2,batch-size="2",这样会有2条select语句。这里为什么是2了?应为customer
表数据是4条,4除以batch-size就是等于2的。

<set name="orders" table="hb_order" inverse="true" cascade="save-update" order-by="hb_order_id" lazy="true" batch-size="2">
<key column="customer_id"/>
<one-to-many class="Order"/>
</set>


Hibernate:
select
customer0_.hb_customer_id as hb1_2_,
customer0_.hb_customer_name as hb2_2_
from
mage.hb_customer customer0_
4
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in (
?, ?
)
order by
orders0_.hb_order_id
3
3
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in ( ?, ? )
order by
orders0_.hb_order_id
3
0

Process finished with exit code 0

set元素的fetch属性,
当fetch取值为select或subselect,决定初始化order集合的查询语句形式,
若取值为join则决定orders集合被初始化的时机。

若把fetch设置为join,lazy属性取值将被忽略。

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testFetch() {
List<Customer> customers = session.createQuery("from Customer ").list();

System.out.println(customers.size());
for (Customer customer : customers) {
if (customer.getOrders() != null) {
System.out.println(customer.getOrders().size());
}
}

}

首先设置fetch是select,默认是select值。

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
Hibernate: 
select
customer0_.hb_customer_id as hb1_2_,
customer0_.hb_customer_name as hb2_2_
from
mage.hb_customer customer0_
4
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in ( ?, ? )
order by
orders0_.hb_order_id
3
3
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in ( ?, ? )
order by
orders0_.hb_order_id
3
0

Process finished with exit code 0

这里设置fetch是subselect。通过子查询的方式来初始化所有set集合。
次数lazy有效,但是batch-szie失效。

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

Hibernate:
select
customer0_.hb_customer_id as hb1_2_,
customer0_.hb_customer_name as hb2_2_
from
mage.hb_customer customer0_
4
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in (
select customer0_.hb_customer_id
from mage.hb_customer customer0_
)
order by
orders0_.hb_order_id
3
3
3
0

这里fetch取值join。
这里采用的是HQL查询。HQL查询忽略fetch=join的取值。

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
Hibernate: 
select
customer0_.hb_customer_id as hb1_2_,
customer0_.hb_customer_name as hb2_2_
from
mage.hb_customer customer0_
4
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in ( ?, ? )
order by
orders0_.hb_order_id
3
3
Hibernate:
select
orders0_.customer_id as customer3_2_1_,
orders0_.hb_order_id as hb1_1_,
orders0_.hb_order_id as hb1_3_0_,
orders0_.hb_order_name as hb2_3_0_,
orders0_.customer_id as customer3_3_0_
from
mage.hb_order orders0_
where
orders0_.customer_id in ( ?, ? )
order by
orders0_.hb_order_id
3
0

Process finished with exit code 0

这里值查询一个customer对象,这时值查询customer对象,这个时候order也初始化了,
这个时候lazy属性是没用的。
使用左外链接的方式检索。
HQL查询忽略fetch=join的取值。

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
   @Test
public void testFetch2() {
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer.getOrders().size());
}

Hibernate:
select
customer0_.hb_customer_id as hb1_2_1_,
customer0_.hb_customer_name as hb2_2_1_,
orders1_.customer_id as customer3_2_3_,
orders1_.hb_order_id as hb1_3_,
orders1_.hb_order_id as hb1_3_0_,
orders1_.hb_order_name as hb2_3_0_,
orders1_.customer_id as customer3_3_0_
from
mage.hb_customer customer0_
left outer join
mage.hb_order orders1_
on customer0_.hb_customer_id=orders1_.customer_id
where
customer0_.hb_customer_id=?
order by
orders1_.hb_order_id
3


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
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mamh.hibernate.demo.entities">

<class name="Order" table="hb_order" schema="mage">
<id name="orderId">
<column name="hb_order_id" sql-type="int(11)"/>
<generator class="native"/>
</id>
<property name="orderName" type="string">
<column name="hb_order_name"/>
</property>

<!-- 映射多对一的关联关系 property不再试用这个多对一的关系了
name是多这一端关联的那个一那一端属性名称
class的类全名
column是多的一端这边数据表的一个列名,一般是外键
-->
<many-to-one name="customer" class="Customer" column="customer_id"/>


</class>
</hibernate-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  @Test
public void testManyToOneGet() {
Order order = (Order) session.get(Order.class, 1);
System.out.println(order.getOrderName() + ": " + order.getOrderId());

//Customer customer = order.getCustomer();
//System.out.println(customer);
}



Hibernate:
select
order0_.hb_order_id as hb1_3_0_,
order0_.hb_order_name as hb2_3_0_,
order0_.customer_id as customer3_3_0_
from
mage.hb_order order0_
where
order0_.hb_order_id=?
aa: 1

lazy属性设置为true或者false,表示延迟加载,还是立即加载。当设置了 lazy=”false”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        <many-to-one name="customer" class="Customer" column="customer_id"
lazy="false"
/>

Hibernate:
select
order0_.hb_order_id as hb1_3_0_,
order0_.hb_order_name as hb2_3_0_,
order0_.customer_id as customer3_3_0_
from
mage.hb_order order0_
where
order0_.hb_order_id=?
Hibernate:
select
customer0_.hb_customer_id as hb1_2_0_,
customer0_.hb_customer_name as hb2_2_0_
from
mage.hb_customer customer0_
where
customer0_.hb_customer_id=?
aa: 1

fetch属性取值为join,表示采用迫切左外链接的方式初始化n关联的1的一端的属性。fetch=”join“

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hibernate: 
select
order0_.hb_order_id as hb1_3_1_,
order0_.hb_order_name as hb2_3_1_,
order0_.customer_id as customer3_3_1_,
customer1_.hb_customer_id as hb1_2_0_,
customer1_.hb_customer_name as hb2_2_0_
from
mage.hb_order order0_
left outer join
mage.hb_customer customer1_
on order0_.customer_id=customer1_.hb_customer_id
where
order0_.hb_order_id=?
aa: 1

fetch属性取值为join,表示采用迫切左外链接的方式初始化n关联的1的一端的属性。fetch=”join“