1. 程式人生 > >Hibernate集合對映、關聯對映

Hibernate集合對映、關聯對映

集合對映

開發流程:

需求分析/資料庫設計、專案設計/ 編碼/測試/實施部署上線/驗收

需求:

使用者購買, 填寫地址!

資料庫:
這裡寫圖片描述
程式碼:

// javabean設計
public class User {

    private int userId;
    private String userName;
    // 一個使用者,對應的多個地址
    private Set<String> address;
    private List<String> addressList = new ArrayList<String>(); 
    //private String[] addressArray; // 對映方式和list一樣     <array name=""></array>
private Map<String,String> addressMap = new HashMap<String, String>(); }

配置

<?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.a_collection"
>
<class name="User" table="t_user"> <id name="userId" column="id"> <generator class="native"></generator> </id> <property name="userName"></property> <!-- set集合屬性的對映 name 指定要對映的set集合的屬性 table 集合屬性要對映到的表 key 指定集合表(t_address)的外來鍵欄位 element 指定集合表的其他欄位 type 元素型別,一定要指定 -->
<set name="address" table="t_address"> <key column="uid"></key> <element column="address" type="string"></element> </set> <!-- list集合對映 list-index 指定的是排序列的名稱 (因為要保證list集合的有序) --> <list name="addressList" table="t_addressList"> <key column="uid"></key> <list-index column="idx"></list-index> <element column="address" type="string"></element> </list> <!-- map集合的對映 key 指定外來鍵欄位 map-key 指定map的key element 指定map的value --> <map name="addressMap" table="t_addressMap"> <key column="uid"></key> <map-key column="shortName" type="string" ></map-key> <element column="address" type="string" ></element> </map> </class> </hibernate-mapping>

操作:

    // 儲存set
    @Test
    public void testSaveSet() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();

        //-- 儲存
        Set<String> addressSet = new HashSet<String>();
        addressSet.add("廣州");
        addressSet.add("深圳");
        // 使用者物件
        User user = new User();
        user.setUserName("Jack");
        user.setAddress(addressSet);

        // 儲存
        session.save(user);

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

    // 儲存list/map
    @Test
    public void testSaveList() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        User user = new User();
        user.setUserName("Tom");
//      // 使用者物件  --  list
//      user.getAddressList().add("廣州");
//      user.getAddressList().add("深圳");
//      // 儲存
//      session.save(user);

        // 使用者物件  --  Map
        user.getAddressMap().put("A0001", "廣州");
        user.getAddressMap().put("A0002", "深圳");

        // 儲存
        session.save(user);

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

關聯對映

需求1:

部門與員工
      一個部門有多個員工;       【一對多】
      多個員工,屬於一個部門    【多對一】

需求2:

專案與開發員工
    一個專案,有多個開發人員!
    一個開發人員,參與多個專案!   【多對多】

多對一對映與一對多

程式碼

需求:員工與部門
資料庫:
設計javabean封裝:
對映:

bean:

public class Dept {

    private int deptId;
    private String deptName;
    // 【一對多】 部門對應的多個員工
    private Set<Employee> emps = new HashSet<Employee>();
public class Employee {

    private int empId;
    private String empName;
    private double salary;
    // 【多對一】員工與部門
    private Dept dept;

配置:
Dept.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.b_one2Many">

    <class name="Dept" table="t_dept">
        <id name="deptId">
            <generator class="native"></generator>
        </id>   
        <property name="deptName" length="20"></property>

        <!-- 
            一對多關聯對映配置  (通過部門管理到員工)
            Dept 對映關鍵點:
            1.  指定 對映的集合屬性: "emps"
            2.  集合屬性對應的集合表: "t_employee"
            3.  集合表的外來鍵欄位   "t_employee. dept_id"
            4.  集合元素的型別

         -->
         <set name="emps">   <!-- table="t_employee" -->
             <key column="dept_id"></key>
             <one-to-many class="Employee"/>
         </set>
    </class>
</hibernate-mapping>

Employee.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.b_one2Many">

    <class name="Employee" table="t_employee">
        <id name="empId">
            <generator class="native"></generator>
        </id>   
        <property name="empName" length="20"></property>
        <property name="salary" type="double"></property>

        <!-- 
            多對一對映配置
            Employee 對映關鍵點:
            1.  對映的部門屬性  :  dept
            2.  對映的部門屬性,對應的外來鍵欄位: dept_id
            3.  部門的型別
         -->
         <many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>   
    </class>
</hibernate-mapping>

測試:

public class App {

    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Dept.class)   
            .addClass(Employee.class)   // 測試時候使用
            .buildSessionFactory();
    }

    // 儲存, 部門方 【一的一方法操作】
    @Test
    public void save() {

        Session session = sf.openSession();
        session.beginTransaction();

        // 部門物件
        Dept dept = new Dept();
        dept.setDeptName("應用開發部");
        // 員工物件
        Employee emp_zs = new Employee();
        emp_zs.setEmpName("張三");
        Employee emp_ls = new Employee();
        emp_ls.setEmpName("李四");
        // 關係
        dept.getEmps().add(emp_zs);
        dept.getEmps().add(emp_ls);

        // 儲存
        session.save(emp_zs);
        session.save(emp_ls);
        session.save(dept); // 儲存部門,部門下所有的員工  

        session.getTransaction().commit();
        session.close();
        /*
         *  結果
         *  Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_dept (deptName) values (?)
            Hibernate: update t_employee set deptId=? where empId=?    維護員工引用的部門的id
            Hibernate: update t_employee set deptId=? where empId=?
         */
    }
    // 【推薦】 儲存, 部員方 【多的一方法操作】
    @Test
    public void save2() {

        Session session = sf.openSession();
        session.beginTransaction();

        // 部門物件
        Dept dept = new Dept();
        dept.setDeptName("綜合部");
        // 員工物件
        Employee emp_zs = new Employee();
        emp_zs.setEmpName("張三");
        Employee emp_ls = new Employee();
        emp_ls.setEmpName("李四");
        // 關係
        emp_zs.setDept(dept);
        emp_ls.setDept(dept);


        // 儲存
        session.save(dept); // 先儲存一的方法
        session.save(emp_zs);
        session.save(emp_ls);// 再儲存多的一方,關係回自動維護(對映配置完)

        session.getTransaction().commit();
        session.close();
        /*
         *  結果
         *  Hibernate: insert into t_dept (deptName) values (?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
            少生成2條update  sql
         */
    }   
}
總結:
  在一對多與多對一的關聯關係中,儲存資料最好的通過多的一方來維護關係,這樣可以減少update語句的生成,從而提高hibernate的執行效率!
配置一對多與多對一,這種叫“雙向關聯”
只配置一對多,           叫“單項一對多”
只配置多對一,           叫“單項多對一”
注意:
    配置了哪一方,哪一方才有維護關聯關係的許可權!

Inverse屬性

Inverse屬性,是在維護關聯關係的時候起作用的。
表示控制權是否轉移。(在一的一方起作用)

Inverse , 控制反轉

Inverse = false  不反轉;   當前方有控制權
True  控制反轉; 當前方沒有控制權

維護關聯關係中,是否設定inverse屬性:
1. 儲存資料 有影響。
如果設定控制反轉,即inverse=true, 然後通過部門方維護關聯關係。在儲存部門的時候,同時儲存員工, 資料會儲存,但關聯關係不會維護。即外來鍵欄位為NULL
2. 獲取資料 無。
3. 解除關聯關係? 有影響。

inverse=false,  可以解除關聯
inverse=true,  當前方(部門)沒有控制權,不能解除關聯關係
(不會生成update語句,也不會報錯)

4. 刪除資料對關聯關係的影響?有影響。

    inverse=false, 有控制權, 可以刪除。先清空外來鍵引用,再刪除資料。
    inverse=true,  沒有控制權: 如果刪除的記錄有被外來鍵引用,會報錯,違反主外來鍵引用約束! 
    如果刪除的記錄沒有被引用,可以直接刪除。
private static SessionFactory sf;
static {
    sf = new Configuration()
        .configure()
        .addClass(Dept.class)   
        .addClass(Employee.class)   // 測試時候使用
        .buildSessionFactory();
}

// 1. 儲存資料 
@Test
public void save() {

    Session session = sf.openSession();
    session.beginTransaction();

    // 部門物件
    Dept dept = new Dept();
    dept.setDeptName("應用開發部");
    // 員工物件
    Employee emp_zs = new Employee();
    emp_zs.setEmpName("張三");
    Employee emp_ls = new Employee();
    emp_ls.setEmpName("李四");
    // 關係
    dept.getEmps().add(emp_zs);
    dept.getEmps().add(emp_ls);  // inverse=true,  不會設定關聯。
                                //                 此時的關聯應該通過員工方維護。


    // 儲存
    session.save(emp_zs);
    session.save(emp_ls);
    session.save(dept); // 儲存部門,部門下所有的員工  

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

//2. 是否設定inverse,對獲取資料的影響?   無.
@Test
public void get() {
    Session session = sf.openSession();
    session.beginTransaction();

    Dept dept = (Dept) session.get(Dept.class, 1);
    System.out.println(dept.getDeptName());
    System.out.println(dept.getEmps());

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

// 3. 是否設定inverse,對解除關聯關係影響?
// inverse=false,  可以解除關聯
// inverse=true,  當前方(部門)沒有控制權,不能解除關聯關係(不會生成update語句,也不會報錯)
// 
@Test
public void removeRelation() {
    Session session = sf.openSession();
    session.beginTransaction();

    // 獲取部門
    Dept dept = (Dept) session.get(Dept.class, 2);
    // 解除關係
    dept.getEmps().clear();

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


//3. 是否設定inverse屬性,在刪除資料中對關聯關係的影響?
// inverse=false, 有控制權, 可以刪除。先清空外來鍵引用,再刪除資料。
// inverse=true,  沒有控制權: 如果刪除的記錄有被外來鍵引用,會報錯,違反主外來鍵引用約束!
//                           如果刪除的記錄沒有被引用,可以直接刪除。
@Test
public void deleteData() {
    Session session = sf.openSession();
    session.beginTransaction();

    // 查詢部門
    Dept dept = (Dept) session.get(Dept.class, 8);
    session.delete(dept);


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

cascade 屬性

cascade 表示級聯操作 【可以設定到一的一方或多的一方】

none          不級聯操作, 預設值
save-update     級聯儲存或更新
delete        級聯刪除
save-update,delete    級聯儲存、更新、刪除
all                 同上。級聯儲存、更新、刪除

hibernate常見面試題: inverse與cascade區別?

// 級聯儲存
@Test
public void save() {

    Session session = sf.openSession();
    session.beginTransaction();

    // 部門物件
    Dept dept = new Dept();
    dept.setDeptName("財務部");
    // 員工物件
    Employee emp_zs = new Employee();
    emp_zs.setEmpName("張三");
    Employee emp_ls = new Employee();
    emp_ls.setEmpName("李四");
    // 關係
    dept.getEmps().add(emp_zs);
    dept.getEmps().add(emp_ls);  

    // 儲存
//      session.save(emp_zs);
//      session.save(emp_ls);
    session.save(dept); // 需要設定級聯儲存; 儲存部門,部門下所有的員工  

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

// 級聯刪除
@Test
public void delete() {
    Session session = sf.openSession();
    session.beginTransaction();

    Dept dept = (Dept) session.get(Dept.class,7);
    session.delete(dept); // 級聯刪除

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

多對多對映

這裡寫圖片描述

/**
 * 開發人員
 * 
 * @author Jie.Yuan
 * 
 */
public class Developer {
    private int d_id;
    private String d_name;
    // 開發人員,引數的多個專案
    private Set<Project> projects = new HashSet<Project>();
}

/**
 * 專案
 * 
 * @author Jie.Yuan
 * 
 */
public class Project {
    private int prj_id;
    private String prj_name;
    // 專案下的多個員工
    private Set<Developer> developers = new HashSet<Developer>();
//Project.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.c_many2many">

    <class name="Project" table="t_project">
        <id name="prj_id">
            <generator class="native"></generator>
        </id>   
        <property name="prj_name" length="20"></property>
        <!-- 
            多對多對映:
            1.  對映的集合屬性: “developers”
            2.  集合屬性,對應的中間表: “t_relation”
            3. 外來鍵欄位:  prjId
            4. 外來鍵欄位,對應的中間表字段:  did
            5.   集合屬性元素的型別
         -->
         <set name="developers" table="t_relation" cascade="save-update">
            <key column="prjId"></key>
            <many-to-many column="did" class="Developer"></many-to-many>
         </set>

    </class>


</hibernate-mapping>


//Developer.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.c_many2many">

    <class name="Developer" table="t_developer">
        <id name="d_id">
            <generator class="native"></generator>
        </id>   
        <property name="d_name" length="20"></property> 
        <!-- 
            多對多對映配置: 員工方
                name  指定對映的集合屬性
                table 集合屬性對應的中間表
                key   指定中間表的外來鍵欄位(引用當前表t_developer主鍵的外來鍵欄位)
                many-to-many
                    column 指定外來鍵欄位對應的專案欄位
                    class  集合元素的型別
         -->
        <set name="projects" table="t_relation">
            <key column="did"></key>
            <many-to-many column="prjId" class="Project"></many-to-many>
        </set>   
    </class>
</hibernate-mapping>

維護關聯關係

設定inverse屬性,在多對多種維護關聯關係的影響?
1) 儲存資料
有影響。
inverse=false ,有控制權,可以維護關聯關係; 儲存資料的時候會把物件關係插入中間表;
inverse=true, 沒有控制權, 不會往中間表插入資料。
2) 獲取資料
無。

3) 解除關係
// 有影響。
// inverse=false ,有控制權, 解除關係就是刪除中間表的資料。
// inverse=true, 沒有控制權,不能解除關係。
4) 刪除資料
有影響。
// inverse=false, 有控制權。 先刪除中間表資料,再刪除自身。
// inverse=true, 沒有控制權。 如果刪除的資料有被引用,會報錯! 否則,才可以刪除