映射:
单向多对一就是在多的里面设一个一的对象;
单向一对多就是在一的里面设一个多的集合(注意此时多的里面要有一个与外键有关的属性);
单向关系都非常简单,这里只贴出双向一对多的代码(将单向一对多和单向多对一合并所得)
District.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="model">
<class name="District" table="district">
<id name="id">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name"></property>
<set name="streets" table="street" inverse="true" cascade="all">
<key column="district"></key>
<one-to-many class="Street"/>
</set>
</class>
</hibernate-mapping>
Street.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="model">
<class name="Street" table="street">
<id name="id">
<column name="id"></column>
<generator class="increment"></generator>
</id>
<property name="name"></property>
<many-to-one name="district">
<column name="district"></column>
</many-to-one>
</class>
</hibernate-mapping>
District.java
private int id;
private String name;
private Set<Street> streets=new HashSet<Street>();
Street.java
private int id;
private String name;
private District district;
Test.java
public static void main(String[] args) {
Configuration cfg=new Configuration().configure();
SessionFactory sf=cfg.buildSessionFactory();
Session session=sf.openSession();
Transaction tr=null;
try{
//最佳实践:最好要先设置一的一方,让多的一方来找多的一方
tr=session.beginTransaction();
District d1=new District();
d1.setName("海淀区");
Street s1=new Street();
s1.setName("北洼路");
d1.getStreets().add(s1);
s1.setDistrict(d1);
//session.save(s1);
Street s2=new Street();
s2.setName("闽南路");
d1.getStreets().add(s2);
s2.setDistrict(d1);
//当一的一方set中的inverse="true"时,多的一方要手动如下代码设置关联
//这样少了一句Update语句,提高了一些效率
//d1.getStreets().add(s2);
//s2.setDistrict(d1);
//session.save(s2);
//使用级联cascade="all" 对d1的操作会自动关联到与它有关的对象
session.save(d1);
session.delete(d1);
tr.commit();
}catch(HibernateException e){
tr.rollback();
e.printStackTrace();
}finally{
session.close();
}
}
ps:cascade和inverse属性一般都设在set(即一的一方)比较好。 cascade可以级联操作,如果保存了district,则与其相关的street的也会保存。
inverse是控制反转,inverse="false"为主动方,将<set>中inverse设为true,则不会自动更新外键值,由多的一方去找一的一方,效率更高。(多个学生找一个老师比一个老师找多个学生效率高)。所以要手动关联d1.getStreets().add(s1);s1.setDistrict(d1);
双向多对多
使用Many2Many不论在哪一方来维护关系都比较的麻烦,而且很多时候关联表中需要加入其他的属性
所以在开发中,经常使用两个一对多来替代多对多
示例如下:
Couse.java
private int id;
private String name;
private Set<TeacherCourse> tcs;
Teacher.java
private int id;
private String name;
private Set<TeacherCourse> tcs;
CouseTeacher.java
private int id;
private double ach;
private Teacher teacher;
private Course course;
Couse.xml
<class name="Course" table="t_course">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="tcs" lazy="extra" inverse="true">
<key column="cid"/>
<one-to-many class="TeacherCourse"/>
</set>
</class>
Teacher.xml
<class name="Teacher" table="t_teacher">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="tcs" lazy="extra" inverse="true">
<key column="tid"/>
<one-to-many class="TeacherCourse"/>
</set>
</class>
CourseTeacher.xml
<class name="TeacherCourse" table="t_teacher_course">
<id name="id">
<generator class="native"/>
</id>
<property name="ach"/>
<many-to-one name="teacher" column="tid"/>
<many-to-one name="course" column="cid"/>
</class>
Test.java
@Test
public void testAdd01() {
Session session = null;
try {
session = HibernateSessionFactory.getSession();
session.beginTransaction();
Teacher t1 = new Teacher();
t1.setName("老张");
session.save(t1);
Teacher t2 = new Teacher();
t2.setName("老刘");
session.save(t2);
Course c1 = new Course();
c1.setName("数据结构");
session.save(c1);
Course c2 = new Course();
c2.setName("计算机组成原理");
session.save(c2);
TeacherCourse tc1 = new TeacherCourse();
tc1.setAch(87);
tc1.setTeacher(t1);
tc1.setCourse(c1);
session.save(tc1);
tc1 = new TeacherCourse();
tc1.setAch(66);
tc1.setTeacher(t1);
tc1.setCourse(c2);
session.save(tc1);
tc1 = new TeacherCourse();
tc1.setAch(190);
tc1.setTeacher(t2);
tc1.setCourse(c1);
session.save(tc1);
tc1 = new TeacherCourse();
tc1.setAch(20);
tc1.setTeacher(t2);
tc1.setCourse(c2);
session.save(tc1);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateSessionFactory.closeSession();
}
}
@Test
public void testLoad01() {
Session session = null;
try {
session = HibernateSessionFactory.getSession();
session.beginTransaction();
Teacher t = (Teacher)session.load(Teacher.class, 1);
//load的时候由于延迟加载,会根据不同的情况取相应的关联对象,所以会发出大量的sql
/**
* 总体来说:最佳实践就是,尽量不使用双向关联,特别不建议使用一的这一方的关联
* 因为从一的这一端取关联对象很有可能会涉及到分页操作,所以基本不会使用
* 在设计的时不是特殊情况不要使用双向关联
*/
System.out.println(t.getName());
for(TeacherCourse tc:t.getTcs()) {
System.out.println(tc.getCourse().getName()+":"+tc.getAch());
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
if(session!=null) session.getTransaction().rollback();
} finally {
HibernateSessionFactory.closeSession();
}
}