框架学习之Hibernate 第六节 关系映射

学习各种映射关系时总结:

①如何将对象之间的关联关系映射到数据库中

②如何检索出关联对象

 

1.多对一映射

它是使用最多的映射关系,性能上占有优势

关系模型和对象模型:两者并不是一一对应的

举个例子:告诉员工属于哪个部门和告诉部门有哪些员工对于 数据库(关系模型)来说只要有一个就可以

但是对于对象模型不是如此,告诉员工属于哪个部门之后,部门并不会知道自己有哪些员工

幻灯片17

实例:

domain 包中添加

Department .java

/**
* @Author:胡家威  
* @CreateTime:2011-8-15 下午10:03:58
* @Description:
*/

package com.yinger.domain;


public class Department {
    
    private int id;
    private String name;
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    
}

 

Employee.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-15 下午10:05:08
* @Description:
*/

package com.yinger.domain;

public class Employee {
    
    private int id;
    private String name;
    private Department depart;
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public Department getDepart() {
        return depart;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setDepart(Department depart) {
        this.depart = depart;
    }    

}

 

并添加两个新的 xml

Department.hbm.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="com.yinger.domain">

    <class name="Department">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name"/>
    </class>
    
</hibernate-mapping>

 

Employee.hbm.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="com.yinger.domain">

    <class name="Employee">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name"/>
        
        <!-- 多对一映射  name是对应的属性名  column是对应的表的字段名[可以不指定] 
         Hibernate 会自动的根据数据库中的这个字段的值到属性对应的类对应的表里查找对象
         -->
        <many-to-one name="depart" column="depart_id"></many-to-one>
    </class>
    
</hibernate-mapping>

 

然后在 Hibernate.cfg.xml 中添加两个新的mapping

<mapping resource="com/yinger/domain/Department.hbm.xml"/>
<mapping resource="com/yinger/domain/Employee.hbm.xml"/>

 

最后,编写测试类和方法

/**
* @Author:胡家威  
* @CreateTime:2011-8-15 下午11:18:42
* @Description:
*/

package com.yinger.main;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.yinger.domain.Department;
import com.yinger.domain.Employee;
import com.yinger.util.HibernateUtils;

public class Many2One {

    public static void main(String[] args) {
        add();
    }

    private static void add() {
        Department depart = new Department();
        depart.setName("depart name");
        
        Employee emp1 = new Employee();
        emp1.setName("emp1 name");
        emp1.setDepart(depart);
        
        Employee emp2 = new Employee();
        emp2.setName("emp2 name");
        emp2.setDepart(depart);
        
        Session s = null;
        Transaction tr = null;
        try{
            s = HibernateUtils.getSession();
            tr = s.beginTransaction();
            s.save(depart);
            s.save(emp1);
            s.save(emp2);
            
            tr.commit();
        }catch(Exception e){
            if(tr!=null)
                tr.rollback();
        }finally{
            if(s!=null)
                s.close();
        }
    }

}
测试结果:控制台输出

many2one1

 
数据库中的数据

department1department2

 
建表语句:[MySQL 命令行中获取的]

mysql_departmysql_emp

 
修改save的顺序
s.save(emp1);
s.save(emp2);
s.save(depart);
Hibernate的sql语句:多了两天update,因为在保存(insert)emp时还没有建立department

many2one3

 

2.一对多映射

它的关系模型和上面一样,也就是表的结构并不改变,但是对象模型改变了

幻灯片18

实例:

在原有的 Department.java 中添加

属性  private Set<Employee> emps;  以及相应的get和set方法

删掉 Employee.hbm.xml 中的多对一映射

修改 Department.hbm.xml 添加

        <!-- 一对多的映射,key属性一定要有,指定字段名,这个字段是“多”的那个表中的字段名
             class属性是指set中的元素的类型
        -->
        <set name="emps">
            <key column="depart_id" />
            <one-to-many class="Employee"/>
        </set>

 

最后编写测试类:One2Many.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-15 下午11:18:42
* @Description:
*/

package com.yinger.main;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.yinger.domain.Department;
import com.yinger.domain.Employee;
import com.yinger.util.HibernateUtils;

public class One2Many {

    public static void main(String[] args) {
        add();
    }

    private static void add() {
        Department depart = new Department();
        depart.setName("depart name");
        
        Employee emp1 = new Employee();
        emp1.setName("emp1 name");
        
        Employee emp2 = new Employee();
        emp2.setName("emp2 name");
        
        Set<Employee> emps = new HashSet<Employee>();
        emps.add(emp1);
        emps.add(emp2);
        depart.setEmps(emps);
        
        Session s = null;
        Transaction tr = null;
        try{
            s = HibernateUtils.getSession();
            tr = s.beginTransaction();
            s.save(emp1);
            s.save(emp2);
            s.save(depart);
            
            tr.commit();
        }catch(Exception e){
            if(tr!=null)
                tr.rollback();
        }finally{
            if(s!=null)
                s.close();
        }
    }

}

 

测试结果:三条 insert 和 两条 update,先保存emp,这时的emp的depart_id为null,所以当插入了depart之后,就要update了

因为这个时候的emp都是持久对象,Hibernate会自动的检测它们的改变并更新

即使修改了save顺序,结果还是一样,因为插入了depart之后,还要根据 depart.setEmps(emps);  确定depart和emp的关系,所以还是要更新

数据库的结果和上面的多对一是一样的

one2many1

 

3.一对一映射

主对象和从对象

主对象如果没有从对象可以,但是从对象没有主对象是不行的

查找主对象时只用了一条sql语句,使用了outer join

查找从对象时用了两条语句,首先查从对象,然后查主对象(如果要使用从对象对应的主对象)

 

两种映射方法:① 基于主键的 一对一

幻灯片19

② 基于外键的 一对一

幻灯片20

 

测试实例:

添加两个domain Object:

Person.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-16 上午12:44:38
* @Description:
*/

package com.yinger.domain;

public class Person {
    
    private int id;
    private String name;
    private IdCard idCard;
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public IdCard getIdCard() {
        return idCard;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

}

 

IdCard.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-16 上午12:45:35
* @Description:
*/

package com.yinger.domain;

import java.util.Date;

public class IdCard {
    
    private int id;
    private Date life;
    private Person person;
    
    public int getId() {
        return id;
    }
    public Person getPerson() {
        return person;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setPerson(Person person) {
        this.person = person;
    }
    public Date getLife() {
        return life;
    }
    public void setLife(Date life) {
        this.life = life;
    }

}

 

添加 映射文件

如果是第一种,基于主键的一对一:此时 person表中id和id_card 表中的id一一对应

Person.hbm.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="com.yinger.domain">

    <class name="Person">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name"/>
    </class>
    
</hibernate-mapping>

 

IdCard.hbm.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="com.yinger.domain">

    <class name="IdCard" table="id_card">
        <id name="id">
            <generator class="foreign">
                <param name="property">person</param>
            </generator>
        </id>
        <property name="life" />
        <one-to-one name="person" constrained="true"/>
    </class>

</hibernate-mapping>

 

如果是第二种,基于外键的一对一:此时 id_card 表中的字段 person_id 与 person 表中的 id对应

Person.hbm.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="com.yinger.domain">

    <class name="Person">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name"/>
            
    </class>
    
</hibernate-mapping>

 

IdCard.hbm.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="com.yinger.domain">

    <class name="IdCard" table="id_card">

        <!-- 基于外键的一对一映射,加上了unique="true"约束,相当于就是一对一映射 
        首先假设多个idCard对应一个person,但是加上了unique约束之后就变成了一个idCard对应一个person 
        -->
        <id name="id">
            <generator class="native" />
        </id>
        <property name="life" />
        <many-to-one name="person" column="person_id" unique="true"></many-to-one>

    </class>

</hibernate-mapping>

 

添加了 mapping 映射之后,编写测试类 One2One.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-16 上午12:52:21
* @Description:
*/

package com.yinger.main;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.yinger.domain.IdCard;
import com.yinger.domain.Person;
import com.yinger.util.HibernateUtils;

public class One2One {
    
    public static void main(String[] args) {
        add();
    }

    private static void add() {
        Person p = new Person();
        p.setName("person name");
        
        IdCard card = new IdCard();
        card.setLife(new Date());
        
        //p.setIdCard(card);  // 外键一对一映射的这种情况下 id_card表中的person_id字段没有值
        card.setPerson(p);
        
        Session s = null;
        Transaction tr = null;
        try{
            s = HibernateUtils.getSession();
            tr = s.beginTransaction();
            s.save(p);
            s.save(card);
            
            tr.commit();
        }catch(Exception e){
            if(tr!=null)
                tr.rollback();
        }finally{
            if(s!=null)
                s.close();
        }
    }

}

 

结果:

sql语句: 都是 两条 sql 语句

3

数据库数据:

基于 主键的 情况,id_card 表没有 person_id 字段

4 5

 

基于 外键的 情况,id_card 表有 person_id 字段

2 1

4.多对多映射

使用的不是很多,要注意数据量的大小对性能的影响,因为多对多需要查三张表

幻灯片21

 

测试实例:

Teacher.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-16 上午12:44:38
* @Description:
*/

package com.yinger.domain;

import java.util.Set;

public class Teacher {
    
    private int id;
    private String name;
    private Set<Student> stus;
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Set<Student> getStus() {
        return stus;
    }
    public void setStus(Set<Student> stus) {
        this.stus = stus;
    }

}

 

Student.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-16 上午12:44:38
* @Description:
*/

package com.yinger.domain;

import java.util.Set;

public class Student {
    
    private int id;
    private String name;
    private Set<Teacher> teas;
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public void setId(int id) {
        this.id = id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Set<Teacher> getTeas() {
        return teas;
    }
    public void setTeas(Set<Teacher> teas) {
        this.teas = teas;
    }

}

 

hbm.xml

Teacher.hbm.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="com.yinger.domain">

    <class name="Teacher">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name"/>
        <set name="stus" table="teacher_student">
            <key column="teacher_id"></key>
            <many-to-many class="Student" column="student_id"></many-to-many>
        </set>
    </class>
    
</hibernate-mapping>

 

Student.hbm.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="com.yinger.domain">

    <class name="Student">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="name"/>
        <set name="teas" table="teacher_student">
            <key column="student_id"></key>
            <many-to-many class="Teacher" column="teacher_id"></many-to-many>
        </set>
    </class>
    
</hibernate-mapping>

 

添加到了 cfg 中之后,编写测试类:

/**
* @Author:胡家威  
* @CreateTime:2011-8-15 下午11:18:42
* @Description:
*/

package com.yinger.main;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.yinger.domain.Student;
import com.yinger.domain.Teacher;
import com.yinger.util.HibernateUtils;

public class Many2Many {

    public static void main(String[] args) {
        add();
    }

    private static void add() {
        Teacher t1 = new Teacher();
        t1.setName("teacher name 1");
        
        Teacher t2 = new Teacher();
        t2.setName("teacher name 2");
        
        Student s1 = new Student();
        s1.setName("student name 1");
        
        Student s2 = new Student();
        s2.setName("student name 2");
        
        Set<Teacher> teas = new HashSet<Teacher>();
        teas.add(t1);
        teas.add(t2);
        s1.setTeas(teas);
        s2.setTeas(teas);
        
        // 这一部分和上面的部分产生的效果是一样的,但是不能同时存在,否则会因为唯一性约束导致事务回滚,数据库中没有任何数据
//        Set<Student> stus = new HashSet<Student>();
//        stus.add(s1);
//        stus.add(s2);
//        t1.setStus(stus);
//        t2.setStus(stus);
        
        Session s = null;
        Transaction tr = null;
        try{
            s = HibernateUtils.getSession();
            tr = s.beginTransaction();
            s.save(s1);
            s.save(s2);
            s.save(t1);
            s.save(t2);
            
            tr.commit();
        }catch(Exception e){
            if(tr!=null)
                tr.rollback();
        }finally{
            if(s!=null)
                s.close();
        }
    }

}

 

测试结果:

控制台输出:

6

数据库中的数据,新创建了三张表,这种多对多映射就是通过创建一个关联表来实现的

7 8 9

 

5. 组件映射

当一个类的属性很特殊,不是数据库中支持的类型,而且又算不上是一个实体,没有表与之对应时可以使用 组件映射

如果组件的属性不能和表中的字段简单对应的时候可以选择使用自定义用户类型!

幻灯片22

 

测试:

People.java

/**
* @Author:胡家威  
* @CreateTime:2011-8-17 下午09:16:04
* @Description:
*/

package com.yinger.domain;

public class People {
    
    private int id;
    private Name name;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public Name getName() {
        return name;
    }
    public void setName(Name name) {
        this.name = name;
    }

}
 
Name.java
/**
* @Author:胡家威  
* @CreateTime:2011-8-17 下午09:16:36
* @Description:
*/

package com.yinger.domain;

public class Name {

    private String firstName;
    private String lastName;
    
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    
}

 

People.hbm.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="com.yinger.domain">

    <class name="People">
        <id name="id">
            <generator class="native" />
        </id>
        <component name="name" class="Name">
            <property name="firstName"></property>
            <property name="lastName"></property>
        </component>
    </class>
    
</hibernate-mapping>

 

测试类:

/**
* @Author:胡家威  
* @CreateTime:2011-8-16 上午12:52:21
* @Description:
*/

package com.yinger.main;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.yinger.domain.Name;
import com.yinger.domain.People;
import com.yinger.util.HibernateUtils;

public class ComponentTest {
    
    public static void main(String[] args) {
        add();
    }

    private static void add() {
        People p = new People();
        Name n = new Name();
        n.setFirstName("firstName");
        n.setLastName("lastName");
        p.setName(n);
        
        Session s = null;
        Transaction tr = null;
        try{
            s = HibernateUtils.getSession();
            tr = s.beginTransaction();
            s.save(p);
            tr.commit();
        }catch(Exception e){
            if(tr!=null)
                tr.rollback();
        }finally{
            if(s!=null)
                s.close();
        }
    }

}

 

测试结果:

10

 

11

 

6.关系映射的总结

①对于关联关系的查询,Hibernate一般都是使用两条sql语句将它查出来,但是一对一的情况里的查主对象例外,它是一条sql

②分析各种情况可以根据表的结构开始,用 关系模型来分析 对象模型

 

7.集合映射

xml –>  Java集合类型   ->  特点                                      xml中需要配置的信息

set:->Set                     没有重复,不保存加入的顺序

array:->Class[]            有顺序的                                     list-index

list:->List                     可以重复,有顺序的                  list-index

bag:->List                   不保存顺序的List                       list-index

map:->Map                map键值对的映射类型              map-key

编写 xml 方法

幻灯片32

幻灯片33

幻灯片34

 

使用原则:多数情况下都是使用set,需要保证顺序使用list,但是想用List而又不需要保证顺序使用bag

幻灯片35

 

注意点:

① 定义成 Set,而不是 HashSet

② 原来是 HashSet,保存了之后就不再是 HashSet 了,强制转型时会报错的!

原因是Hibernate进行的封装,变成了 PersistentSet,实现了 Set 接口,但是和 HashSet 没有关系,不能转型!

 

关系级联的设置

cascade:关系级联的处理方式,默认是 none

多对多和多对一 一般不配置级联

一对一 一般是 delete(前提是主从对象是同生共死的关系)

一对多 一般是 save-update

delete 就是说,比如删除一个部门,那么就要删除这个部门的所有的员工,这个一般是不符合实际的

 

inverse :是否放弃维护关联关系

如果是true的话,当某个对象是持久对象时,如果和它关联的对象发生了改变,Hibernate是不会产生update语句来进行更新的,所以会产生错误

注意:如果是true的话,那么一对多中的“多”一定要被告知是对应哪个“一”,否则数据库中“一”的字段会出现 null!

 

幻灯片36

猜你喜欢

转载自wcxt2012.iteye.com/blog/1870145