Hibernate的緩存和幾種特殊的映射(09)

Hibernate的緩存和幾種特殊的映射

一. 理解對(duì)象的狀態(tài)

Hibernate中對(duì)象的幾種狀態(tài): 臨時(shí)狀態(tài),持久化狀態(tài),游離狀態(tài)

臨時(shí)狀態(tài): 直接new關(guān)鍵字創(chuàng)建的普通對(duì)象,不在session下管理,數(shù)據(jù)庫中沒有對(duì)應(yīng)的記錄,即普通的一個(gè)實(shí)例.

持久化狀態(tài): 當(dāng)調(diào)用session的save/saveOrUpdate/get/load/list等方法的時(shí)候,對(duì)象就是持久化狀態(tài),處于持久化狀態(tài)的對(duì)象,當(dāng)對(duì)對(duì)象的屬性進(jìn)行修改的時(shí)候,會(huì)反映到數(shù)據(jù)庫中.
特點(diǎn): 處于session的管理中,數(shù)據(jù)庫中有對(duì)應(yīng)的記錄.

游離狀態(tài): 不處于session的管理,數(shù)據(jù)庫有對(duì)應(yīng)的記錄,session關(guān)閉后,對(duì)象的狀態(tài)會(huì)不存在.

二. 一級(jí)緩存

使用緩存的目的: 減少對(duì)數(shù)據(jù)庫的訪問次數(shù),提升Hibernate的執(zhí)行效率,Hibernate中的緩存分為一級(jí)緩存和二級(jí)緩存.

一級(jí)緩存基本概念:
* Hibernate的一級(jí)緩存: 也叫做session的緩存,可以在session范圍內(nèi)減少數(shù)據(jù)庫的訪問次數(shù),只在session范圍內(nèi)有用,session關(guān)閉,一級(jí)緩存失效.
* 當(dāng)調(diào)用session的save/saveOrUpdate/get/load/list/iterator 等方法的時(shí)候,都會(huì)把對(duì)象放入session的緩存中.
* session的緩存由Hibernate維護(hù),用戶不能操作緩存的內(nèi)容,如果需要操作緩存的內(nèi)容,必須通過Hibernate提供的evict/clear方法操作.

特點(diǎn): 只在當(dāng)前session有效,作用時(shí)間斷,效果不是特別明顯.在短時(shí)間內(nèi)多處操作數(shù)據(jù)庫,效果比較明顯.

緩存相關(guān)的幾個(gè)方法:
    session.flush() : 刷新緩存,讓一級(jí)緩存與數(shù)據(jù)庫同步
    session.evict(obj) 清空一級(jí)緩存中指定的對(duì)象
    session.clear() 清空一級(jí)緩存中的所有對(duì)象
    
    使用場(chǎng)景: 在批量操作的時(shí)候
        session.flush(); //先與數(shù)據(jù)庫同步
        session.clear(); //再清空一級(jí)緩存內(nèi)容


注意點(diǎn):
    不同的session不會(huì)共享緩存數(shù)據(jù). 比如Dept dept = session1.get(Dept.class,1);
    
    session2.update(dept);//dept放入session2的緩存.
    dept.setDeptName("name");//如果生成2條update SQL語句,說明不同的session使用不同的緩存區(qū),不能共享數(shù)據(jù).
    
    list和iterator的區(qū)別:
    list():會(huì)一次性把所有記錄都查詢出來,會(huì)放入緩存,但是不會(huì)從緩存中獲取數(shù)據(jù).
    
    iterator: N+1次查詢,N表示總的記錄數(shù),即會(huì)先發(fā)送一條語句查詢所有的記錄的主鍵(1次)
    再根據(jù)每一個(gè)主鍵去數(shù)據(jù)庫查詢(N)
    會(huì)把數(shù)據(jù)放入緩存,也會(huì)從緩存中取數(shù)據(jù).

  • Hibernate的一級(jí)緩存測(cè)試:

Javabean設(shè)計(jì)

Dept.java

package com.mrq.domain;

public class Dept {
    private Integer deptId;
    private String deptName;
    public Integer getDeptId() {
        return deptId;
    }
    public void setDeptId(Integer deptId) {
        this.deptId = deptId;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    @Override
    public String toString() {
        return "Dept [deptId=" + deptId + ", deptName=" + deptName + "]";
    }
}

Dept.hbm.xml配置

<?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.mrq.domain">
    <class name="Dept" table="t_dept2">
        <id name="deptId">
            <generator class="native"></generator>
        </id>
        
        <property name="deptName" type="string"></property>
        
    </class>
</hibernate-mapping>

  • 緩存測(cè)試
    @Test
    public void testCache() throws Exception{
        Session session = sf.openSession();
        session.beginTransaction();
        
        Dept dept = null;
        dept = (Dept)session.get(Dept.class, 1);//向數(shù)據(jù)庫查詢
        System.out.println(dept);
        dept = (Dept)session.get(Dept.class, 1);//不查詢,使用session緩存
        
        session.getTransaction().commit();
        session.close();
    }

運(yùn)行結(jié)果類似:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=人事部]

從運(yùn)行的結(jié)果分析:這里使用了兩個(gè)get方法,但是只進(jìn)行了一次SQL查詢,說明第二次的get是從緩存中獲取

  • flush方法同步數(shù)據(jù)
    @Test
    public void flush() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        Dept dept = null;
        dept = (Dept) session.get(Dept.class, 5);
        dept.setDeptName("行政部");
        // 刷新數(shù)據(jù),和數(shù)據(jù)庫同步
        session.flush();
        
        dept.setDeptName("研發(fā)部");
        
        session.getTransaction().commit();  // 相當(dāng)于session.flush();
        session.close();
    }

結(jié)果如下:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?

進(jìn)行了2次update,一次是flush,一次是commit操作.

  • clear清除緩存
    @Test
    public void clear() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        
        Dept dept = null;
        //放入緩存
        dept = (Dept) session.get(Dept.class, 5);
        // 清除緩存對(duì)象 
        //session.clear(); // 全部清除
        session.evict(dept);// 清除特定對(duì)象       
        dept = (Dept) session.get(Dept.class, 5);//重新從數(shù)據(jù)庫獲取對(duì)象

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

結(jié)果:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
清除緩存后
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?

進(jìn)行了2次select操作,一次是在清除緩存之后

  • 不同的session不共享數(shù)據(jù)
@Test
    public void sessionTest() throws Exception {
        Session session1 = sf.openSession();
        session1.beginTransaction();
        Session session2 = sf.openSession();
        session2.beginTransaction();
        
        // dept放入session1中
        Dept dept = (Dept) session1.get(Dept.class, 1);
        // dept放入session2中
        session2.update(dept);
        
        //
        dept.setDeptName("人事部");

        session1.getTransaction().commit();  // session1.flush();
        session1.close();
        session2.getTransaction().commit();  // session2.flush();
        session2.close();
    }

運(yùn)行結(jié)果:

Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?
Hibernate: update t_dept2 set deptName=? where deptId=?

生成2條update SQL語句,說明不同的session使用不同的緩存區(qū),不能共享數(shù)據(jù).

2.1 list和iterator的緩存

  • list操作
    @Test
    public void list() {
        Session session = sf.openSession();
        session.beginTransaction();
        
        //HQL查詢
        Query query = session.createQuery("from Dept");
        List<Dept> list = query.list();
        System.out.println(list);
        
        session.getTransaction().commit();
        session.close();
    }

運(yùn)行結(jié)果:

Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
[Dept [deptId=1, deptName=事業(yè)部], Dept [deptId=2, deptName=文化部1], Dept [deptId=3, deptName=文化部2], Dept [deptId=4, deptName=文化部3], Dept [deptId=5, deptName=研發(fā)部]]

可以看到,list操作會(huì)一次性的把數(shù)據(jù)讀取出來

  • iterator操作
    @Test
    public void iterator() {
        Session session = sf.openSession();
        session.beginTransaction();
        
        //HQL查詢
        Query query = session.createQuery("from Dept");
        Iterator<Dept> iterator = query.iterate();
        while (iterator.hasNext()) {
            Dept dept = (Dept) iterator.next();
            System.out.println(dept);
        }
        session.getTransaction().commit();
        session.close();
    }

運(yùn)行結(jié)果:

Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_     //執(zhí)行一次次數(shù)查詢操作
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=事業(yè)部]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=2, deptName=文化部1]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=3, deptName=文化部2]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=4, deptName=文化部3]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=5, deptName=研發(fā)部]

可以看到,iterator方法,會(huì)先進(jìn)行一次次數(shù)查詢,再進(jìn)行N次對(duì)象查詢.

  • list和iterator的緩存測(cè)試

list:

        Session session = sf.openSession();
        session.beginTransaction();
        
        Query query = session.createQuery("from Dept");
        List<Dept> list = query.list();
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
        System.out.println("==============List====");
        list = query.list();
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
        session.getTransaction().commit();
        session.close();

運(yùn)行結(jié)果1:

Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]
==============List====
Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]

從list方法運(yùn)行結(jié)果分析: 兩次list都進(jìn)行了同樣的向數(shù)據(jù)庫發(fā)送SQL語句進(jìn)行查詢.表示list不會(huì)從session中獲取數(shù)據(jù).

iterator:

        /*
         * Iterator
         * */
        Session session = sf.openSession();
        session.beginTransaction();
        Query query2 = session.createQuery("from Dept");
        Iterator<Dept> iterator = query2.iterate();
        while (iterator.hasNext()) {
            Dept dept = (Dept) iterator.next();
            System.out.println(dept);
        }
        
        System.out.println("====iterator===");
        iterator = query2.iterate();
        while (iterator.hasNext()) {
            Dept dept = (Dept) iterator.next();
            System.out.println(dept);
        }
        
        
        session.getTransaction().commit();
        session.close();

運(yùn)行結(jié)果2:

Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=1, deptName=事業(yè)部]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=2, deptName=文化部1]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=3, deptName=文化部2]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=4, deptName=文化部3]
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept2 dept0_ where dept0_.deptId=?
Dept [deptId=5, deptName=研發(fā)部]
====iterator===
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]

iterator結(jié)果分析: 先進(jìn)行一次次數(shù)查詢,再執(zhí)行N次查詢對(duì)象. 第二次iterator查詢,只進(jìn)行了一次次數(shù)查詢,并不進(jìn)行對(duì)象查詢,而是直接從緩存中獲取.

下面的測(cè)試也證明:list獲取會(huì)把數(shù)據(jù)緩存到session,iterator操作也可以從list緩存的數(shù)據(jù)中獲取對(duì)象

@Test
    public void list_iterator() throws Exception {
        Session session = sf.openSession();
        session.beginTransaction();
        Query q = session.createQuery("from Dept");
        
        // 
        List<Dept> list = q.list(); 
        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i));
        }
        
        // 
        Iterator<Dept> it = q.iterate();
        while(it.hasNext()){
            Dept user = it.next();
            System.out.println(user);
        }
        
        session.getTransaction().commit();  
        session.close();
    }

運(yùn)行結(jié)果如下

Hibernate: select dept0_.deptId as deptId0_, dept0_.deptName as deptName0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]
Hibernate: select dept0_.deptId as col_0_0_ from t_dept2 dept0_
Dept [deptId=1, deptName=事業(yè)部]
Dept [deptId=2, deptName=文化部1]
Dept [deptId=3, deptName=文化部2]
Dept [deptId=4, deptName=文化部3]
Dept [deptId=5, deptName=研發(fā)部]

三. 懶加載

get方法和load方法的區(qū)別:
    get: 及時(shí)加載,只要調(diào)用get方法立即向數(shù)據(jù)庫查詢
    load. 默認(rèn)使用懶加載,當(dāng)用到數(shù)據(jù)的時(shí)候才向數(shù)據(jù)庫查詢
    
懶加載(lazy) :當(dāng)用到數(shù)據(jù)的時(shí)候才向數(shù)據(jù)庫查詢,是Hibernate的懶加載特性,主要的目的是提供程序的執(zhí)行相率

lazy的取值:
    TRUE: 使用懶加載
    FALSE: 關(guān)閉懶加載
    extra: 在集合數(shù)據(jù)懶加載的時(shí)候提升效率,在真正使用數(shù)據(jù)的時(shí)候才向數(shù)據(jù)庫發(fā)送查詢SQL語句;如果調(diào)用集合的size()/isEmpty()方法,只是統(tǒng)計(jì),不真正查詢數(shù)據(jù)
    
    ?   懶加載異常
Session關(guān)閉后薪捍,不能使用懶加載數(shù)據(jù)照卦!
如果session關(guān)閉后,使用懶加載數(shù)據(jù)報(bào)錯(cuò):
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
        如何解決session關(guān)閉后不能使用懶加載數(shù)據(jù)的問題姑廉?
         // 方式1: 先使用一下數(shù)據(jù)
        //dept.getDeptName();
        // 方式2:強(qiáng)迫代理對(duì)象初始化
        Hibernate.initialize(dept);
        // 方式3:關(guān)閉懶加載
            設(shè)置lazy=false;
        // 方式4: 在使用數(shù)據(jù)之后,再關(guān)閉session壹哺! 

四: 特別的多對(duì)一映射(一對(duì)一映射)

一對(duì)一映射的例子:身份證與用戶的關(guān)系

映射有兩種方式:基于外鍵映射(用戶使用外鍵,引用身份證ID)和基于主鍵映射(用戶ID左外身份證ID),兩種方式設(shè)計(jì)都差不多,主要在于映射文件的設(shè)計(jì)

4.1 基于外鍵的映射配置

javabean設(shè)計(jì).

User.java

package com.mrq.domain;


public class User {
    private Integer userId;
    private String userName;
    private IdCard idCard;
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public IdCard getIdCard() {
        return idCard;
    }
    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }
}

身份證IdCard.java

package com.mrq.entity;

//身份證類
public class IdCard {
    private String cardNum;//身份證主鍵
    private String address;//身份證地址
    private User user;//身份證與用戶一對(duì)一關(guān)系
    public String getCardNum() {
        return cardNum;
    }
    public void setCardNum(String cardNum) {
        this.cardNum = cardNum;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}

  • 映射文件的配置要點(diǎn):
一對(duì)一映射,有外鍵方
    unique="true"   給外鍵字段添加唯一約束
        

User.hbm.xml

<?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.mrq.entity">
    <class name="User" table="t_user2">
        <id name="userId">
            <generator class="native"></generator>
        </id>
        
        <property name="userName" type="string"></property>
        
       <!-- 
            一對(duì)一映射:沒有外鍵方
        -->
        
        <one-to-one name="idCard" class="IdCard"></one-to-one>
        
    </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://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.mrq.entity">
    <class name="IdCard" table="t_idCard">
        <id name="cardNum">
            <generator class="assigned"></generator>
        </id>
        
        <property name="address" type="string"></property>
        
       <!-- 
            一對(duì)一映射有外鍵方
            unique="true":給外鍵字段添加唯一約束
        -->
        
        <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>
        
    </class>
</hibernate-mapping>

測(cè)試方法

//一對(duì)一映射:基于外鍵
    @Test
    public void testOne2One() {
        Configuration configuration = new Configuration();
        SessionFactory sf = configuration.configure().buildSessionFactory();
        Session session = sf.openSession();
        session.beginTransaction();
        
        User user = new User();
        user.setUserName("小明!");
        
        IdCard idCard = new IdCard();
        idCard.setAddress("山西長治");
        idCard.setCardNum("8673287979Ax");
        
        idCard.setUser(user);
        session.save(idCard);
        session.getTransaction().commit();
        session.close();
    }

4.2 基于主鍵的設(shè)計(jì)

一對(duì)一映射脆荷,有外鍵方
            (基于主鍵的映射)
             constrained="true"  指定在主鍵上添加外鍵約束
主鍵生成方式: foreign  即把別的表的主鍵作為當(dāng)前表的主鍵蛋逾;
                property (關(guān)鍵字不能修改)指定引用的對(duì)

User bean保持不變

IdCard.java

package com.mrq.domain;

//身份證類
public class IdCard {
    private Integer user_id;//主鍵
    private String cardNum;//身份證主鍵
    private String address;//身份證地址
    private User user;//身份證與用戶一對(duì)一關(guān)系
    
    
    public Integer getUser_id() {
        return user_id;
    }
    public void setUser_id(Integer user_id) {
        this.user_id = user_id;
    }
    public String getCardNum() {
        return cardNum;
    }
    public void setCardNum(String cardNum) {
        this.cardNum = cardNum;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
}

映射配置文件

IdCard.hbm.xml

<?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.mrq.domain">
    <class name="IdCard" table="t_idCard2">
        <id name="user_id">
            <!-- 
                id節(jié)點(diǎn)指定的是主鍵映射,即user_id是主鍵
                主鍵生成方式:foreign,即把別的表的主鍵作為當(dāng)前表的主鍵:
                property: 指定引用的對(duì)象,對(duì)象的全名
             -->
            
            <generator class="foreign">
                <param name="property">user</param>
            </generator>
        </id>
        
        <property name="address" type="string"></property>
        <property name="cardNum" length="20"></property>
        
       <!-- 
            一對(duì)一映射有外鍵方
            基于主鍵
            constrained="true" 指定在主鍵上添加外鍵約束
        -->
        
    <one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one>        
    </class>
</hibernate-mapping>

測(cè)試:

//一對(duì)一映射,基于主鍵
    @Test
    public void testOne2OneOther() {
        Configuration configuration = new Configuration();
        SessionFactory sf = configuration.configure().buildSessionFactory();
        Session session = sf.openSession();
        session.beginTransaction();
        
        User user = new User();
        user.setUserName("嘻嘻");
        
        IdCard idCard = new IdCard();
        idCard.setAddress("山西長治");
        idCard.setCardNum("SDASDS3233Ax");
        
        idCard.setUser(user);
        session.save(idCard);
        session.getTransaction().commit();
        session.close();
    }

4.3 組合(組件)映射

  • 簡(jiǎn)單理解類的關(guān)系:
類的關(guān)系:
    組合關(guān)系:一個(gè)類中包含了另外一個(gè)類.這兩個(gè)類就是組合關(guān)系
        比如: 汽車和車輪,禮物盒子和禮物
    繼承關(guān)系: 一個(gè)類繼承另外一個(gè)類,即為繼承關(guān)系
    

組合映射的例子:

  • 組件類和被包含的組件類,共同映射到一張表

汽車和車輪

Car.java

package com.mrq.component;

public class Car {
    
    private Integer id;
    private String name;
    private Wheel wheel;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Wheel getWheel() {
        return wheel;
    }
    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
    @Override
    public String toString() {
        return "Car [id=" + id + ", name=" + name + ", wheel=" + wheel + "]";
    }
}

wheel.java

package com.mrq.component;

public class Wheel {
    private Integer count;
    private Integer size;
    public Integer getCount() {
        return count;
    }
    public void setCount(Integer count) {
        this.count = count;
    }
    public Integer getSize() {
        return size;
    }
    public void setSize(Integer size) {
        this.size = size;
    }
    @Override
    public String toString() {
        return "Wheel [count=" + count + ", size=" + size + "]";
    }
}

  • 組合關(guān)系映射的要點(diǎn): component節(jié)點(diǎn)的使用

Car.hbm.xml

<?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.mrq.component">
    <class name="Car" table="t_car">
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        <property name="name" type="string"></property>
        
       <!-- 
            組件映射
        -->
        
        <component name="wheel" class="Wheel">
            <property name="size"></property>
            <property name="count"></property>
        </component>
        
    </class>
</hibernate-mapping>

4.4 繼承映射的4種配置方式

  • 方式一:簡(jiǎn)單繼承關(guān)系映射,直接寫父類的屬性,每個(gè)子類對(duì)應(yīng)一個(gè)表

Animal.java

package com.mrq.extends1;

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

Cat類繼承Animal類

package com.mrq.extends1;

public class Cat extends Animal{
    private String catchMouse;

    public String getCatchMouse() {
        return catchMouse;
    }

    public void setCatchMouse(String catchMouse) {
        this.catchMouse = catchMouse;
    }
    
}

簡(jiǎn)單繼承的映射配置,和普通的類類似

<?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.mrq.extends1">
    <class name="Cat" table="t_cat">
    <!-- 簡(jiǎn)單繼承映射,父類屬性直接寫 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        <property name="name" type="string"></property>
        <property name="catchMouse"></property>
        
    </class>
</hibernate-mapping>
  • 方式二:所有子類映射到一張表
什么情況用白对?
    子類教多,且子類較為簡(jiǎn)單换怖,即只有個(gè)別屬性甩恼!
    好處:因?yàn)槭褂靡粋€(gè)映射文件, 減少了映射文件的個(gè)數(shù)沉颂。
    缺點(diǎn):(不符合數(shù)據(jù)庫設(shè)計(jì)原則)
一個(gè)映射文件: Animal.hbm.xml
    通過鑒別器區(qū)分是哪個(gè)子類的信息

Animal.hbm.xml

  • 關(guān)鍵節(jié)點(diǎn)discriminator和sub-class
<?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.mrq.extends2">
    <class name="Animal" table="t_animal">
    <!-- 簡(jiǎn)單繼承映射,父類屬性直接寫 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        <!-- 指定鑒別字段:區(qū)分不同的類 -->
        <discriminator column="class_type"></discriminator>
        
        
        <property name="name" type="string"></property>
        <!-- 
            子類貓:
            使用subclass節(jié)點(diǎn)映射一個(gè)子類
            要指定鑒別器字段的值,如果不設(shè)置,默認(rèn)為子類的全名
            
         -->
         <subclass name="Cat" discriminator-value="cat_">
            <property name="catchMouse"></property>
         </subclass>
         
         <subclass name="Tiger" discriminator-value="tiger_">
            <property name="eatSheep"></property>
         </subclass>
         
         
        
    </class>
</hibernate-mapping>

寫法較為簡(jiǎn)單:所有子類用一個(gè)映射文件条摸,且映射到一張表!
但數(shù)據(jù)庫設(shè)計(jì)不合理!
(不推薦用铸屉。)

  • 方式三: 每一個(gè)類都映射一張表,包括父類

關(guān)鍵節(jié)點(diǎn):joined-subclass

Animal.hbm.xml

<?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">

<!-- 繼承映射,每一個(gè)類都對(duì)應(yīng)一張表,父類也對(duì)應(yīng)一張表 -->
<hibernate-mapping package="com.mrq.extends3">
    <class name="Animal" table="t_animal2">
    <!-- 簡(jiǎn)單繼承映射,父類屬性直接寫 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        
        
        <property name="name" type="string"></property>
        <!-- 
            子類貓:
            key:指定外鍵
            
         -->
         <joined-subclass name="Cat" table="t_cat2">
            <key column="t_animal_id"></key>
            <property name="catchMouse"></property>
         </joined-subclass>
         <!-- 子類:老虎 -->
        <joined-subclass name="Tiger" table="t_tiger2">
            <key column="t_animal_id"></key>
            <property name="eatSheep"></property>
        </joined-subclass>
         
         
        
    </class>
</hibernate-mapping>

一個(gè)映射文件钉蒲,存儲(chǔ)所有的子類; 子類父類都對(duì)應(yīng)表彻坛;
缺點(diǎn):表結(jié)構(gòu)比較負(fù)責(zé)顷啼,插入一條子類信息,需要用2條sql: 往父類插入昌屉、往子類插入钙蒙!

  • 方式四: 每個(gè)子類映射一張表, 父類不對(duì)應(yīng)表(推薦使用)

關(guān)鍵節(jié)點(diǎn):union-class,同時(shí)主鍵不能使用自增長的策略

Animal.hbm.xml

<?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">

<!-- 繼承映射,每一個(gè)類都對(duì)應(yīng)一張表,父類不對(duì)應(yīng)表 -->
<hibernate-mapping package="com.mrq.extends4">
<!-- 
    abstract="true"指定實(shí)體類不對(duì)應(yīng)表,數(shù)據(jù)庫不會(huì)生成對(duì)應(yīng)的表
 -->
    <class name="Animal" abstract="true">
        <!-- 如果使用union-subclass節(jié)點(diǎn),主鍵生成策略不能自增長 -->
        <id name="id">
            <generator class="uuid"></generator>
        </id>
        
        
        <property name="name" type="string"></property>
        <!-- 
            子類貓:
            union-subclass
            table 對(duì)應(yīng)的表,表的主鍵為ID列
            
         -->
         <union-subclass name="Cat" table="t_cat4">
            <property name="catchMouse"></property>
         </union-subclass>
         <!-- 子類:老虎 -->
        <union-subclass name="Tiger" table="t_tiger4">
            <property name="eatSheep"></property>
        </union-subclass>
         
         
        
    </class>
</hibernate-mapping>

所有的子類都寫到一個(gè)映射文件间驮;
父類不對(duì)應(yīng)表躬厌; 每個(gè)子類對(duì)應(yīng)一張表

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市竞帽,隨后出現(xiàn)的幾起案子扛施,更是在濱河造成了極大的恐慌,老刑警劉巖屹篓,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疙渣,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡堆巧,警方通過查閱死者的電腦和手機(jī)妄荔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恳邀,“玉大人懦冰,你說我怎么就攤上這事灶轰∫シ校” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵笋颤,是天一觀的道長乳附。 經(jīng)常有香客問我内地,道長,這世上最難降的妖魔是什么赋除? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任阱缓,我火速辦了婚禮,結(jié)果婚禮上举农,老公的妹妹穿的比我還像新娘荆针。我一直安慰自己,他們只是感情好颁糟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布航背。 她就那樣靜靜地躺著,像睡著了一般棱貌。 火紅的嫁衣襯著肌膚如雪玖媚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天婚脱,我揣著相機(jī)與錄音今魔,去河邊找鬼。 笑死障贸,一個(gè)胖子當(dāng)著我的面吹牛错森,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播篮洁,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼问词,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了嘀粱?” 一聲冷哼從身側(cè)響起激挪,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锋叨,沒想到半個(gè)月后垄分,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娃磺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年薄湿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偷卧。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豺瘤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出听诸,到底是詐尸還是另有隱情坐求,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布晌梨,位于F島的核電站桥嗤,受9級(jí)特大地震影響须妻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泛领,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一荒吏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渊鞋,春花似錦绰更、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至员辩,卻和暖如春盒粮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奠滑。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工丹皱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宋税。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓摊崭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杰赛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呢簸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 對(duì)象的狀態(tài) Hibernate中對(duì)象的狀態(tài) : 臨時(shí)/瞬時(shí)狀態(tài)、持久化狀態(tài)乏屯、游離狀態(tài)臨時(shí)狀態(tài)特點(diǎn):直接new出來的...
    奮斗的老王閱讀 916評(píng)論 0 49
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法根时,類相關(guān)的語法,內(nèi)部類的語法辰晕,繼承相關(guān)的語法蛤迎,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理含友,服務(wù)發(fā)現(xiàn)替裆,斷路器,智...
    卡卡羅2017閱讀 134,600評(píng)論 18 139
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,793評(píng)論 0 11
  • 感恩早起鍛煉遇見了一個(gè)5歲的小女孩窘问,她特別開心的跟我分享了她的生活辆童,跟她聊天我也特別開心,感恩她讓我種下了關(guān)心和陪...
    rainlove2011閱讀 158評(píng)論 0 0