超全面 hibernate 復習總結筆記

如有轉載,請申明:
轉載自 IT天宇http://www.lxweimin.com/p/50964e92c5fb

前言

博客正式轉到簡書,實在忍受不了 CSDN 的模版,有廣告或界面難看還可以忍,但有些模版還有 bug,作為程序員忍無可忍,修個 bug 真的有那么難嗎,這么多年了。

接著上篇,先說個段子。
三四年前如果有人問我 Android/Ios 如何入門,我可能會推薦上百 G 的資料,但如果現在問我,我只會推薦一本書《app開發從上架到上吊》


你可能覺得我危言聳聽,但今年的移動開發行情真的很差,看到很多幾年經驗的一個月才收到幾個面試通知,沒有經驗的就更絕望了。

好吧,今天不是來討論行情的。
前段時間寫了一篇 struts2 的筆記,有好心的老司機告訴我,struts2 已經被拋棄了。但話是這么說,面試的時候,難免碰到問 struts2,如果這個時候表現得一臉懵逼,那估計 offer 就沒有了。所以雖然現在 hibernate 用得不多了,但還是得復習一下。

目錄

  1. 環境搭建
  2. 實體類映射
  3. 核心配置詳解
  4. 一級緩存
  5. 關系映射
  6. 抓取策略
  7. HQL
  8. QBC
  9. 其他配置
  10. 事務
  11. 二級緩存

正文

<a id="1"></a>

1.環境搭建

導包

根據需要選擇手動導入 jar 包,或者用依賴管理工具。

此外,為了連接數據庫,還需要數據庫的驅動包。

配置

核心配置文件以 hibernate.cfg.xml 命名,放在類路徑下(Idea 放在 resources 下)

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!-- SessionFactory,相當于之前學習連接池配置 -->
    <session-factory>
        <!-- 1 基本4項 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///db01</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">1234</property>
    
    </session-factory>

</hibernate-configuration>

<a id="2"> </a>

2.實體類映射

數據庫

create database db01;
use db01;

實體類

public class User {
    private Integer uid;
    private String username;
    private String password;
// 省略 get set 方法
}

映射實體類

可以用 xml 或者 注解來映射

xml

放在實體類同目錄下,名字和實體類相同,擴展名為 .hbm.xml
例如: ser.hbm.xml

<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.ittianyu.a_hello.User" table="t_user">
        <!-- 主鍵 -->
        <id name="uid">
            <generator class="native"></generator>
        </id>
        
        <!-- 普通屬性 -->
        <property name="username"></property>
        <property name="password"></property>
    
    </class>
</hibernate-mapping>

核心配置文件中加上映射文件位置

<mapping resource="com/ittianyu/hibernate/helloworld/User.hbm.xml"/>

注解

// name 對應表名稱
@Entity
@Table(name = "t_user")
public class User {
    // 主鍵
    @Id
    @GeneratedValue
    private Integer uid;
    private String username;
    private String password;
// 省略 get set 方法
}

在核心配置文件中加上映射文件位置

<mapping class="com.ittianyu.hibernate.helloworld.User" />

測試

public class HelloWorld {
    @Test
    public void hello() {
        // username, password
        User user = new User("123456", "123");

        // 1.加載配置文件
        Configuration configure = new Configuration().configure();
        // 2.獲得session factory對象
        SessionFactory sessionFactory = configure.buildSessionFactory();
        // 3.創建session
        Session session = sessionFactory.openSession();
        // 4.開啟事務
        Transaction transaction = session.beginTransaction();
        // 5.保存并提交事務
        session.save(user);
        transaction.commit();
        // 6.釋放資源
        session.close();
        sessionFactory.close();
    }
}

<a id="3"> </a>

3.核心配置詳解

核心 api

Configuration

常用方法

  • 構造方法:默認加載 hibernate.properties
  • configure() 方法:默認加載 hibernate.cfg.xml
  • configure(String) 方法:加載指定配置文件

手動添加映射

// 手動加載指定的配置文件
config.addResource("com/ittianyu/a_hello/User.hbm.xml");

// 手動加載指定類,對應的映射文件 User--> User.hbm.xml
config.addClass(User.class);

SessionFactory

  • SessionFactory 相當于java web連接池,用于管理所有session
  • 獲得方式:config.buildSessionFactory();
  • sessionFactory hibernate緩存配置信息 (數據庫配置信息、映射文件,預定義HQL語句 等)
  • SessionFactory線程安全,可以是成員變量,多個線程同時訪問時,不會出現線程并發訪問問題
  • 開啟一個 session:factory.openSession();
  • 獲取和當前線程綁定的會話(需要配置):factory.getCurrentSession();
    <property name="hibernate.current_session_context_class">thread</property>
    

Session

  • Session 相當于 JDBC的 Connection -- 會話
  • 通過session操作PO對象 --增刪改查
  • session單線程,線程不安全,不能編寫成成員變量。
  • Api:
    save 保存
    update 更新
    delete 刪除
    get 通過id查詢,如果沒有 null
    load 通過id查詢,如果沒有拋異常
    createQuery("hql")  獲得Query對象
    createCriteria(Class) 獲得Criteria對象
    

Transaction

開啟事務 beginTransaction()
獲得事務 getTransaction()

提交事務:commit()
回滾事務:rollback()

和 spring 整合后,無需手動管理

Query

  • hibernate執行hql語句
  • hql語句:hibernate提供面向對象查詢語句,使用對象(類)和屬性進行查詢。區分大小寫。
  • 獲得 session.createQuery("hql")
  • 方法:
    list()  查詢所有
    uniqueResult() 獲得一個結果。如果沒有查詢到返回null,如果查詢多條拋異常。
    
    setFirstResult(int) 分頁,開始索引數startIndex
    setMaxResults(int) 分頁,每頁顯示個數 pageSize
    

Criteria

  • QBC(query by criteria),hibernate提供純面向對象查詢語言,提供直接使用PO對象進行操作。
  • 獲得方式:Criteria criteria = session.createCriteria(User.class);
  • 條件
    criteria.add(Restrictions.eq("username", "tom"));
    Restrictions.gt(propertyName, value)        大于
    Restrictions.ge(propertyName, value)    大于等于
    Restrictions.lt(propertyName, value)    小于
    Restrictions.le(propertyName, value)    小于等于
    Restrictions.like(propertyName, value)  模糊查詢,注意:模糊查詢值需要使用 % _
    

工具類

public class HibernateUtils {
    private static SessionFactory sessionFactory;
    static {
        Configuration configuration = new Configuration().configure();
        sessionFactory = configuration.buildSessionFactory();

        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                sessionFactory.close();
            }
        });
    }

    public static Session openSession() {
        return sessionFactory.openSession();
    }

    public static Session getCurrentSession() {
        return sessionFactory.getCurrentSession();
    }

    public static void main(String[] args) {
        Session session = openSession();
        System.out.println(session);
        session.close();

    }
}

核心配置

基本配置

<!-- SessionFactory,相當于之前學習連接池配置 -->
<session-factory>
    <!-- 1 基本4項 -->
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql:///h_day01_db</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">1234</property>

    <!-- 2 與本地線程綁定 -->
    <property name="hibernate.current_session_context_class">thread</property>

        <!-- 3 方言:為不同的數據庫,不同的版本,生成sql語句(DQL查詢語句)提供依據 
            * mysql 字符串 varchar
            * orcale 字符串 varchar2
        -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
    
    <!-- 4 sql語句 -->
    <!-- 顯示sql語句 -->
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.format_sql">true</property>

    <!-- 5 自動創建表(了解) ,學習中使用,開發不使用的。
        * 開發中DBA 先創建表,之后根據表生產 PO類
        * 取值:
        update:【】
            如果表不存在,將創建表。
            如果表已經存在,通過hbm映射文件更新表(添加)。(映射文件必須是數據庫對應)
                表中的列可以多,不負責刪除。
        create :如果表存在,先刪除,再創建。程序結束時,之前創建的表不刪除。【】
        create-drop:與create幾乎一樣。如果factory.close()執行,將在JVM關閉同時,將創建的表刪除了。(測試)
        validate:校驗 hbm映射文件 和 表的列是否對應,如果對應正常執行,如果不對應拋出異常。(測試)
    -->
    <property name="hibernate.hbm2ddl.auto">create</property>
    
    <!-- 6 java web 6.0 存放一個問題
        * BeanFactory 空指針異常
            異常提示:org.hibernate.HibernateException: Unable to get the default Bean Validation factory
        * 解決方案:取消bean校驗
    -->
    <property name="javax.persistence.validation.mode">none</property>

    <!-- 添加映射文件 
        <mapping >添加映射文件
            resource 設置 xml配置文件 (addResource(xml))
            class 配置類 (addClass(User.class)) 配置的是全限定類名
    -->
    <mapping  resource="com/ittianyu/a_hello/User.hbm.xml"/>
</session-factory>

主鍵種類

  • 自然主鍵: 在業務中,某個屬性符合主鍵的三個要求.那么該屬性可以作為主鍵列.
  • 代理主鍵: 在業務中,不存符合以上3個條件的屬性,那么就增加一個沒有意義的列.作為主鍵.

類型對應

Java數據類型 Hibernate數據類型 標準SQL數據類型(不同DB有差異)
byte、java.lang.Byte byte TINYINT
short、java.lang.Short short SMALLINT
int、java.lang.Integer integer INGEGER
long、java.lang.Long long BIGINT
float、java.lang.Float float FLOAT
double、java.lang.Double double DOUBLE
java.math.BigDecimal big_decimal NUMERIC
char、java.lang.Character character CHAR(1)
boolean、java.lang.Boolean boolean BIT
java.lang.String string VARCHAR
boolean、java.lang.Boolean yes_no CHAR(1)('Y'或'N')
boolean、java.lang.Boolean true_false CHAR(1)('Y'或'N')
java.util.Date、java.sql.Date date DATE
java.util.Date、java.sql.Time time TIME
java.util.Date、java.sql.Timestamp timestamp TIMESTAMP
java.util.Calendar calendar TIMESTAMP
java.util.Calendar calendar_date DATE
byte[] binary VARBINARY、BLOB
java.lang.String text CLOB
java.io.Serializable serializable VARBINARY、BLOB
java.sql.Clob clob CLOB
java.sql.Blob blob BLOB
java.lang.Class class VARCHAR
java.util.Locale locale VARCHAR
java.util.TimeZone timezone VARCHAR
java.util.Currency currency VARCHAR

普通屬性

<hibernate-mapping> 
            package 用于配置PO類所在包
                例如: package="com.ittianyu.d_hbm"
        <class> 配置 PO類 和 表 之間對應關系
            name:PO類全限定類名
                例如:name="com.ittianyu.d_hbm.Person"
                如果配置 package,name的取值可以是簡單類名 name="Person"
            table : 數據庫對應的表名
            dynamic-insert="false" 是否支持動態生成insert語句
            dynamic-update="false" 是否支持動態生成update語句
                如果設置true,hibernate底層將判斷提供數據是否為null,如果為null,insert或update語句將沒有此項。
        普通字段
            <property>
                name : PO類的屬性
                column : 表中的列名,默認name的值相同
                type:表中列的類型。默認hibernate自己通過getter獲得類型,一般情況不用設置
                    取值1: hibernate類型
                        string 字符串
                        integer 整形
                    取值2: java類型 (全限定類名)
                        java.lang.String 字符串
                    取值3:數據庫類型
                        varchar(長度) 字符串
                        int 整形
                        <property name="birthday">
                            <column name="birthday" sql-type="datetime"></column>
                        </property>
                        javabean 一般使用類型 java.util.Date
                        jdbc規范提供3中
                            java類型              mysql類型
                            java.sql.Date       date
                            java.sql.time       time
                            java.sql.timestamp  timestamp
                            null                datetime
                            
                            以上三個類型都是java.util.Date子類
                            
                length : 列的長度。默認值:255
                not-null : 是否為null
                unique : 是否唯一
                access:設置映射使用PO類屬性或字段
                    property : 使用PO類屬性,必須提供setter、getter方法
                    field : 使用PO類字段,一般很少使用。
                insert 生成insert語句時,是否使用當前字段。
                update 生成update語句時,是否使用當前字段。
                    默認情況:hibernate生成insert或update語句,使用配置文件所有項
        注意:配置文件如果使用關鍵字,列名必須使用重音符    

主鍵

<id>配置主鍵
name:屬性名稱
access="" 設置使用屬性還是字段
column=""  表的列名
length=""  長度
type="" 類型
<generator> class屬性用于設置主鍵生成策略
1.increment 由hibernate自己維護自動增長
    底層通過先查詢max值,再+1策略
    不建議使用,存在線程并發問題
2.identity hibernate底層采用數據庫本身自動增長列
    例如:mysql auto_increment
3.sequence hibernate底層采用數據庫序列
    例如:oracle 提供序列
4.hilo 
    
    </generator>
5.native 根據底層數據庫的能力選擇 identity、sequence 或者 hilo 中的一個?!尽?##以上策略使用整形,long, short 或者 int 類型
6.uuid 采用字符串唯一值【】
##以上策略 代理主鍵,有hibernate維護。
7.assigned 自然主鍵,由程序自己維護。【】

<a id="4"> </a>

4.一級緩存

對象狀態

三種狀態

  • 瞬時態:transient,session沒有緩存對象,數據庫也沒有對應記錄。
    OID特點:沒有值
  • 持久態:persistent,session緩存對象,數據庫最終會有記錄。(事務沒有提交)
    OID特點:有值
  • 脫管態:detached,session沒有緩存對象,數據庫有記錄。
    OID特點:有值

轉換

三種狀態轉換圖

一級緩存

一級緩存:又稱為session級別的緩存。當獲得一次會話(session),hibernate在session中創建多個集合(map),用于存放操作數據(PO對象),為程序優化服務,如果之后需要相應的數據,hibernate優先從session緩存中獲取,如果有就使用;如果沒有再查詢數據庫。當session關閉時,一級緩存銷毀。

@Test
public void demo02(){
    //證明一級緩存
    Session session = factory.openSession();
    session.beginTransaction();
    
    //1 查詢 id = 1
    User user = (User) session.get(User.class, 1);
    System.out.println(user);
    //2 再查詢 -- 不執行select語句,將從一級緩存獲得
    User user2 = (User) session.get(User.class, 1);
    System.out.println(user2);
    
    session.getTransaction().commit();
    session.close();
}

可以調用方法清除一級緩存

//清除
//session.clear();
session.evict(user);

快照

與一級緩存一樣的存放位置,對一級緩存數據備份。保證數據庫的數據與 一級緩存的數據必須一致。如果一級緩存修改了,在執行commit提交時,將自動刷新一級緩存,執行update語句,將一級緩存的數據更新到數據庫。

當緩存和數據庫數據不一樣且在提交之前,可以調用 refresh 強制刷新緩存。

<a id="5"> </a>

5.關系映射

一對一

一對一關系一般是可以整合成一張表,也可以分成兩張表。
維護兩張表的關系可以選擇外鍵也可以選擇讓主鍵同步。

實體類

Address.java

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

    private Company company;
    // 省略 get set
}

Company.java

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

    private Address address;
    // 省略 get set
}

外鍵維護關系

Address.hbm.xml

<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <!--
        主鍵與外鍵表的主鍵同步
        -->
        <generator class="foreign">
            <param name="property">company</param>
        </generator>
    </id>
    <property name="name" column="name"/>

    <!--
        需要在同步主鍵的一方加上 constrained="true" 使用給主鍵加上外鍵約束
    -->
    <one-to-one name="company" class="Company" constrained="true" />
</class>

Company.hbm.xml

<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Company" table="t_company_ref" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <generator class="native"/>
    </id>
    <property name="name" column="name"/>

    <!--
        one-to-one 中使用了 property-ref :當前類哪個屬性是引用外鍵
        放棄維護外鍵
    -->
    <one-to-one name="address" class="Address" property-ref="company" />
</class>

主鍵同步關系

Address.hbm.xml

<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <!--
        主鍵與外鍵表的主鍵同步
        -->
        <generator class="foreign">
            <param name="property">company</param>
        </generator>
    </id>
    <property name="name" column="name"/>

    <!--
        需要在同步主鍵的一方加上 constrained="true" 使用給主鍵加上外鍵約束
    -->
    <one-to-one name="company" class="Company" constrained="true" />
</class>

Company.hbm.xml

<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Company" table="t_company_sync" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <generator class="native"/>
    </id>
    <property name="name" column="name"/>

    <!--
    在另一個表需要修改主鍵生成策略為 外鍵
    -->
    <one-to-one name="address" class="Address" />
</class>

一對多

實體類

Customer.java

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

    private Set<Order> orders = new HashSet<>();
    // 省略 get set
}

Order.java

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

    private Customer customer;
    // 省略 get set
}

映射文件

Customer.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.ittianyu.hibernate.onetomany">
    <!--
    dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
    -->
    <class name="Customer" table="t_customer" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <!--
        inverse 為 true 表示放棄維護關系,留給對方來維護,
        一般是一對多中 一的一方放棄,由多的一放維護,
        這個時候刪除對象時,需要手動將關聯的對象外鍵引用移除
        -->
        <set name="orders" inverse="true">
            <key column="cid"></key>
            <one-to-many class="Order" />
        </set>
    </class>
</hibernate-mapping>

Order.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.ittianyu.hibernate.onetomany">
    <!--
    dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
    -->
    <class name="Order" table="t_order" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <many-to-one name="customer" column="cid" class="Customer" />
    </class>
</hibernate-mapping>

多對多

實體類

Course.java

public class Course {
    private Integer id;
    private String name;
    private Set<Student> students = new HashSet<>();
    // 省略 get set
}

Student.java

public class Student {
    private Integer id;
    private String name;
    private Set<Course> courses = new HashSet<>();
    // 省略 get set
}

映射文件

Course.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.ittianyu.hibernate.manytomany">
    <!--
    dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
    -->
    <class name="Course" table="t_course" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>

        <!--
            many to many 中,需要給 set 加上 table 名
            放棄維護外鍵
        -->
        <set name="students" table="t_student_course" inverse="true">
            <key column="cid"></key>
            <many-to-many class="Student" column="sid" />
        </set>
    </class>
</hibernate-mapping>

Student.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.ittianyu.hibernate.manytomany">
    <!--
    dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
    -->
    <class name="Student" table="t_studennt" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <!--
            many to many 中,需要給 set 加上 table 名
            外鍵表由 student 維護,并啟用級聯,所以 course 中要放棄維護
        -->
        <set name="courses" table="t_student_course" cascade="save-update">
            <key column="sid"></key>
            <many-to-many class="Course" column="cid" />
        </set>
    </class>
</hibernate-mapping>

級聯

cascade 表示指定級聯操作的類型。

  • save-update : 增加或更新 A 時,自動增加或更新 B。
  • delete : 刪除 A 時,自動刪除 B
  • all : 上面兩項效果疊加
  • delete-orphan (孤兒刪除) : 刪除所有和當前對象解除關聯關系的對象
  • all-delete-orphan : all + delete-orphan 效果疊加
<set name="courses" table="t_student_course" cascade="save-update">
    <key column="sid"></key>
    <many-to-many class="Course" column="cid" />
</set>

<a id="6"> </a>

6.抓取策略

檢索方式

  • 立即檢索:立即查詢,在執行查詢語句時,立即查詢所有的數據。
  • 延遲檢索:延遲查詢,在執行查詢語句之后,在需要時在查詢。(懶加載)

檢索策略

  • 類級別檢索:當前的類的屬性獲取是否需要延遲。
  • 關聯級別的檢索:當前類 關聯 另一個類是否需要延遲。

類級別檢索

  • get:立即檢索。get方法一執行,立即查詢所有字段的數據。
  • load:延遲檢索。默認情況,load方法執行后,如果只使用OID的值不進行查詢,如果要使用其他屬性值將查詢??梢耘渲檬欠裱舆t檢索:
    <class  lazy="true | false">
    lazy 默認值true,表示延遲檢索,如果設置false表示立即檢索。
    

關聯級別檢索

容器<set> 提供兩個屬性:fetch、lazy,用于控制關聯檢索。

  • fetch:確定使用sql格式
    • join:底層使用迫切左外連接
    • select:使用多個select語句(默認值)
    • subselect:使用子查詢
  • lazy:關聯對象是否延遲。
    • false:立即
    • true:延遲(默認值)
    • extra:極其懶惰,調用 size 時,sql 查詢 count。(用于只需要獲取個數的時候)

批量查詢

一次加載多行數據,用于減少 sql 語句數量
<set batch-size="5">

比如: 當客戶關聯查詢訂單時,默認給每一個客戶生產一個select語句查詢訂單。開啟批量查詢后,使用in語句減少查詢訂單語句個數。

默認:select * from t_order where customer_id = ?
批量:select * from t_order where customer_id in (?,?,?,?)

檢索總結

檢索策略| 優點| 缺點| 優先考慮使用的場合
----| ----| ----
立即檢索| 對應用程序完全透明,不管對象處于持久化狀態還是游離狀態,應用程序都可以從一個對象導航到關聯的對象| (1)select語句多
(2)可能會加載應用程序不需要訪問的對象,浪費許多內存空間。| (1)類級別
(2)應用程序需要立即訪問的對象
(3)使用了二級緩存
延遲檢索| 由應用程序決定需要加載哪些對象,可以避免執行多余的select語句,以及避免加載應用程序不需要訪問的對象。因此能提高檢索性能,并節省內存空間。| 應用程序如果希望訪問游離狀態的代理類實例,必須保證她在持久化狀態時已經被初始化。| (1)一對多或者多對多關聯
(2)應用程序不需要立即訪問或者根本不會訪問的對象
表連接檢索| (1)對應用程序完全透明,不管對象處于持久化狀態還是游離狀態,都可從一個對象導航到另一個對象。
(2)使用了外連接,select語句少| (1)可能會加載應用程序不需要訪問的對象,浪費內存。
(2)復雜的數據庫表連接也會影響檢索性能。| (1)多對一或一對一關聯
(2)需要立即訪問的對象
(3)數據庫有良好的表連接性能。

<a id="7"> </a>

7.HQL

查詢所有

//1  使用簡單類名 , 存在自動導包
// * Customer.hbm.xml <hibernate-mapping auto-import="true">
//  Query query = session.createQuery("from Customer");
//2 使用全限定類名
Query query = session.createQuery("from com.ittianyu.bean.Customer");
// 獲取結果
List<Customer> allCustomer = query.list();

條件查詢

//1 指定數據,cid OID名稱
//  Query query = session.createQuery("from Customer where cid = 1");
//2 如果使用id,也可以(了解)
//  Query query = session.createQuery("from Customer where id = 1");
//3 對象別名 ,格式: 類 [as] 別名
//  Query query = session.createQuery("from Customer as c where c.cid = 1");
//4 查詢所有項,mysql--> select * from...
Query query = session.createQuery("select c from Customer as c where c.cid = 1");

Customer customer = (Customer) query.uniqueResult();

投影查詢

//1 默認
//如果單列 ,select c.cname from,需要List<Object>
//如果多列,select c.cid,c.cname from ,需要List<Object[]>  ,list存放每行,Object[]多列
//  Query query = session.createQuery("select c.cid,c.cname from Customer c");
//2 將查詢部分數據,設置Customer對象中
// * 格式:new Customer(c.cid,c.cname)
// * 注意:Customer必須提供相應的構造方法。
// * 如果投影使用oid,結果脫管態對象。
Query query = session.createQuery("select new Customer(c.cid,c.cname) from Customer c");

List<Customer> allCustomer = query.list();

排序

Query query = session.createQuery("from Customer order by cid desc");
List<Customer> allCustomer = query.list();

分頁

Query query = session.createQuery("from Customer");
// * 開始索引 , startIndex 算法: startIndex = (pageNum - 1) * pageSize;
// *** pageNum 當前頁(之前的 pageCode)
query.setFirstResult(0);
// * 每頁顯示個數 , pageSize
query.setMaxResults(2);

List<Customer> allCustomer = query.list();

綁定參數

Integer cid = 1;

//方式1 索引 從 0 開始
//  Query query = session.createQuery("from Customer where cid = ?");
//  query.setInteger(0, cid);
//方式2 別名引用 (:別名)
Query query = session.createQuery("from Customer where cid = :xxx");
//  query.setInteger("xxx", cid);
query.setParameter("xxx", cid);

Customer customer = (Customer) query.uniqueResult();

聚合函數和分組

//1 
//  Query query = session.createQuery("select count(*) from Customer");
//2 別名
//  Query query = session.createQuery("select count(c) from Customer c");
//3 oid
Query query = session.createQuery("select count(cid) from Customer");

Long numLong = (Long) query.uniqueResult();

連接查詢

//左外連接
//  List list = session.createQuery("from Customer c left outer join c.orderSet ").list();
//迫切左外鏈接 (默認數據重復)
//  List list = session.createQuery("from Customer c left outer join fetch c.orderSet ").list();
//迫切左外鏈接 (去重復)
List list = session.createQuery("select distinct c from Customer c left outer join fetch c.orderSet ").list();

命名查詢

Custom.hbm.xml

...
    <!--局部 命名查詢-->
    <query name="findAll"><![CDATA[from Customer ]]></query>
</class>
<!--全局 命名查詢-->
<query name="findAll"><![CDATA[from Customer ]]></query>

測試

//全局
//List list = session.getNamedQuery("findAll").list();
//局部
List list = session.getNamedQuery("com.ittianyu.a_init.Customer.findAll").list();

<a id="8"> </a>

8.QBC

查詢所有

List<Customer> list = session.createCriteria(Customer.class).list();

分頁查詢

Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult(10);
criteria.setMaxResults(10);
List<Order> list = criteria.list();

排序

Criteria criteria = session.createCriteria(Customer.class);
//      criteria.addOrder(org.hibernate.criterion.Order.asc("age"));
criteria.addOrder(org.hibernate.criterion.Order.desc("age"));
List<Customer> list = criteria.list();

條件查詢

// 按名稱查詢:
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.eq("cname", "tom"));
List<Customer> list = criteria.list();*/

// 模糊查詢;
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
List<Customer> list = criteria.list();*/

// 條件并列查詢
Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
criteria.add(Restrictions.ge("age", 35));
List<Customer> list = criteria.list();

離線查詢

// service 層 封裝與 session 無關的 criteria
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.eq("id", 4));

// dao 層
Session session = HibernateUtils.openSession();
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List list = criteria.list();

<a id="9"> </a>

9.其他配置

c3p0(spring 整合后直接配 dataSource)

  1. 導入 c3p0 包
  2. hibernate.cfg.xml 配置
    <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
    

log4j

  1. 導入包
    • log4j 核心包:log4j-1.2.17.jar
    • 過渡jar:slf4j-log4j12-1.7.5.jar
  2. 導入配置文件
    • log4j.properties ,此配置文件通知log4j 如何輸出日志

<a id="10"> </a>

10.事務

隔離級別

  • read uncommittd,讀未提交。存在3個問題。
  • read committed,讀已提交。解決:臟讀。存在2個問題。
  • repeatable read ,可重復讀。解決:臟讀、不可重復讀。存在1個問題。
  • serializable,串行化。單事務。沒有問題。

hibernate 中配置

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

對照上面的分別是 1 2 4 8,0表示沒有事務級別

悲觀鎖

采用數據庫鎖機制。丟失更新肯定會發生。

  • 讀鎖:共享鎖。
    select .... from  ... lock in share mode;
    
  • 寫鎖:排他鎖。(獨占)
    select ... from  ....  for update
    

Hibernate 中使用

Customer customer = (Customer) session.get(Customer.class, 1 ,LockMode.UPGRADE);

樂觀鎖

在表中提供一個字段(版本字段),用于標識記錄。如果版本不一致,不允許操作。丟失更新肯定不會發生

Hibernate 中使用

  1. 在PO對象(javabean)提供字段,表示版本字段。
    ...
    private Integer version;
    ...
    
  2. 在配置文件中增加 version
    <class ...>
        ...
        <version name="version" />
        ...
    

<a id="11"> </a>

11.二級緩存

sessionFactory 級別緩存,整個應用程序共享一個會話工廠,共享一個二級緩存。

由4部分構成:

  • 類級別緩存
  • 集合級別緩存
  • 時間戳緩存
  • 查詢緩存(二級緩存的第2大部分,三級緩存)

并發訪問策略

||
---|---
transactional| 可以防止臟讀和不可重復讀,性能低
read-write| 可以防止臟讀,更新緩存時鎖定緩存數據
nonstrict-read-write| 不保證緩存和數據庫一致,為緩存設置短暫的過期時間,減少臟讀
read-only| 適用于不會被修改的數據,并發性能高

應用場景

  • 適合放入二級緩存中的數據:
    很少被修改
    不是很重要的數據, 允許出現偶爾的并發問題
  • 不適合放入二級緩存中的數據:
    經常被修改
    財務數據, 絕對不允許出現并發問題
    與其他應用數據共享的數據

二級緩存提供商

  • EHCache: 可作為進程(單機)范圍內的緩存, 存放數據的物理介質可以是內存或硬盤, 對 Hibernate 的查詢緩存提供了支持。--支持集群。
  • OpenSymphony `:可作為進程范圍內的緩存, 存放數據的物理介質可以是內存或硬盤, 提供了豐富的緩存數據過期策略, 對 Hibernate 的查詢緩存提供了支持
  • SwarmCache: 可作為集群范圍內的緩存, 但不支持 Hibernate 的查詢緩存
  • JBossCache:可作為集群范圍內的緩存, 支持 Hibernate 的查詢緩存

開啟二級緩存

  1. 導包 hibernate-ehcache-5.2.8.Final.jar
  2. 配置
    <!--二級緩存
    #hibernate.cache.region.factory_class org.hibernate.cache.internal.EhCacheRegionFactory
    -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
    

使用二級緩存

類緩存

<!--
類緩存
-->
<class-cache class="com.ittianyu.hibernate.onetomany.Order" usage="read-only"/>
<class-cache class="com.ittianyu.hibernate.onetomany.Customer" usage="read-only"/>

集合緩存

<collection-cache collection="com.ittianyu.hibernate.onetomany.Customer.orders" usage="read-only" />

查詢緩存

將HQL語句 與 查詢結果進行綁定。通過HQL相同語句可以緩存內容。

  1. 配置

    #hibernate.cache.use_query_cache true 啟用 HQL查詢緩存
    <property name="hibernate.cache.use_query_cache">true</property>
    
  2. 使用

    Query query = session.createQuery("from Customer");
    query.setCacheable(true);// 標記為緩存
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容