Hibernate 知識點梳理


一、Hibernate簡介(網上搜的,理解性地看看)

  1.概念Hibernate是持久層(數據訪問層)的框架,對JDBC進行了封裝,是對數據庫訪問提出的面向對象的解決方案。

 

  2.作用使用Hibernate可以直接訪問對象,Hibernate自動將訪問轉換成SQL執行,從而實現簡介訪問數據庫的目的,簡化了數據訪問層的代碼開發。

 

  3.JDBC、MyBatis對比:

    a)JDBC需要編寫大量SQL語句,以及對大量參數賦值。需要手動將ResultSet結果集轉換成實體對象;SQL中包含特有函數,無法移植。而Hibernate可以自動生成SQL和參數賦值,自動將ResultSet結果集轉換成實體對象,采用一致的方法對數據庫操作,移植性好。

    b)MyBatis與Hibernate都對JDBC進行了封裝,采用ORM思想解決了Entity和數據庫的映射問題。MyBatis采用SQL與Entity映射,對JDBC封裝程度比較輕,需要自己寫SQL,更具有靈活性;而Hibernate采用數據庫與Entity映射,對JDBC封裝程度較重,自動生成SQL,對於基本的操作,開發效率高。

 

  4.原理:Hibernate框架是ORM思想的一種實現,解決了對象和數據庫映射問題。我們可以通過Hibernate提供的一系列API,允許我們直接訪問實體對象,然后其根據ORM映射關系,轉換成SQL並且執行,從而達到訪問數據庫的目的。

    ORM:Object Relation Mapping,即對象關系映射,指Java對象和關系數據庫之間的映射。

    ORM思想:將對象與數據庫數據進行相互轉換的思想,不同的框架實現ORM的手段不同,但更多的是采用配置+反射的方式來實現ORM。

 

  5.框架體系結構

    a)主配置文件,通常為“hibernate.cfg.xml”,用於配置數據庫連接參數,框架參數,已經映射關系文件。

    b)實體類,與數據庫對應的Java類型,用於封裝數據庫記錄的對象類型。

    c)映射關系文件,通常為“實體類.hbm.xml”,並放置在與實體類相同的路徑下。該文件是指定實體類和數據庫的對應關系,以及類中屬性和表中字段之間的對應關系。

    d)底層API,對映射關系文件的解析,根據解析出來的內容,動態生成SQL語句,自動將屬性和字段映射。

 

 

二、Hibernate使用

  1.常用API

    Configuration:負責加載主配置文件信息,同時加載映射關系文件信息

/**
到src下找到名稱為hibernate.cfg.xml的配置文件,創建對象,把配置文件放到對象中(加載核心配置文件)
*/
Configuration cfg = new Configuration().configure();

//加載指定的核心配置文件
Configuration cfg = new Configuration().configure("com/konrad/hibernate.cfg.xml");

//加載指定的映射配置文件
cfg.addResource("com/konrad/entity/User.hbm.xml");

 

 

 

 

    SessionFactory:負責創建Session對象,根據核心配置文件的配置,在數據庫創建對應的表,一個項目只應有一個此對象

private static final SessionFactory sessionFactory;
    
    static{
        try{
            //配置文件的方式
            sessionFactory = new Configuration().configure("hibernate.cfg.xml")  
                    .buildSessionFactory();  
            /* 注解的方式
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();   
            */
        }catch (Throwable ex){
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }    

 

 

 

    Session:數據庫連接會話,負責執行增刪改操作

    Transaction:負責事務控制

    Query:負責執行特殊查詢

 

  2.使用步驟

    a)導入Hibernate包,以及數據庫驅動包。(開發需要的包不知道的自行百度,也可以通過Maven構建)

    b)引入Hibernate主配置文件hibernate.cfg.xml

       - <session-factory>標簽要寫在<hibernate-configuration>標簽內部

    c)創建實體類

    d)創建映射關系文件(也可以通過注解的方式進行映射,這樣就不需要xml映射文件)

      - <class>標簽中的name屬性寫的是類的全路徑

       - <id>和<property>標簽中的name屬性寫的是實體類中的屬性名稱

       - <id>和<property>標簽中,column可以省略,若省略就是以name屬性值生成表的字段名

         - <property>標簽中還有一個type屬性,用於生成表的字段的類型,但是使用比較少

    e)使用Hibernate API執行增刪改查等操作

      f)額外說明:注解方式的應用

     - 如果實體類屬性名與表字段名稱不同時,要么都注解在屬性前,要么都注解在get方法前。

      - 如果實體類屬性名和表字段名稱統一,可以部分注解在屬性前,部分注解在get方法前。

      - 若都不注解,則默認表字段名和屬性名一致

      - 若實體類中某個屬性不需要存進數據庫表,使用@Transient進行注解即可

      - 表名稱可以在實體類前進行注解

      - 所有注解都在javax.persistence包下

  3.映射類型

    a)Java類型:映射關系文件中,配置屬性和字段關系時,可以在type屬性上指定Java類型,用於做Java屬性和數據庫字段的轉換。指定時需要完整的類型名,如java.lang.String。

    b)自定義類型:當某些特殊類型,Java預置類型無法支持,需要自定義一個類來實現,這個類要求實現接口UserType。比如boolean類型,數據庫中一般存char(1),存y/n或者t/f,Java預置類型無法支持boolean類型的配置,需要自定義。

    c)Hibernate也提供了一些類型來支持這些映射,提供了7中映射類型,書寫時全是小寫

      

 

三、Hibernate的主鍵生成方式

   1.sequence:采用序列生成主鍵,適用於Oracle數據庫。

<generator class="sequence">
    <param name="sequence">序列名</param>
</generator>

 

  2.identity:采用數據庫自增長機制生成主鍵,適用於Oracle之外的其他數據庫。

<generator class="identity">
</generator>

 

  3.native:根據當前配置的數據庫方言,自動選擇sequence或者identity。

<generator class="native">
    <param name="sequence">序列名</param>
</generator>

 

  4.increment:不是采用數據庫自身的機制來生成主鍵,而是Hibernate提供的一種生成主鍵的方式,它會獲取當前表中主鍵的最大值,然后加1作為新的主鍵。PS:這種方式在並發量高時存在問題,可能會產生重復的主鍵,因此不推薦。

<generator class="increment">
</generator>

 

  5.assigned:Hibernate不負責生成主鍵,需要程序員自己處理主鍵的生成。

<generator class="assigned">
</generator>

 

  6.uuid/hilo:采用uuid或hilo算法生成一個主鍵值,這個主鍵值是一個不規則的長數字。PS:這個方式生成的主鍵可以保證不重復,但是沒有規律,因此不能按主鍵排序

<generator class="uuid">
</generator>

 

 

 

 

四、一些使用的代碼實例

  1.核心配置文件

  

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

<hibernate-configuration>  
    <session-factory>  
        <!-- 配置數據庫信息 必須的 -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>  
        <property name="connection.url">jdbc:mysql://localhost:3306/test</property>  
        <property name="connection.username">root</property>  
        <property name="connection.password"></property>  
        <property name="javax.persistence.validation.mode">none</property>   
        <!-- 支持mysql方言 -->
        <property name="dialect">org.hibernate.dialect.SQLServerDialect</property>  
          
        <property name="current_session_context_class">thread</property>  
  
        <!-- 配置是否在控制台顯示sql語句 -->
        <property name="show_sql">true</property>  
        <!-- 配置是否按照格式顯示sql語句 -->
        <property name="format_sql">true</property>  
        <!-- 配置生成策略
        update:如果沒有表創建之,有表就更新表
     -->
        <property name="hbm2ddl.auto">update</property>  
        
        <!-- 引入映射配置文件 必須的 -->
    <mapping resource="com/maven/test/hibernate/entity/PersonEntity.hbm.xml"/>
    <!-- 若使用注解的方式,需要如下配置 -->
<!-- <mapping class="com.maven.test.hibernate.entity.PersonEntity">-->

</session-factory> </hibernate-configuration>

 

  2.關系映射XML文件dtd約束:

<!DOCTYPE hibernate-configuration PUBLIC  
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

 

  3.HibernateUtil類:提供獲取Session和關閉Session的方法。Hibernate中我們使用ThreadLocal管理Session。 

package com.maven.test.hibernate.util;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    public static final ThreadLocal<Session> SESSIONMAP = new ThreadLocal<Session>(); 
    private static final SessionFactory sessionFactory;
    private static final Logger LOGGER = Logger.getLogger(HibernateUtil.class);
    
    static{
        try{
            LOGGER.debug("HibernateUtil.static - loading config");
            sessionFactory = new Configuration().configure("hibernate.cfg.xml")  
                    .buildSessionFactory();  
            LOGGER.debug("HibernateUtil.static - end");
        }catch (Throwable ex){
            ex.printStackTrace();
            LOGGER.error("HibernateUtil error: ExceptionInInitializerError");
            throw new ExceptionInInitializerError(ex);
        }
    }
    
    private HibernateUtil(){}
    
    public static Session getSession() throws HibernateException{
        Session session  = SESSIONMAP.get();
        if(session == null){
            session = sessionFactory.openSession();
            SESSIONMAP.set(session);
        }
        return session;
    }
    
    
    public static void closeSession() throws HibernateException{
        Session session = SESSIONMAP.get();
        SESSIONMAP.set(null);
        
        if(session != null)
            session.close();
    }
    
}

 

  4.添加操作

PersonEntity person = new PersonEntity();
person.setId(100);
person.setName("Konrad");        
        
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
        
session.save(person);
        
tx.commit();
HibernateUtil.closeSession();

 

  5.刪除操作

Session session = HibernateUtil.getSession();
session.beginTransaction();

PersonEntity person = session.get(PersonEntity,class, 1);
session.delete(person);

session.getTransaction().commit();
HibernateUtil.closeSession();

 

  6.查詢操作

Session session = HibernateUtil.getSession();
session.beginTransaction();

//單行查詢,根據主鍵查詢
PersonEntity p = session.get(PersonEntity.class, 1)

//多行查詢    
@SuppressWarnings("unchecked")
List<PersonEntity> personList = session.createQuery("select p from PersonEntity p").list();
        
for(PersonEntity person : personList){
     System.out.println(person);
}

session.getTransaction().commit();
HibernateUtil.closeSession();

 

  7.修改操作

Session session = HibernateUtil.getSession();
session.beginTransaction();
        
PersonEntity person = session.get(PersonEntity.class, 1);
person.setName("haha");
session.update(person);

session.getTransaction().commit();
HibernateUtil.closeSession();

 

 

 

 

五、Hibernate進階

  1.一級緩存

    a)Hibernate創建每個Session對象時,都會給該Session對象分配一塊獨立的緩存區,用於存放該Session查詢出來的對象,這個分配給Session的緩存區稱之為一級緩存,也叫Session級緩存。Session的save、update、delete操作會觸發緩存更新。

    b)使用一級緩存的原因是,Session取數據時,會優先向緩存區取數據,如果存在數據則直接返回,不存在才會去數據庫查詢,從而降低了數據庫訪問次數,提升代碼性能。

    c)一級緩存是默認開啟的,使用Hibernate API查詢時會自動使用。

    d)session.evict(obj) - 將obj從一級緩存中移除

       session.clear() - 清除一級緩存中所有的obj

       session.close() - 關閉session,釋放緩存空間  

 

  2.Hibernate中,實體對象的3中狀態:臨時態、持久態、游離態

    a)臨時態:臨時態的對象可以被垃圾回收,未進行持久化,未與session關聯

     - 通過new創建的對象為臨時態

      - 通過delete方法操作的對象將轉變為臨時態

 

    b)持久態:持久態對象垃圾回收器不能回收,進行了持久化,與session關聯。實際上持久態對象存在於session緩存中,由session負責管理;持久態對象的數據可以自動更新到數據庫中,在調用session.flush()時執行,而提交事務時會使用session.flush(),因此提交事務也會觸發同步

     - 通過get、load、list、iterate方法查詢到的對象為持久態

      - 通過save、update方法操作的對象轉變為持久態

 

    c)游離態:游離態的對象可以被垃圾回收,進行過持久化,但已與session解除了關聯

      - 通過session的evict、clear、close方法操作的對象會轉變為游離態

 

      

 

  3.延遲加載

    a)使用某些Hibernate方法查詢數據時,Hibernate返回的只是一個空對象(除id外屬性都為null),並沒有真正查詢數據庫。而在使用這個對象時才會觸發查詢數據庫,並將查詢到的數據注入到這個空對象中,這種將查詢時機制推遲到對象訪問時的機制稱之為延遲加載。  

    b)延遲加載可以提升內存資源的使用率,降低對數據庫的訪問次數。

    c)session.load()、query.iterate()、關聯映射中對關聯屬性的加載,以上屬於采用延遲加載的方法。

    d)采用具有延遲加載機制的操作,需要避免session提前關閉

 

  4.關聯映射

    a)關聯映射即使用Hibernate操作一張表時,它可以通過配置關系自動地幫助我們操作另一張表

    b)關聯查詢出關系表的數據、關聯新增/修改關系表的數據、關聯刪除關系表的數據

    c)類型:一對多關聯、多對一關聯、多對多關聯、一對一關聯、繼承關聯

     一對多關聯實現步驟:

      - 在“一”方實體類添加集合屬性,以及get、set方法

      - 在“一”方hbm文件中配置關聯關系

<!-- 
set指定屬性類型為Set集合
name指定屬性名
-->
<set name="courses">
    <!-- column指定了關聯字段名-->
    <key column="stu_id"/>
    <!-- one-to-many指定了關聯關系,class指定了另一方類型-->
    <one-to-many class="com.konrad.entity.Course" />
</set>

 

     關聯實現步驟:

      - 在“多”方實體類添加屬性,以及get、set方法

      - 在“多”方hbm文件中配置關聯關系

 <!-- many-to-one指定了關聯關系,
name指定了屬性名,
column指定了關系字段,
class指定了另一方類型-->
<many-to-one name="student" column="stu_id" class="com.konrad.entity.Student" />

     多關聯實現步驟

      - 在雙方方實體類添加集合屬性,以及get、set方法

      - 在雙方hbm文件中配置關聯關系

Student映射配置文件
<!--
配置學生對應的教師集合 name屬性:配置教師實體類中的Set集合屬性的名稱 table屬性:表示中間表的名稱 --> <set name="teachers" table="t_stu_tea"> <!-- column屬性:配置當前映射文件在中間表中外鍵名稱 --> <key column="sid"></key> <!-- class屬性:配置另一個張表的實體類全路徑名稱 colunm屬性:配置一張表在中間表中的外鍵名稱 --> <many-to-many class="cn.konrad.entity.Teacher" column="tid"></many-to-many> </set>

 

 

 

 

Teacher映射配置文件
<!-- 
            配置教師對應的學生集合
            name屬性:配置學生實體類中的Set集合屬性的名稱
            table屬性:中間表的名稱
         -->
        <set name="students" table="t_stu_tea">
            <!-- column屬性:配置當前映射文件在中間表中外鍵名稱    -->
            <key column="tid"></key>
            <!-- 
                class屬性:配置另一個張表的實體類全路徑名稱
                colunm屬性:配置一張表在中間表中的外鍵名稱
             -->
            <many-to-many class="cn.konrad.entity.Student" column="sid"></many-to-many>
        </set>

 

  對一關聯實現步驟:

      - 在雙方方實體類添加屬性,以及get、set方法

 

      - 在雙方hbm文件中配置關聯關系

Wife映射關系文件
<!--
property-ref屬性:指定使用被關聯實體主鍵以外的字段作為關聯字段 --> <one-to-one name="husband" class="cn.konrad.entity.Husband" property-ref="wife"></one-to-one>

 

 

 

 

 

Husband映射配置文件
<id name="hid" column="hid">
            <!-- 2.1配置主鍵的策略 
                foreign屬性:表示主鍵參照外鍵生成
            -->
            <generator class="foreign">
                <!-- param標簽配置當前實體類中引用的對方實體對象的引用名 -->
                <param name="property">wife</param>
            </generator>
        </id>
        <!-- 3.配置實體類與表的其他屬性 
            name屬性:實體類的屬性名稱
            column屬性:表中的列名
           Contrained=true表示生產外鍵
        -->
        <property name="hname" column="hname"></property>
        <one-to-one name="wife" class="cn.konrad.entity.Wife" constrained="true"></one-to-one>

 

 

 

  5.關聯操作

    a)默認情況下,關聯屬性時采用延遲加載機制加載的,可以通過映射關系文件中關聯屬性配置標簽中的lazy屬性進行修改,true/false。

    b)通過一個連接查詢一次性取出2張表的數據,避免2次查詢。在關聯屬性標簽上通過fetch屬性進行設置,稱之為抓取策略。“join”表示查詢時使用連接查詢,一起把對方數據抓取過來;“select”表示查詢時不使用連接查詢,是默認情況。當fetch為“join”時,關聯屬性的延遲加載失效。

 

  6.級聯操作:通過關聯映射,在對一方進行增刪改時,連帶增刪改關聯的另一方數據

    a)實現級聯添加/修改,需要在映射關系文件中的關聯屬性標簽中,通過cascade屬性進行設置,cascade="save-update"

    b)實現級聯刪除,cascade="delete"

    c)若想級聯添加、修改、刪除一起支持,cascade="all"

    d)控制反轉,在一對多關聯中,使用級聯新增、刪除時,當前操作的“一”方會試圖維護關聯字段,然而關聯字段是在“多”方對象中,它會自動維護這個字段,因此“一”方沒必要做這樣的處理。在關聯屬性標簽上通過inverse屬性(true/false)交出控制權,默認是false,不控制反轉。

 

  7.Hibernate查詢

    a)HQL按條件查詢:條件中寫的是屬性名,在執行查詢前調用query對象為條件參數賦值

String hql = "from Course where name=?";
Session session = HibernateUtil.getSession();
Query query = session.createQuery(hql);
query.setString("math");
List<Course> courses = query.list();

 

    b)HQL查詢部分字段:可以只查詢表中的一部分字段,需要在from之前追加select語句,指定要查詢列對應的屬性名

String hql = "select id,name" + " from Course";

    注意:查詢部分字段時,query.list()方法返回的集合中封裝的不是實體對象,而是一個Object[],數組中的值與select語句后面的屬性按順序對應。

 

    c)分頁查詢:通過API統一實現

int from = (page - 1) * pageSize;
query.setFirstResult(from);
query.setMaxResults(pageSize);

    注意:查詢的起點是本頁第一行,按照JDBC計算,公式為(page -1) * pageSize +1;Hibernate中行數的起點是0,不同於JDBC是從1開始,所以要在上面公式的基礎上-1,即(page -1) * pageSize

 

    d)查詢總頁數:根據以下hql查詢總行數,再計算總頁數

String hql = "select count(*) from Student";

  

    e)多表聯合查詢:可以使用HQL進行多表聯合查詢,不過HQL中寫的是關聯的對象的屬性名;有3中關聯查詢的方式:

    對象方式關聯

String hql = "select s.id, s.name, c.name from Student s, Course c " +
    "where s.course.id = c.id";

    join方式關聯(不能直接join對象,需要join關聯屬性)

String hql = "select s.id, s.name, c.name from Student s inner join s.courses c";

    select子句關聯

 

    f)直接使用SQL查詢

String sql = "select * from t_course where c_name=?";
Session session = HibernateUtil.getSession();
SQLQuery query = session.createSQLQuery(sql);
query.setString(0,"math");
List<Object[]> list = query.list(); //返回集合封裝的是Object[]

//若想返回集合中封裝實體對象
query.addEntity(Course.class);
List<Course> list = query.list();

 

    g)使用Criteria查詢

Criteria c = session.createCriteria(Course.class);
c.add(Restriction.eq("name", "math")).add(Restrictions.or(Restrictions.eq(), Restrications.eq()));
List<Course> list = c.list();

  

 

  8.二級緩存

    a)二級緩存類似於一級緩存,可以緩存對象,但它是SessionFactory級別的緩存,有SessionFactory負責管理。因此二級緩存的數據是Session間共享的,不同的Session對象都可以共享二級緩存中的數據。

    b)二級緩存適用於:對象數據頻繁共享,數據變化頻率低

    c)二級緩存使用步驟:

      導入ehcache.jar

      在src下添加緩存配置文件ehcache.xml

<ehcache>
<!--緩存到硬盤時的緩存路徑,java.io.tmpdir表示系統默認緩存路徑-->
<diskStore path="java.io.tmpdir"/>
<!--默認緩存配置
maxElementsInMemory:二級緩存可容納最大對象數
eternal:是否保持二級緩存中對象不變
timeToIdleSeconds:允許對象空閑的時間,即對象最后一次訪問起,超過該時間即失效
timeToLiveSeconds:允許對象存活的時間,即對象創建起,超過該時間即失效
overflowToDisk:內存不足,是否允許使用硬盤緩存,寫入路徑參考diskStore
-->

<defaultCache>
    maxElementsInMemory = "300"
    eternal = "false"
    timeToIdleSeconds = "120"
    timeToLiveSeconds = "300"
    overflowToDisk = "true" />
  

<!--自定義配置-->
<cache name="myCache">
    maxElementsInMemory = "2000"
    eternal = "false"
    timeToIdleSeconds = "200"
  timeToLiveSeconds = "300"
    overflowToDisk = "true" />
</ehcache>

      在hibernate.cfg.xml中開啟二級緩存,指定采用的二級緩存驅動類

<property name="hibernate.cache.use_second_level_cache">true</property>

<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>

      在要緩存的對象對應的映射關系文件中,開啟當前對象的二級緩存支持,並指定緩存策略

<!--開啟二級緩存,並指定緩存策略
可以用region屬性指定自定義的緩存設置-->
<cache usage="read-only" />

  

   d)緩存策略:

      -只讀型(read-only):緩存不會更新,適用於不會發生改變的數據,效率最高,事務隔離級別最低

      -讀寫型(read-write):緩存會在數據變化時更新,適用於變化的數據

      -不嚴格讀寫型(nonstrict-read-write):緩存不定期更新,適用於變化頻率低的數據

      -事務型(transactional):緩存會在數據變化時更新,並且支持事務。效率最低,事務隔離界別最高。

<!-- 配置事務的隔離級別
        1 -- Read uncommitted isolation
        2 -- Read committed isolation
        3 -- Repeatable read isolation
        4 -- Serializable isolation
     -->
<property name="hibernate.connection.isolation">2</property>

     

  9.查詢緩存

    a)查詢緩存依賴於二級緩存,可以理解為特殊的二級緩存,也是SessionFactory級別的,也是由SessionFactory負責維護

    b)查詢緩存可以緩存任何查詢到的結果

    c)查詢緩存是以hql為key,緩存該hql查詢到的整個結果。如果執行2次同樣的hql,第二次執行可以從查詢緩存中取到第一次查詢緩存的內容

    d)使用查詢緩存步驟:

      開啟二級緩存

      在hibernate.cfg.xml中,開啟查詢緩存

<!--開啟查詢緩存-->
<property name="hibernate.cache.use_query_cache">true</property>

      在查詢代碼執行前,指定開啟查詢緩存

query.setCacheable(true); //開啟查詢緩存

 

 

 

    

 

整理到此為止,有不足的歡迎指正討論。。。☺


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM