- 应用场景
- 演示
[一]、应用场景
在用注解的方式(@ManyToMany @JoinTable)实现多对多映射时,并没有具体的多对多关系表的实体类,那么我们如何利用HQL实现关联查询呢?比如:学生和课程表之间的多对多关系。
[二]、演示
1.实体类
IdEntity.java
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
|
package
com
.
micmiu
.
hibernate
.
anno
.
entity
;
import
javax
.
persistence
.
Column
;
import
javax
.
persistence
.
GeneratedValue
;
import
javax
.
persistence
.
Id
;
import
javax
.
persistence
.
MappedSuperclass
;
/**
* 统一定义id的entity基类.
*
* 基类统一定义id的属性名称、数据类型、列名映射及生成策略. <br>
* 子类可重载getId()函数重定义id的列名映射和生成策略.
*
* @author <a href="http://www.micmiu.com">Michael Sun</a>
*/
@
MappedSuperclass
public
abstract
class
IdEntity
{
protected
Long
id
;
@
Id
@
Column
(
name
=
"ID"
)
@
GeneratedValue
// @GeneratedValue(strategy = GenerationType.SEQUENCE)
// @GeneratedValue(generator = "system-uuid")
// @GenericGenerator(name = "system-uuid", strategy = "uuid")
public
Long
getId
(
)
{
return
id
;
}
public
void
setId
(
Long
id
)
{
this
.
id
=
id
;
}
}
|
学生实体类:Student.java
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
|
package
com
.
micmiu
.
hibernate
.
anno
.
entity
;
import
java
.
util
.
Collection
;
import
java
.
util
.
Date
;
import
javax
.
persistence
.
Column
;
import
javax
.
persistence
.
Entity
;
import
javax
.
persistence
.
JoinColumn
;
import
javax
.
persistence
.
JoinTable
;
import
javax
.
persistence
.
ManyToMany
;
import
javax
.
persistence
.
OrderBy
;
import
javax
.
persistence
.
Table
;
import
org
.
hibernate
.
annotations
.
Fetch
;
import
org
.
hibernate
.
annotations
.
FetchMode
;
/**
*
* Description:
*
* @author <a href="http://www.micmiu.com">Michael</a>
* @see <a href="http://www.micmiu.com">http://www.micmiu.com</a>
* @time Create on 2013-6-6 下午2:40:08
* @version 1.0
*/
@
Entity
@
Table
(
name
=
"DEMO_T_STUDENT"
)
public
class
Student
extends
IdEntity
{
@
Column
(
name
=
"NAME"
)
private
String
name
;
@
Column
(
name
=
"BIRTHDAY"
)
private
Date
birthday
;
private
Collection
&
lt
;
Course
&
gt
;
courses
;
public
String
getName
(
)
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
public
Date
getBirthday
(
)
{
return
birthday
;
}
public
void
setBirthday
(
Date
birthday
)
{
this
.
birthday
=
birthday
;
}
@
ManyToMany
@
JoinTable
(
name
=
"DEMO_R_S2C"
,
joinColumns
=
{
@
JoinColumn
(
name
=
"SID"
)
}
,
inverseJoinColumns
=
{
@
JoinColumn
(
name
=
"CID"
)
}
)
@
Fetch
(
FetchMode
.
SUBSELECT
)
@
OrderBy
(
"id"
)
public
Collection
&
lt
;
Course
&
gt
;
getCourses
(
)
{
return
courses
;
}
public
void
setCourses
(
Collection
&
lt
;
Course
&
gt
;
courses
)
{
this
.
courses
=
courses
;
}
@
Override
public
String
toString
(
)
{
return
"Student [name="
+
name
+
", birthday="
+
birthday
+
"]"
;
}
}
|
课程实体类:Course.java
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
|
package
com
.
micmiu
.
hibernate
.
anno
.
entity
;
import
javax
.
persistence
.
Column
;
import
javax
.
persistence
.
Entity
;
import
javax
.
persistence
.
Table
;
/**
*
* Description:
*
* @author <a href="http://www.micmiu.com">Michael</a>
* @see <a href="http://www.micmiu.com">http://www.micmiu.com</a>
* @time Create on 2013-6-6 下午2:52:31
* @version 1.0
*/
@
Entity
@
Table
(
name
=
"DEMO_T_COURSE"
)
public
class
Course
extends
IdEntity
{
@
Column
(
name
=
"NAME"
)
private
String
name
;
public
String
getName
(
)
{
return
name
;
}
public
void
setName
(
String
name
)
{
this
.
name
=
name
;
}
@
Override
public
String
toString
(
)
{
return
"Course [name="
+
name
+
"]"
;
}
}
|
2.初始化数据:
利用dbunit初始化演示数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?
xml
version
=
'1.0'
encoding
=
"utf-8"
?>
<dataset>
<demo_t_student
id
=
"101"
name
=
"micmiu.com"
birthday
=
"2012-12-12"
/>
<demo_t_student
id
=
"103"
name
=
"ctosun.com"
birthday
=
"2013-01-04"
/>
<demo_t_course
id
=
"901"
name
=
"chinese"
/>
<demo_t_course
id
=
"902"
name
=
"english"
/>
<demo_t_course
id
=
"903"
name
=
"math"
/>
<demo_r_s2c
sid
=
"101"
cid
=
"901"
/>
<demo_r_s2c
sid
=
"101"
cid
=
"902"
/>
<demo_r_s2c
sid
=
"101"
cid
=
"903"
/>
<demo_r_s2c
sid
=
"102"
cid
=
"902"
/>
<demo_r_s2c
sid
=
"102"
cid
=
"903"
/>
<demo_r_s2c
sid
=
"103"
cid
=
"903"
/>
</dataset>
|
3.演示关联查询
在Student中配置了多对多关系,在Course没有配置多对多关系,下面将分别演示关联查询这两个实体的方法:
测试基础类 HibernateBaseTest.java
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
|
package
com
.
micmiu
.
hibernate
;
import
org
.
hibernate
.
SessionFactory
;
import
org
.
hibernate
.
cfg
.
Configuration
;
import
org
.
hibernate
.
service
.
ServiceRegistry
;
import
org
.
hibernate
.
service
.
ServiceRegistryBuilder
;
import
org
.
junit
.
AfterClass
;
import
org
.
junit
.
BeforeClass
;
import
org
.
junit
.
Test
;
/**
*
* Description:
*
* @author <a href="http://www.micmiu.com">Michael</a>
* @see <a href="http://www.micmiu.com">http://www.micmiu.com</a>
* @time Create on 2013-6-6 下午7:31:02
* @version 1.0
*/
public
abstract
class
HibernateBaseTest
{
protected
static
SessionFactory
sessionFactory
;
@
BeforeClass
public
static
void
beforeClass
(
)
{
Configuration
configuration
=
new
Configuration
(
)
.
configure
(
)
;
ServiceRegistry
serviceRegistry
=
new
ServiceRegistryBuilder
(
)
.
applySettings
(
configuration
.
getProperties
(
)
)
.
buildServiceRegistry
(
)
;
sessionFactory
=
configuration
.
buildSessionFactory
(
serviceRegistry
)
;
}
@
AfterClass
public
static
void
afterClass
(
)
{
sessionFactory
.
close
(
)
;
}
@
Test
public
abstract
void
testMethod
(
)
;
}
|
测试HQL查询:ManyToManyTest.java
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
|
package
com
.
micmiu
.
hibernate
;
import
java
.
util
.
List
;
import
org
.
hibernate
.
Query
;
import
org
.
hibernate
.
Session
;
import
org
.
junit
.
Assert
;
import
org
.
junit
.
Test
;
import
com
.
micmiu
.
hibernate
.
anno
.
entity
.
Course
;
import
com
.
micmiu
.
hibernate
.
anno
.
entity
.
Student
;
/**
*
* Description:测试 注解配置多对多关系的表之间的联合查询
*
* @author <a href="http://www.micmiu.com">Michael</a>
* @see <a href="http://www.micmiu.com">http://www.micmiu.com</a>
* @time Create on 2013-6-7 上午11:38:49
* @version 1.0
*/
public
class
ManyToManyTest
extends
HibernateBaseTest
{
@
Override
@
Test
public
void
testMethod
(
)
{
// 测试查询有配置多对多关系的对象
test1
(
)
;
// 测试查询无配置多对多关系的对象
test2
(
)
;
}
/**
* 测试查询有配置多对多关系的对象
*/
@
SuppressWarnings
(
"unchecked"
)
public
void
test1
(
)
{
Session
session
=
sessionFactory
.
openSession
(
)
;
session
.
beginTransaction
(
)
;
String
hql
=
"select s from Student s join s.courses c where s.name like '%micmiu.com%' and c.name ='math'"
;
String
hql2
=
"select s from Student s,Course c where c.id in elements (s.courses) and s.name like '%micmiu.com%' and c.name ='math'"
;
Query
query
=
session
.
createQuery
(
hql
)
;
List
&
lt
;
Student
&
gt
;
list
=
query
.
list
(
)
;
System
.
out
.
println
(
"----------- size:"
+
list
.
size
(
)
)
;
Assert
.
assertEquals
(
1
,
list
.
size
(
)
)
;
for
(
Student
s
:
list
)
{
Assert
.
assertEquals
(
"micmiu.com"
,
s
.
getName
(
)
)
;
System
.
out
.
println
(
s
)
;
}
session
.
getTransaction
(
)
.
commit
(
)
;
session
.
close
(
)
;
}
/**
* 测试查询无配置多对多关系的对象
*/
@
SuppressWarnings
(
"unchecked"
)
public
void
test2
(
)
{
Session
session
=
sessionFactory
.
openSession
(
)
;
session
.
beginTransaction
(
)
;
String
hql
=
"select distinct c from Student s,Course c where c.id in elements (s.courses) and s.name like '%micmiu.com%' and c.name ='math'"
;
Query
query
=
session
.
createQuery
(
hql
)
;
List
&
lt
;
Course
&
gt
;
list
=
query
.
list
(
)
;
System
.
out
.
println
(
"----------- size:"
+
list
.
size
(
)
)
;
Assert
.
assertEquals
(
1
,
list
.
size
(
)
)
;
for
(
Course
c
:
list
)
{
Assert
.
assertEquals
(
"math"
,
c
.
getName
(
)
)
;
System
.
out
.
println
(
c
)
;
}
session
.
getTransaction
(
)
.
commit
(
)
;
session
.
close
(
)
;
}
}
|
在我们查询Parent对象的时候,默认只有Parent的内容,并不包含childs的信息,如果在Parent.hbm.xml里设置lazy="false"的话才同时取出关联的所有childs内容.
问题是我既想要hibernate默认的性能又想要临时的灵活性该怎么办? 这就是fetch的功能。我们可以把fetch与lazy="true"的关系类比为事务当中的编程式事务与声明式事务,不太准确,但是大概是这个意思。
总值,fetch就是在代码这一层给你一个主动抓取得机会.
- Parent parent = (Parent)hibernateTemplate.execute(new HibernateCallback() {
- public Object doInHibernate(Session session) throws HibernateException, SQLException {
- Query q = session.createQuery(
- "from Parent as parent "+
- " left outer join fetch parent.childs " +
- " where parent.id = :id"
- );
- q.setParameter("id",new Long(15));
- return (Parent)q.uniqueResult();
- }
- });
- Assert.assertTrue(parent.getChilds().size() > 0);
你可以在lazy="true"的情况下把fetch去掉,就会报异常. 当然,如果lazy="false"就不需要fetch了。
fetch设置为lazy,查询Parent对象的时候,默认只有Parent的内容,并不包含child的信息,这样可以避免n+1问题。
但又想查询parent时把children得到,就可以用fetch。
fetch主动将child得到并set到children列表中。
HQL语句:from Parent p left join fetch p.children