1. 程式人生 > >hibernate 對映檔案的學習

hibernate 對映檔案的學習

hibernate中有兩種主要的配置檔案,一種是主配置檔案,還有一種就是對映檔案,對映檔案需要注意的是以下幾個方面來學習:
參考文件:http://download.csdn.net/download/u011249702/9487540
1.對映基礎
2.普通屬性
3.主鍵
4.聯合屬性
5.關聯關係
6.繼承結構
在配置hibernate的時候要注意以下幾點,這個有的必須有的是非必須的,在使用中要明確和注意
對持久化物件的要求:
①必須體提供一個無參的建構函式,當使用了有引數的構造器的時候,記得要寫上一個預設的構造器,因為hibernate使用了無參的構造器使用反射得到實體物件
②一般都要提供一個標識屬性,和主鍵對應,如果沒有有的功能將失去作用,如save 或 update()
③一般都使用get/set方法對持久化的欄位宣告,其他的方法不太推薦
④使用非Final類,如果使用了Final修飾,將會影響hibernate使用CGLIB生成代理功能,這樣就不能使用懶載入。
⑤可以重寫equals和hashCode(),如果需要把關聯的類實體放入到set中,需要關聯對映時,應該重寫兩個方法。
⑥OID需要定義一個OID找到相應的一個物件(Object identifier- OID)物件標識,資料庫中的主鍵
下面介紹hbm對映檔案,對映檔案是物件和資料庫表的對映,那麼這個必須要滿足很多的約束和定義,在使用中務必要仔細和明確,首先介紹hbm檔案的基本配置屬性:
在hibernate-mapping中必須定義一個class,在class中首先定義的是name屬性,代表的是實體類,需要註明的是類的全限定名,但是為了開發的簡單,可以在hibernate-mapping上定一一個package來指定包,然後是table屬性,指的是資料庫中對應的表,不寫表示使用類的簡簡單名稱,記下來是主鍵和主鍵生成城策略,接下來就是普通屬性

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

<hibernate-mapping package="com.heying.domain">
    <class name="User" table="t_user">
        <id name
="id" column="id" type="int">
<generator class="native"/> </id> <property name="name" column="name" type="string" length="20" /> </class> </hibernate-mapping>

普通屬性使用property 定義,一般定義的name指的是實體對應的屬性,column指的是表中的列名,同樣可以省略,在定義中如果屬性使用到了資料庫中的關鍵字的時候就需要特別注意,比如使用的屬性為Desc,這個在資料庫中是一個關鍵字,可以使用column定義修改。type屬性是配置檔案和資料庫使用的資料型別的一個橋樑,在配置檔案中使用string指的是java.lang.String,也可以這麼寫,但是不建議。也可以定義指定資料庫中的資料型別,但是這樣就不是很通用,需要考慮到使用的資料庫。型別主要有string,text,date,time,timestamp,binary等。

一.主鍵生成策略
assigned:手工指定,使用時必須在程式中指定註定insert的主鍵值;
foreign:外來鍵,只適用基於共享主鍵的一對一關聯對映的時候使用。即一個物件的主鍵是參照的另一張表的主鍵生成的;
native:根據底層資料庫的能力選擇 identity、sequence 或者 hilo 中的一個;
identitiy:由底層資料庫生成識別符號,有底層資料庫提供的一種自增長策略,不是多有資料庫都有自增長(比如Oracle使用的是序列),支援DB2,MySQL,MS SQL Server,Sybase 和 HypersonicSQL;
sequence:序列,在 DB2,PostgreSQL,Oracle,SAP DB,McKoi 中使用;
hilo:高/低位演算法高效的生成(預設分別是 hibernate_unique_key 和 next_hi)作為高位值的來源,高底位額外使用了一張表,下面是高低位的配置例子來源官方文件:

<id name="id" type="long" column="cat_id">
    <generator class="hilo">
        <param name="table">hi_value</param>
        <param name="column">next_value</param>
        <param name="max_lo">100</param>
    </generator>
</id>
Oracle風格的seqhilo(需要支援sequence)
<id name="id" type="long" column="cat_id">
    <generator class="seqhilo">
        <param name="sequence">hi_value</param>
        <param name="max_lo">100</param>
    </generator>
</id>

uuid:128位UUID演算法生成主鍵,內建使用UUID.randomUUID()生成
increment:只有在沒有其他程序往同一張表中插入資料時才能使用。在叢集下不要使用,因為存線上程安全問題,具體實現是先select max(id)+1 賦值給下面。

二.集合屬性
集合屬性分為set,list,array,map四種種基本的對映方式:
①Set集:
比如需要網路購物中,一個帳號可能對應多個收件地址,所以必須對這個帳號分配一個收件方式的地址集合,在已知帳號的情況,怎麼設計收件地址表,那麼需要對帳號的主鍵對映過去的外來鍵,還需要一個地址的元素,當然必不可少還需要指明表的名稱

        <!-- addressSet地址的集合, Set集合,可以在set中使用 order-by=""/sort 排序,前者是資料庫排序,者是記憶體中排序 -->
        <set name="addressSet" table="user_addressSet"> <!-- 表名 -->
            <key column="userId"></key><!-- 外來鍵 -->
            <element type="string" column="address"></element><!-- 列名 -->
        </set>

②List集:

        <!-- addressList地址的集合, List集合 -->
        <list name="addressList" table="user_addressList">
            <key column="userId"></key>
            <index column="idx"></index>
            <element type="string" column="address"></element>
        </list>

③Array陣列:

      <!-- addressArray地址的集合, Array集合 -->
        <array name="addressArray" table="user_addressArray">
            <key column="userId"></key>
            <index column="idx"></index>
            <element type="string" column="address"></element>
        </array>

④Map集:

         <!-- addressMap地址的集合, Map集合 --> 
         <map name="addressMap" table="user_addressMap">
            <key column="userId"></key>
            <map-key type="string" column="key_"></map-key> 
            <element type="string" column="address"></element>
         </map>

三.關聯關係
關聯關係主要分為:一對一,一對多,多對一,多對多,前面介紹的是值型別的集合,這個對應的是實體和實體之間的對應集合,集合元素是一個實體。

(1)一對多,多對一

現實世界中很多的一對多和多對一的關係,比如系和學生,部門員工,領導階層等,下面介紹員工和部門的例子來解釋,看一看物件的設計和實體對應的hbm對映檔案怎麼配置。
第一. 建立兩個實體:Department(部門),Employee(員工)

public class Department {   
    private Integer id;
    private String name;

    private Set<Employee> employees = new HashSet<Employee>(); //關聯的員工
    get/set();
}
public class Employee {
    private Integer id;
    private String name;

    private Department department; // 關聯的部門
    get/set();
}

設計好了實體,應該分析實體和資料庫中的對應關係,會幫助設計資料庫,看到類圖上進行配置資料庫就比較明確!
這裡寫圖片描述

第二. 配置對映檔案
在寫對映檔案的時候,建議大家按照哦對應的實體去寫,這樣避免寫錯了關係,如下:
這裡寫圖片描述

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

<hibernate-mapping package="com.heying.f_hbm_oneToMany">
    <class name="Employee" table="employee">
        <id name="id">
            <generator class="native"></generator>
        </id>

        <property name="name" type="string" column="name"></property>

        <!-- employee 與  department 的關係 many to one ,departmentId 為外來鍵列-->
        <many-to-one name="department" class="Department" column="departmentId"></many-to-one>
         <!-- name type(Class) column 原則 -->  
    </class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.heying.f_hbm_oneToMany">
    <class name="Department" table="department">
        <id name="id">
            <generator class="native"></generator>
        </id>

        <property name="name" type="string" column="name"></property>

        <!-- department 與 employee 的關係  one to many, departmentId為結合外來鍵 -->
        <set name="employees" inverse="true"><!-- 放棄 -->
            <key column="departmentId"></key>
            <one-to-many class="Employee"/>
        </set>
        <!-- 
            <set name="">
                <集合外來鍵><key></key>
                <集合元素><one-to-many/>
            </set>
         -->


    </class>
</hibernate-mapping>

第二. 設定關聯關係
設定了一個部門和兩個員工,初始出來兩個之間是沒有聯絡的,可以雙向設定,兩邊都維護,也可以一方關聯去維護,這邊是雙向都維護,給員工設定部門,給部門繫結員工。一般是有外來鍵的一方維護關聯,沒有外來鍵的一方放棄維護,可以使用inverse=”true”

①建立關聯關係:

@Test                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    public void testSave()throws Exception{

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        // create object
        Department department = new Department();
        department.setName("產品部");

        Employee employee1 = new Employee();
        employee1.setName("zhangshan_1");

        Employee employee2 = new Employee();
        employee2.setName("zhangshan_2");

        // join object 
        employee1.setDepartment(department);
        employee2.setDepartment(department);

        department.getEmployees().add(employee1);
        department.getEmployees().add(employee2);

        session.save(department);
        session.save(employee1);
        session.save(employee2);

        tx.commit();
        session.close();

    }

②解除關聯關係:

    @Test                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    public void remove()throws Exception{
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        //員工方解除,設定部門為Null
        /*Employee employee = (Employee) session.get(Employee.class, 1);
        employee.setDepartment(null);*/

        //從部門方解除關係,inverse="false"可以解除
        Department department = (Department) session.get(Department.class, 1);
        department.getEmployees().clear();

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

③刪除關聯關係
也有的使用級聯刪除cascade=”delete”,如果這個物件放棄維護關聯關係,可以在後面新增cascade=”delete”,表示在刪除這個物件時候,也會級聯刪除對應的物件資料。

    @Test
    public void delete()throws Exception{

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        // 刪除員工對部門沒有影響,也是刪除外來鍵方不影響引用的主鍵方,哪怕部門下沒有員工部門還是可以存在
//      Employee employee = (Employee) session.get(Employee.class, 1);
//      session.delete(employee);

        // 刪除部門方,先會去維護員工方,如果沒有關聯員工,直接刪除,
        // 如果有讓引用刪除的主鍵的外來鍵updatenull,
        // 然後去delete部門方,inverse="false"可以刪除
        Department department = (Department) session.get(Department.class, 1);
        session.delete(department);

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

(2)多對多
多對多的例子也是很多的,學生老師,課程和學生等,下面依照學生和教師為例子
第一. 建立實體:

public class Student {
    private Long id;
    private String name;

    private Set<Teacher> teachers = new HashSet<Teacher>();

    ......
}

public class Teacher {

    private Long id;
    private String name;

    private Set<Student> students = new HashSet<Student>();

    ......
}

第二. 設定關聯關係
多對多的關聯關係,需要建立一箇中間的表去儲存兩個多對多的關係,儲存的分別是對方主鍵生成的外來鍵,通過外來鍵對用關係找到對應的物件的對映關係項。
這裡寫圖片描述

student.hbm.xml:

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

<hibernate-mapping package="com.heying.g_manyToMany">
    <class name="Student" table="student">
        <id name="id">
            <generator class="native"></generator>
        </id>

        <property name="name" type="string" column="name"></property>

        <set name="teachers" table="teacher_student" inverse="true">
            <key column="studentId"></key>
            <many-to-many class="Teacher" column="teacherId"></many-to-many>
        </set>

    </class>
</hibernate-mapping>

teacher.hbm.xml:

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

<hibernate-mapping package="com.heying.g_manyToMany">
    <class name="Teacher" table="teacher">
        <id name="id">
            <generator class="native"></generator>
        </id>

        <property name="name" type="string" column="name"></property>

        <set name="students" table="teacher_student">
            <key column="teacherId"></key>
            <many-to-many class="Student" column="studentId"></many-to-many>
        </set>

    </class>
</hibernate-mapping>

第三. 關聯物件
建立關聯關係可以選擇一方去維護,這個視情況決定哪邊維護設定:inverse=”true”放棄維護,由對方維護,雙方都放棄,中間表將不會插入資料。
①建立關聯關係

    @Test                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    public void testSave()throws Exception{

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

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

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

        Student student1 = new Student();
        student1.setName("學生1");

        Student student2 = new Student();
        student2.setName("學生2");

        //對映關係
        student1.getTeachers().add(teacher1);
        student1.getTeachers().add(teacher2);
        student2.getTeachers().add(teacher2);

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

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

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

②解除關聯關係
同樣受維護物件影響,應該檢視配置檔案決定誰維護。

    @Test                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    public void remove()throws Exception{

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        Teacher teacher = (Teacher) session.get(Teacher.class, 2L);
        teacher.getStudents().clear();  

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

③刪除關聯關係
同樣有維護方決定,也可以使用級聯cascade=”delete”,save-update同理,在儲存的時候只儲存一方時候沒有save-update會報錯。值得注意的是:一般情況下是一對一和一對多使用級聯,多對多一般不使用。

    @Test
    public void delete()throws Exception{

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        Teacher teacher = (Teacher) session.get(Teacher.class, 1L);
        session.delete(teacher);

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

在Hibernate中物件通常有4種狀態,臨時,持久,遊離和刪除
臨時狀態:一般為新new出來的物件,這些物件沒有即沒有和資料庫對應,也沒有和session關聯上,一般需要通過session.save()來儲存為持久狀態;
持久狀態:和資料庫關聯有session管理,比如在session中獲取一個物件,然後通過物件的setter()來修改物件,會看到一條update語句更新到資料庫,這個就是持久化物件,session會檢測這個物件的全過程,有改變就會同步到資料庫;
遊離狀態:資料庫有對應的記錄,不在session中管理,這個狀態的物件不會修改資料庫;
刪除狀態:使用delete()後的物件。

在session中有一個快取的存在需要注意,操作快取的操作主要有:
①session.flush():立即執行SQL語句
②session.evict(obj):清除一個指定物件
③session.clear():清空快取,一般儲存很多的資料可能會引起session的過載而記憶體溢位

在session中要快取有時候能提高效率有時候也能讀到不是真實的資料,比如一個事務中查詢兩次,在第一個查詢結束後,第二個查詢開始前,另外一個事務中的事務對這條資料做出改變,在第二次查詢中就算使用了清除快取和重新整理快取(session.refresh(obj))都不會讀到最新修改的資料,這是因為資料庫的預設隔離級別有關係,一個事務加一個鎖,只能讀到本次事務開始狀態時候資料庫對應的資料,其他事務做出改變只能在這個改變結束後在下一個事務中讀取到。

事務隔離級別參考:http://blog.csdn.net/u011249702/article/details/50999576 最後面
一般是四個隔離界別的:
1.讀未提交:讀沒有commit的資料 級別為:0001(16) 1
2.讀已提交:讀別人commit後的資料 級別為:0010(16) 2
3.可重複讀:讀本次事務之內的資料(大部分資料庫的預設級別)級別為: 0100(16) 4
4.不可併發(序列):一個一個排隊讀取 級別為:1000(16) 8

改變本次連線的隔離級別配置:

<property name="hibernate.connection.isolation">4</property>

session中物件的載入:
①get():立即載入,返回真實物件,沒有返回null
②load():延時載入,返回代理物件,沒有拋exception,不會馬上執行SQL語句,會把查詢的結果放到快取,一般在呼叫到需要查詢的時候才會開始執行,如果沒有必要查就不會查詢資料庫,這就是懶載入,要求物件的類不是Final修飾的(需要子類建立代理物件),預設情況下的配置是使用懶載入的。
③createQuery():查詢語句
④createCriteria():查詢語句

(3)一對一
一對一的關係很多,通常是一個物件的特有屬性,比如在特定情況下一個人一個帳號對應,一個人一張身份證等
一般有兩種方式來實現:

基於外來鍵+唯一約束
基於外來鍵和之前的一樣,只是在生成上新增一個唯一約束。
這裡寫圖片描述

第一實體:

public class Person {

    private Integer id;
    private String name;

    private IdCard idCard;
    ......
}
public class IdCard {

    private Integer id;
    private String number;

    private Person person;
    ......
}

第二 配置關聯關係

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

<hibernate-mapping package="com.heying.oneToOneByFk">
    <class name="Person" table="person">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name" type="string" column="name"></property>

        <!-- property-ref: 關聯對方中外來鍵對應的屬性名 -->
        <one-to-one name="idCard" class="IdCard" property-ref="person"></one-to-one>

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

<hibernate-mapping package="com.heying.oneToOneByFk">
    <class name="IdCard" table="idcard">
        <id name="id">
            <generator class="native"></generator>
        </id>

        <property name="number" type="string" column="name"></property>

        <many-to-one name="person" class="Person" column="personId" unique="true"></many-to-one>

    </class>
</hibernate-mapping>

第三 關聯物件
①建立關聯:略
②解除關聯:
值得注意的是:只能從有外來鍵方解除,只能在有外來鍵方維護關係,這邊只是獲取idCard的物件設定person為null
③刪除:同解除相似,只能外來鍵方刪除,在無外來鍵方刪除就會跑出異常

基於主鍵方式
基於主鍵生成另外一張表的主鍵表明這個值不能為null 且unique 所以不能單獨存在,必須和主表對應存在,而基於外來鍵可以把外來鍵設定為null而資料獨立存在。
這裡寫圖片描述

第一 實體

第二 配置關聯關係

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

<hibernate-mapping package="com.heying.oneToOneByPk">
    <class name="Person" table="person">
        <id name="id">
            <generator class="native"></generator>
        </id>

        <property name="name" type="string" column="name"></property>

        <!-- property-ref: 關聯對方中外來鍵的屬性名-->
        <one-to-one name="idCard" class="IdCard"></one-to-one>

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

<hibernate-mapping package="com.heying.oneToOneByPk">
    <class name="IdCard" table="idcard">
        <id name="id">

            <!-- 
                使用基於主鍵的一對一的,有外來鍵的一方,主鍵生成策略為foreign
             -->
            <generator class="foreign">
                <!-- property: 生成主鍵值時候所根據的物件 -->
                <param name="property">person</param>
            </generator>
        </id>

        <property name="number" type="string" column="name"></property>

        <one-to-one name="person" class="Person" constrained="true"></one-to-one>
    </class>
</hibernate-mapping>

第三 關聯物件
①建立關聯:略
②解除關聯:
值得注意的是:基於主鍵的一對一雙方都不可以解除關聯關係,因為主鍵有非空約束
③刪除:同上面相似,只能外來鍵方刪除,在無外來鍵方刪除就會丟擲異常

四 . 繼承結構
繼承結構是對於有繼承父類的關係結構而使用的,繼承機構普遍存在,這邊比如教師和學生同時都繼承人這個類。實體這邊就不用寫,學生和教師這邊只給兩個簡單的Id屬性,代表教師編號和學號。

①使用subclass 定義子類(公用一張表)

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

<hibernate-mapping package="com.heying.extend">

    <!--discriminator-value : 用於鑑別是屬於哪一個類,預設為全限定名-->

    <class name="Person" table="person" discriminator-value="Person">
        <id name="id">
            <generator class="native"/>
        </id>

        <!-- 指定型別,只通過資料的特徵判斷是哪一個子類,有時候會難以判斷,所以新增一個屬性 -->
        <discriminator type="string" column="class_"></discriminator>        

        <property name="name" type="string"></property>
        <property name="age" type="integer"></property>

         <!-- 子類 -->
         <subclass name="Student" discriminator-value="Student">
            <property name="studentId"></property>
         </subclass>

         <!-- 子類 -->
         <subclass name="Teacher" discriminator-value="Teacher">
            <property name="teacherId"></property>
         </subclass>

    </class>
</hibernate-mapping>

②使用joined-subclass 定義子類(每個類一張表,抽象類也對應一張)

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

<hibernate-mapping package="com.heying.extend2">

     <!-- 每個類一張表,子類表是基於父類表主鍵一對一對映 -->   
    <class name="Person" table=