项目做完了,测试组在在测试中,趁此机会回头看下hibernate,不看不知道一看吓一跳,好多地方全忘了。就重新复习了下。
多对多映射的总结:
下面是三张表,一个班级可以有多个老师,相应的一个老师也可以教多个班级。班级表和教师表通过中间表关联,就构成了多对多的关系。
班级表:
CREATE TABLE `tbl_classes` (`id` INT(11) NOT NULL,`class_name` VARCHAR(20) NULL DEFAULT NULL,PRIMARY KEY (`id`))
教师表:
CREATE TABLE `tbl_teacher` (`id` VARCHAR(255) NOT NULL,`teacher_name` VARCHAR(20) NULL DEFAULT NULL,PRIMARY KEY (`id`))
班级教师关联表:
CREATE TABLE `tbl_teacher_classes` (
`teacher_id` INT(11) NULL DEFAULT NULL,
`class_id` INT(11) NULL DEFAULT NULL,
INDEX `FK_tbl_teacher_classes_tbl_teacher` (`teacher_id`),
INDEX `FK_tbl_teacher_classes_tbl_classes` (`class_id`),
CONSTRAINT `FK_tbl_teacher_classes_tbl_teacher` FOREIGN KEY (`teacher_id`) REFERENCES `tbl_teacher` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `FK_tbl_teacher_classes_tbl_classes` FOREIGN KEY (`class_id`) REFERENCES `tbl_classes` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
package wb.wk.review.bean;
import java.util.Set;
public class Classes {
private int id;
private String className;
private Set<Classes> classes;
//Getter and setter
}
public class Teacher {
private int id;
private String teacherName;
private Set<Classes> classes;
//Getter and setter
}
班级Xml映射:
<hibernate-mapping>
<class name="wb.wk.review.bean.Classes" table="tbl_classes">
<id name="id" column="id" type="java.lang.Integer"></id>
<property name="className" type="java.lang.String" column="class_name"/>
<set name="teacher" table="tbl_teacher_classes" cascade="all" inverse="true" >
<key column="class_id"></key>
<many-to-many class="wb.wk.review.bean.Teacher" column="teacher_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
教师Xml映射:
<hibernate-mapping>
<class name="wb.wk.review.bean.Teacher" table="tbl_teacher">
<id name="id" column="id" type="java.lang.Integer"></id>
<property name="teacherName" type="java.lang.String" column="teacher_name"/>
<set name="classes" table="tbl_teacher_classes" cascade="all">
<key column="teacher_id"></key>
<many-to-many class="wb.wk.review.bean.Classes" column="class_id"></many-to-many>
</set>
</class>
</hibernate-mapping>
测试的main方法:
public static void main(String[] args) {
Session session=null;
Transaction tran=null;
try {
session=HibernateSessionFactory.getSession();
tran=session.beginTransaction();
Set<Teacher> setTeacher=new HashSet<Teacher>();
Teacher t1=new Teacher();
t1.setTeacherName("马老师");
setTeacher.add(t1);
//Teacher t2=new Teacher();
//t2.setTeacherName("张老师");
//setTeacher.add(t2);
Set<Classes> setClass=new HashSet<Classes>();
Classes c1=new Classes();
c1.setClassName("小一班");
setClass.add(c1);
//Classes c2=new Classes();
//c2.setClassName("大一班");
//setClass.add(c2);
标记1:
t1.setClasses(setClass);
session.save(t1);//此时需要在class xml中配inverse="true"
标记2:
c1.setTeacher(setTeacher);
session.save(c1);//此时需要在teacher xml中配inverse="true"
//session.merge(t2);
//session.merge(c1);
//session.merge(c2);
tran.commit();
} catch (HibernateException e) {
tran.rollback();
e.printStackTrace();
}finally{
session.flush();
session.close();
}
}
运行main,出现异常:
org.hibernate.MappingException: An association from the table tbl_teacher_classes refers to an unmapped class: Classes
原来是<many-to-many class="wb.wk.review.bean.Teacher" column="teacher_id"></many-to-many>少了实体类的包路径,把xml红色部分加上就不报此异常的了。
又运行后,又出现个异常。下面异常信息:
Hibernate:
select classes_.id, classes_.class_name as class2_0_
from tbl_classes classes_ where classes_.id=?
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [wb.wk.review.bean.Classes#0]
这是应为我new了两个Classes 和两个Teacher.这四个对象目前全是游离状态,当我调用save()方法时就转成持久化状态了,而一个session中是不能有一个对象的双重copy的,解决办法调用merge()方法,会出现一条更新语句,或者注释掉。哈哈
最后运行,出现下面的sql语句。
Hibernate:
Select classes_.id, classes_.class_name as class2_0_
From tbl_classes classes_ where classes_.id=?
Hibernate:
insert into tbl_teacher (teacher_name, id) Values (?, ?)
Hibernate:
insert into tbl_classes(class_name, id) values (?, ?)
Hibernate:
insert into tbl_teacher_classes (teacher_id, class_id) values (?, ?)
出现上述sql语句,但是插入的数据不对,中间表外键全是0。原因:中间表需要设置主键。Teacher.xml和class.xml需要配置<generator class="identity"></generator>
虽然有插入语句,但是中间表的数据却不准确。检查发现少了inverse属性。
关于标记1:
为teacher添加inverse="true"这是因为反转后,数据的维护交给了class维护,所以修改main方法如下:
c1.setTeacher(setTeacher);
session.save(c1);//save的是班级对象
运行后出现:
Hibernate:
insert into tbl_teacher (teacher_name, id) Values (?, ?)
Hibernate:
insert into tbl_classes(class_name, id) values (?, ?)
Hibernate:
insert into tbl_teacher_classes (teacher_id, class_id) values (?, ?)
运行插入数据正确。
关于标记2:
把inverse="true"添加到class xml中,
t1.setClasses(setClass);
session.save(t1);//save的是教师对象
再次运行出现:
Hibernate:
insert into tbl_teacher (teacher_name, id) Values (?, ?)
Hibernate:
insert into tbl_classes(class_name, id) values (?, ?)
Hibernate:
insert into tbl_teacher_classes (teacher_id, class_id) values (?, ?)
数据插入也正确
总结:标记1和标记2说明;多对多中,数据的维护方在哪一方都行,如果A方设置inverse属性,B方就是数据维护方,B.set(A),保存B就行了。
最后:其实多对多映射比较复杂,还有中方法就是转换成多对一映射,需要写一个TeacherClass中间类,做个xml映射,<many-to-one/>这个就简单多了。