1. 程式人生 > >(轉)Hibernate框架基礎——多對多關聯關系映射

(轉)Hibernate框架基礎——多對多關聯關系映射

-- 否則 del 畫圖 兩個 delete pub attribute cnblogs

http://blog.csdn.net/yerenyuan_pku/article/details/52756536

多對多關聯關系映射

多對多的實體關系模型也是很常見的,比如學生和課程的關系。一個學生可以選修多門課程,一個課程可以被多名學生選修。在關系型數據庫中對於多對多關聯關系的處理一般采用中間表的形式,將多對多的關系轉化成兩個一對多的關系。
為了詳細介紹多對多關聯關系映射,終究還是應以一個例子來說明比較印象深刻。我們以老師和學生的關系為例來說明這種多對多關聯關系映射。

雙向多對多

我們最好新建一個普通java工程,如Hibernate_Test,然後在cn.itcast.g_hbm_manyToMany包下新建持久化類——Student.java和Teacher.java。

持久化類——Student.java的代碼如下:

/**
 * 學生
 * @author li ayun
 *
 */
public class Student {
    private Long id;
    private String name;

    private Set<Teacher> teachers = new HashSet<Teacher>(); // 關聯的老師們

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Teacher> getTeachers() {
        return teachers;
    }

    public void setTeachers(Set<Teacher> teachers) {
        this.teachers = teachers;
    }

    @Override
    public String toString() {
        return "[Student: id=" + id + ", name=" + name + "]";
    }
}

持久化類——Teacher.java的代碼如下:

/**
 * 老師
 * @author li ayun
 *
 */
public class Teacher {
    private Long id;
    private String name;

    private Set<Student> students = new HashSet<Student>(); // 關聯的學生們

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Student> getStudents() {
        return students;
    }

    public void setStudents(Set<Student> students) {
        this.students = students;
    }

    @Override
    public String toString() {
        return "[Teacher: id=" + id + ", name=" + name + "]";
    }
}

接著我們就要創建各個持久化類相應的映射配置文件了。但在做之前,我們約莫是要知道數據庫中三張表的結構的,我畫圖表示:
技術分享
現在再來寫各個持久化類相應的映射配置文件,我想應該會容易得多。先在cn.itcast.g_hbm_manyToMany包中創建Student類對應的映射配置文件——Student.hbm.xml。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.g_hbm_manyToMany">
    <class name="Student" table="student">
        <id name="id">
            <generator class="native"></generator> 
        </id>
        <property name="name" />

        <!-- 
            teachers屬性,Set集合,表達的是本類與Teacher的多對多的關系 
            table屬性:中間表(集合表)
            key子元素:集合外鍵(引用當前表主鍵的那個外鍵)
        -->
        <set name="teachers" table="teacher_student" inverse="false">
            <key column="studentId"></key>
            <many-to-many class="Teacher" column="teacherId"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

  • set元素的兩個子元素:key和many-to-many都必須指定column屬性,其中,key和many-to-many分別指定本持久化類和關聯類在連接表中的外鍵列名。
    因此兩邊的key與many-to-many的column屬性交叉相同。也就是說,一邊的set元素的key的cloumn值為a,many-to-many的column為b;則另一邊的set元素的key的column值b,many-to-many的column值為a。
  • 對於雙向n-n關聯,須把其中一端的inverse設置為true,否則可能會造成主鍵沖突

然後也是在該包中創建Teacher類對應的映射配置文件——Teacher.hbm.xml。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.g_hbm_manyToMany">
    <class name="Teacher" table="teacher">
        <id name="id">
            <generator class="native"></generator> 
        </id>
        <property name="name" type="string" column="name" />

        <!-- students屬性,Set集合,表達的是本類與Student的多對多的關系  -->
        <set name="students" table="teacher_student" inverse="true">
            <key column="teacherId"></key>
            <many-to-many class="Student" column="studentId"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

寫到這裏,我們就很清楚以下幾點了:

  1. 雙向n-n關聯需要兩端都使用集合屬性。
  2. 雙向n-n關聯必須使用中間表。
  3. 集合屬性應增加key子元素用以映射外鍵列,集合元素裏還應增加many-to-many子元素關聯實體類。
  4. 在雙向n-n關聯的兩邊都需指定連接表的表名及外鍵列的列名。兩個集合元素set的table元素的值必須指定,而且必須相同

接下來,我們從以下幾個方面來編寫代碼進行測試:

  • 保存新數據,並有關聯關系。
  • 獲取數據,可以獲取到關聯的對方。
  • 解除關聯關系。
  • 刪除對象,看對關聯對象的影響。

我們首先從保存新數據,並有關聯關系第一個方面開始測試,測試代碼如下:

public class App {
    private static SessionFactory sessionFactory = new Configuration() //
            .configure() //
            .addClass(Student.class) // 添加Hibernate實體類(加載對應的映射文件)
            .addClass(Teacher.class) // 添加Hibernate實體類(加載對應的映射文件)
            .buildSessionFactory();

    // 保存,有關聯關系
    @Test
    public void testSave() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------

        // 新建對象
        Student student1 = new Student();
        student1.setName("王同學");

        Student student2 = new Student();
        student2.setName("李同學");

        Teacher teacher1 = new Teacher();
        teacher1.setName("趙老師");

        Teacher teacher2 = new Teacher();
        teacher2.setName("蔡老師");

        // 關聯起來
        student1.getTeachers().add(teacher1);
        student1.getTeachers().add(teacher2);
        student2.getTeachers().add(teacher1);
        student2.getTeachers().add(teacher2);

        teacher1.getStudents().add(student1);
        teacher1.getStudents().add(student2);
        teacher2.getStudents().add(student1);
        teacher2.getStudents().add(student2);

        // 保存
        session.save(student1);
        session.save(student2);
        session.save(teacher1);
        session.save(teacher2);

        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }
}

測試,順利通過,大發!
接下來我們從獲取數據,可以獲取到關聯的對方第二個方面來進行測試。但我們只打算從老師這一方獲取,然後顯示學生那方的數據。

public class App {
    private static SessionFactory sessionFactory = ...

    // 獲取,可以獲取到關聯的對方
    @Test
    public void testGet() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------

        // 獲取一方,並顯示另一方信息
        Teacher teacher = (Teacher) session.get(Teacher.class, 1L);
        System.out.println(teacher);
        System.out.println(teacher.getStudents());

        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }
}

測試,順利通過,大發!
緊接著我們從解除關聯關系第三個方面來進行測試。
無論從哪一方解除關聯關系,均與inverse屬性的值有關系,如果inverse=false就可以解除,如果inverse=true就不可以解除(刪除中間表中的記錄)。

public class App {
    private static SessionFactory sessionFactory = ...

    // 解除關聯關系
    @Test
    public void testRemoveRelation() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------

        // 如果inverse=false就可以解除,如果為true就不可以解除(刪除中間表中的記錄)
        Teacher teacher = (Teacher) session.get(Teacher.class, 1L);
        teacher.getStudents().clear();

        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }
}

最後,我們從刪除對象,看對關聯對象的影響第四個方面來進行測試。我們打算刪除老師這一方。

  • 如果沒有關聯的對方:能刪除。
  • 如果有關聯的對方且inverse=false,由於可以維護關聯關系,它就會先刪除關聯關系,再刪除自己。
  • 如果有關聯的對方且inverse=true,由於不能維護關聯關系,所以會直接執行刪除自己,就會有異常。
public class App {
    private static SessionFactory sessionFactory = ...

    // 刪除對象,對關聯對象的影響
    @Test
    public void testDelete() {
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        // -------------------------------------------

        // 刪除老師方
        // a,如果沒有關聯的對方:能刪除
        // b,如果有關聯的對方且inverse=false,由於可以維護關聯關系,它就會先刪除關聯關系,再刪除自己。
        // c,如果有關聯的對方且inverse=true,由於不能維護關聯關系,所以會直接執行刪除自己,就會有異常
        Teacher teacher = (Teacher) session.get(Teacher.class, 2L);
        session.delete(teacher);

        // -------------------------------------------
        session.getTransaction().commit();
        session.close();
    }
}

測試,順利通過,大發!
結論維護關聯關系是指在中間表中插入或刪除數據

單向多對多

去掉雙向多對多關聯中的任一方,即是單向多對多

(轉)Hibernate框架基礎——多對多關聯關系映射