以Teacher和Student为例,他们之间是多对多的关系。
手动创建的数据库的三张表为,teacher、student、teacher_student。分别如下:
Teacher类如下:
Teacher类对应的映射文件Teacher.hbm.xml:
其中set标签的name指的是Teacher类的students属性,table指的是该属性要关联到哪张表。内部的key标签的column指的是该Teacher类所属的表的主键id作为teacher_student的外键teacher_id值。<many-tomany>标签指的是Student类所属的student表的主键作为teacher_student表的student_id值。
Stusent类如下:
Student类对应的映射文件Student.hbm.xml如下:
其中的set映射关系同上。
然后就来看下增添方法:
三个save,三个insert语句,同时,由于t.setStudents(students),所以teacher会去维护teacher_student关系表,所以又会增添两条insert语句,sql如下:
如果增添改为如下状态:
此时,不仅建立起了t.setStudents(students)关系,同时s1.setTeachers(teachers);s2.setTeachers(teachers);也建立了关系,所以最终事务提交的时候,teacher、student都会去维护teacher_student表,造成了4个insert语句,造成了重复。如下:
为了避免这一现象,就需要某一方放弃维护teacher_student表的权限。如student放弃维护这一关系,使用inverse="true",如下:
此时再按照上述方法增添就只有teacher去维护teacher_student表了,不会造成重复。
再看下多对多关联关系的查询:
这里同样存在这查询策略问题,此时Teacher类的映射文件对应的set标签的lazy属性有三个值,一个true、false、extra。默认为true,即实行延迟加载的策略,用到Student时才回去加载它,如下sql:
若设置为false,则表示在加载Teacher时立即去加载相关的Student,如下sql:
若设置为extra,则会更加智能化一些,即上述t.getStudents().size()并没有去访问Student的实际内容,仅仅是想获取数量,所以sql语句如下:
再看下更新Teacher:
更新的时候,会先删除之前的teacher_student表中的相关记录,然后再新增新的记录,如下sql:
手动创建的数据库的三张表为,teacher、student、teacher_student。分别如下:
1
2
3
4
5
|
CREATE
TABLE
`teacher` (
`id`
int
(11)
NOT
NULL
AUTO_INCREMENT,
`
name
`
varchar
(45)
DEFAULT
''
,
PRIMARY
KEY
(`id`)
) ENGINE=InnoDB
DEFAULT
CHARSET=utf8;
|
1
2
3
4
5
|
CREATE
TABLE
`student` (
`id`
int
(11)
NOT
NULL
AUTO_INCREMENT,
`
name
`
varchar
(45)
DEFAULT
''
,
PRIMARY
KEY
(`id`)
) ENGINE=InnoDB
DEFAULT
CHARSET=utf8;
|
1
2
3
4
5
6
|
CREATE
TABLE
`teacher_student` (
`id`
int
(11)
NOT
NULL
AUTO_INCREMENT,
`teacher_id`
int
(11)
DEFAULT
NULL
,
`student_id`
int
(11)
DEFAULT
NULL
,
PRIMARY
KEY
(`id`)
) ENGINE=InnoDB
DEFAULT
CHARSET=utf8;
|
Teacher类如下:
1
2
3
4
5
6
7
|
public
class
Teacher {
private
Long id;
private
String name;
private
Set<Student> students;
//省略get、set方法
}
|
Teacher类对应的映射文件Teacher.hbm.xml:
1
2
3
4
5
6
7
8
9
10
11
12
|
<
hibernate-mapping
>
<
class
name
=
"com.ligang.domain.Teacher"
table
=
"teacher"
>
<
id
name
=
"id"
column
=
"id"
type
=
"long"
>
<
generator
class
=
"identity"
/>
</
id
>
<
property
name
=
"name"
column
=
"name"
type
=
"string"
/>
<
set
name
=
"students"
table
=
"teacher_student"
>
<
key
column
=
"teacher_id"
></
key
>
<
many-to-many
class
=
"com.ligang.domain.Student"
column
=
"student_id"
></
many-to-many
>
</
set
>
</
class
>
</
hibernate-mapping
>
|
其中set标签的name指的是Teacher类的students属性,table指的是该属性要关联到哪张表。内部的key标签的column指的是该Teacher类所属的表的主键id作为teacher_student的外键teacher_id值。<many-tomany>标签指的是Student类所属的student表的主键作为teacher_student表的student_id值。
Stusent类如下:
1
2
3
4
5
6
7
|
public
class
Student {
private
Long id;
private
String name;
private
Set<Teacher> teachers;
//省略get、set方法
}
|
Student类对应的映射文件Student.hbm.xml如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
<
hibernate-mapping
>
<
class
name
=
"com.ligang.domain.Student"
table
=
"student"
>
<
id
name
=
"id"
column
=
"id"
type
=
"long"
>
<
generator
class
=
"identity"
/>
</
id
>
<
property
name
=
"name"
column
=
"name"
type
=
"string"
/>
<
set
name
=
"teachers"
table
=
"teacher_student"
inverse
=
"true"
>
<
key
column
=
"student_id"
></
key
>
<
many-to-many
class
=
"com.ligang.domain.Teacher"
column
=
"teacher_id"
></
many-to-many
>
</
set
>
</
class
>
</
hibernate-mapping
>
|
其中的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
|
@Test
public
void
testAddTeacher1(){
Session session=hibernateDao.getSession();
Transaction tx=session.beginTransaction();
Teacher t=
new
Teacher();
t.setName(
"teacher4"
);
Student s1=
new
Student();
s1.setName(
"assa"
);
Student s2=
new
Student();
s2.setName(
"sdfvdv"
);
Set<Student> students=
new
HashSet<Student>();
students.add(s1);
students.add(s2);
t.setStudents(students);
session.save(s1);
session.save(s2);
session.save(t);
tx.commit();
session.close();
}
|
三个save,三个insert语句,同时,由于t.setStudents(students),所以teacher会去维护teacher_student关系表,所以又会增添两条insert语句,sql如下:
1
2
3
4
5
|
Hibernate:
insert
into
hibernate.student (
name
)
values
(?)
Hibernate:
insert
into
hibernate.student (
name
)
values
(?)
Hibernate:
insert
into
hibernate.teacher (
name
)
values
(?)
Hibernate:
insert
into
hibernate.teacher_student (teacher_id, student_id)
values
(?, ?)
Hibernate:
insert
into
hibernate.teacher_student (teacher_id, student_id)
values
(?, ?)
|
如果增添改为如下状态:
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
testAddTeacher(){
Session session=hibernateDao.getSession();
Transaction tx=session.beginTransaction();
Teacher t=
new
Teacher();
t.setName(
"teacher4"
);
Student s1=
new
Student();
s1.setName(
"assa"
);
Student s2=
new
Student();
s2.setName(
"sdfvdv"
);
Set<Student> students=
new
HashSet<Student>();
Set<Teacher> teachers=
new
HashSet<Teacher>();
students.add(s1);
students.add(s2);
teachers.add(t);
t.setStudents(students);
s1.setTeachers(teachers);
s2.setTeachers(teachers);
session.save(s1);
session.save(s2);
session.save(t);
tx.commit();
session.close();
}
|
此时,不仅建立起了t.setStudents(students)关系,同时s1.setTeachers(teachers);s2.setTeachers(teachers);也建立了关系,所以最终事务提交的时候,teacher、student都会去维护teacher_student表,造成了4个insert语句,造成了重复。如下:
1
2
3
4
5
6
7
|
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: insert into hibernate.teacher (name) values (?)
Hibernate: insert into hibernate.teacher_student (student_id, teacher_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (student_id, teacher_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)
|
为了避免这一现象,就需要某一方放弃维护teacher_student表的权限。如student放弃维护这一关系,使用inverse="true",如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
<
hibernate-mapping
>
<
class
name
=
"com.ligang.domain.Student"
table
=
"student"
>
<
id
name
=
"id"
column
=
"id"
type
=
"long"
>
<
generator
class
=
"identity"
/>
</
id
>
<
property
name
=
"name"
column
=
"name"
type
=
"string"
/>
<
set
name
=
"teachers"
table
=
"teacher_student"
inverse
=
"true"
>
<
key
column
=
"student_id"
></
key
>
<
many-to-many
class
=
"com.ligang.domain.Teacher"
column
=
"teacher_id"
></
many-to-many
>
</
set
>
</
class
>
</
hibernate-mapping
>
|
此时再按照上述方法增添就只有teacher去维护teacher_student表了,不会造成重复。
再看下多对多关联关系的查询:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Test
public
void
getTeacher(){
Session session=hibernateDao.getSession();
Transaction tx=session.beginTransaction();
Teacher t=(Teacher) session.get(Teacher.
class
,3L);
System.out.println(t.getName());
System.out.println(t.getStudents().size());
tx.commit();
session.close();
}
|
这里同样存在这查询策略问题,此时Teacher类的映射文件对应的set标签的lazy属性有三个值,一个true、false、extra。默认为true,即实行延迟加载的策略,用到Student时才回去加载它,如下sql:
1
2
3
4
|
Hibernate:
select
teacher0_.id
as
id1_3_0_, teacher0_.
name
as
name2_3_0_
from
hibernate.teacher teacher0_
where
teacher0_.id=?
teacher2
Hibernate:
select
students0_.teacher_id
as
teacher_1_3_0_, students0_.student_id
as
student_2_4_0_, student1_.id
as
id1_2_1_, student1_.
name
as
name2_2_1_
from
hibernate.teacher_student students0_
inner
join
hibernate.student student1_
on
students0_.student_id=student1_.id
where
students0_.teacher_id=?
2
|
若设置为false,则表示在加载Teacher时立即去加载相关的Student,如下sql:
1
2
3
4
|
Hibernate:
select
teacher0_.id
as
id1_3_0_, teacher0_.
name
as
name2_3_0_
from
hibernate.teacher teacher0_
where
teacher0_.id=?
Hibernate:
select
students0_.teacher_id
as
teacher_1_3_0_, students0_.student_id
as
student_2_4_0_, student1_.id
as
id1_2_1_, student1_.
name
as
name2_2_1_
from
hibernate.teacher_student students0_
inner
join
hibernate.student student1_
on
students0_.student_id=student1_.id
where
students0_.teacher_id=?
teacher2
2
|
若设置为extra,则会更加智能化一些,即上述t.getStudents().size()并没有去访问Student的实际内容,仅仅是想获取数量,所以sql语句如下:
1
2
3
4
|
Hibernate:
select
teacher0_.id
as
id1_3_0_, teacher0_.
name
as
name2_3_0_
from
hibernate.teacher teacher0_
where
teacher0_.id=?
teacher2
Hibernate:
select
count
(student_id)
from
hibernate.teacher_student
where
teacher_id =?
2
|
再看下更新Teacher:
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
|
@Test
public
void
updateTeacher(){
Session session=hibernateDao.getSession();
Transaction tx=session.beginTransaction();
Teacher t=(Teacher) session.get(Teacher.
class
,6L);
Student s1=
new
Student();
s1.setName(
"assa"
);
Student s2=
new
Student();
s2.setName(
"sdfvdv"
);
Set<Student> students=
new
HashSet<Student>();
students.add(s1);
students.add(s2);
t.setStudents(students);
session.save(s1);
session.save(s2);
tx.commit();
session.close();
}
|
更新的时候,会先删除之前的teacher_student表中的相关记录,然后再新增新的记录,如下sql:
1
2
3
4
5
6
|
Hibernate: select teacher0_.id as id1_3_0_, teacher0_.name as name2_3_0_ from hibernate.teacher teacher0_ where teacher0_.id=?
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: delete from hibernate.teacher_student where teacher_id=?
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)
|