hibernate系列(三)多对多的关联关系

[size=medium]以Teacher和Student为例,他们之间是多对多的关系。
手动创建的数据库的三张表为,teacher、student、teacher_student。分别如下:[/size]

CREATE TABLE `teacher` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


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;

[size=medium]Teacher类如下:[/size]

public class Teacher {

private Long id;
private String name;
private Set<Student> students;
//省略get、set方法
}

[size=medium]Teacher类对应的映射文件Teacher.hbm.xml:[/size]

<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>

[size=medium]其中set标签的name指的是Teacher类的students属性,table指的是该属性要关联到哪张表。内部的key标签的column指的是该Teacher类所属的表的主键id作为teacher_student的外键teacher_id值。<many-tomany>标签指的是Student类所属的student表的主键作为teacher_student表的student_id值。
Stusent类如下:[/size]

public class Student {

private Long id;
private String name;
private Set<Teacher> teachers;
//省略get、set方法
}

[size=medium]Student类对应的映射文件Student.hbm.xml如下:[/size]

<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>

[size=medium]其中的set映射关系同上。
然后就来看下增添方法:[/size]

@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();
}

[size=medium]三个save,三个insert语句,同时,由于t.setStudents(students),所以teacher会去维护teacher_student关系表,所以又会增添两条insert语句,sql如下:[/size]

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 (?, ?)

[size=medium]如果增添改为如下状态:[/size]

@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();
}

[size=medium]此时,不仅建立起了t.setStudents(students)关系,同时s1.setTeachers(teachers);s2.setTeachers(teachers);也建立了关系,所以最终事务提交的时候,teacher、student都会去维护teacher_student表,造成了4个insert语句,造成了重复。如下:[/size]

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 (?, ?)

[size=medium]为了避免这一现象,就需要某一方放弃维护teacher_student表的权限。如student放弃维护这一关系,使用inverse="true",如下:[/size]

<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>

[size=medium]此时再按照上述方法增添就只有teacher去维护teacher_student表了,不会造成重复。

再看下多对多关联关系的查询:[/size]

@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();
}

[size=medium]这里同样存在这查询策略问题,此时Teacher类的映射文件对应的set标签的lazy属性有三个值,一个true、false、extra。默认为true,即实行延迟加载的策略,用到Student时才回去加载它,如下sql:[/size]

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

[size=medium]若设置为false,则表示在加载Teacher时立即去加载相关的Student,如下sql:[/size]

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

[size=medium]若设置为extra,则会更加智能化一些,即上述t.getStudents().size()并没有去访问Student的实际内容,仅仅是想获取数量,所以sql语句如下:[/size]

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

[size=medium]再看下更新Teacher:[/size]

@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();
}

[size=medium]更新的时候,会先删除之前的teacher_student表中的相关记录,然后再新增新的记录,如下sql:[/size]

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 (?, ?)


若想转载请注明出处: [url]http://lgbolgger.iteye.com/blog/2125014[/url]
作者:iteye的乒乓狂魔
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值