hibernate 主鍵、持久化、緩存、事務

自然主鍵和代理主鍵

關系數據庫主要依靠主鍵區(qū)分不同的記錄,主鍵又有自然主鍵和代理主鍵之分。

  1. 自然主鍵

    自然主鍵:就是充當主鍵的字段本身具有一定的含義,是構成記錄的組成部分,比如學生的身份證號,除了充當主鍵之外,同時也是學生記錄的重要組成部分。

  2. 代理主鍵

    代理主鍵:就是充當主鍵的字段本身不具有業(yè)務意義,只具有主鍵作用,比如自動增長的ID。

主鍵生成策略

  1. native

    該生成主鍵生成策略是自增長,但是該生成策略會根據數據庫的不同,來調用各數據庫底層的自增長的生成策略.

  2. UUID

    適用于字符串類型主鍵,使用hibernate中的隨機方式生成字符串主鍵

  3. identity

    適用于short,int,long,類型的主鍵,由于采用的是hibernate的自動增長機制,所只能在單線程中使用,其內部實現過程是,首先發(fā)送一條語句,select max(id) form 表,然后將id+1作為插入數據的主鍵,如果在多線程中使用,有可能會出現重復的主鍵值,這是不允許的。

  4. sequence

    自增長類型,采用序列方式(適用于Oracle數據庫)

  5. increment

    適用于short,int,long類型的主鍵,適用的是數據庫底層的自動增長機制。適用于有自動增長機制的數據庫(mysql、mssql)

  6. assigned

    hibernate放棄主鍵的管理,需要通過手動編寫程序或者用戶自己設置

持久化

持久化的劃分

  1. 瞬時態(tài)

瞬時態(tài)的對象是由 new 關鍵字開辟內存空間的對象,不存在持久化標識 OID(相當于主鍵值),且未與任何的 Session 實例相關聯,在數據庫中也沒有記錄,失去引用后將被 JVM 回收。瞬時對象在內存孤立存在,它是攜帶信息的載體,不和數據庫的數據有任何關聯關系。

  1. 持久態(tài)

持久態(tài)的對象存在一個持久化標識 OID,當對象加入到 Session 緩存中時,就與 Session 實例相關聯。它在數據庫中存在與之對應的記錄,每條記錄只對應唯一的持久化對象。需要注意的是,持久態(tài)對象是在事務還未提交前變成持久態(tài)的。
當直接執(zhí)行 Session 的 get()、load()、find() 或 iterate() 等方法從數據庫中查詢出對象時,查詢到的對象也會處于持久態(tài)。
如創(chuàng)建對象,并執(zhí)行sessio.save(對象);該對象就會變成持久層對象,在Java中修改持久化對象的屬性值,數據庫會自動更新為更改后的值

  1. 脫管態(tài)\離線態(tài)

脫管態(tài)也稱離線態(tài)或者游離態(tài),當持久化對象與 Session 斷開時就變成了脫管態(tài),但是脫管態(tài)依然存在持久化標識 OID,只是失去了與當前 Session 的關聯。需要注意的是,脫管態(tài)對象發(fā)生改變時 Hibernate 是不能檢測到的。
session對象執(zhí)行close\evict\clear操作時,對象會進入脫管態(tài)。
如session執(zhí)行close方法關閉資源時,對象和session失去關聯,該對象就變成托管態(tài)\離線態(tài)

持久化對象狀態(tài)的轉換

持久化轉化方法

緩存

緩存是一種優(yōu)化方式,將數據存放在內存中,使用的時候直接從緩存中獲取
不用從數據庫中獲取

一級緩存

session級別的緩存
一級緩存是由Session中Java集合構成的

  1. 當執(zhí)行Session中save、update、saveOrUpdate方法
  2. 如果Session緩存中沒有該對象,則自動的從數據庫查詢相對應數據,查詢數據并寫入緩存中
  3. 當執(zhí)行l(wèi)oad、get方法,以及Query接口中的list、iterator方法時,會判斷緩存中是否存在該對象,有則返回,沒有則從數據庫中查詢,并寫入緩存中,并返回。
  4. 當調用close、clear方法時,緩存會被清除
public class HibernateTest{

  @Test
    public void hibernateCache () {
        // 獲取Session對象
        Session session = HibernateUtils.getSession();
        // 開啟事務
        Transaction transaction = session.beginTransaction();
        // 第一次查詢
        Customer customer = session.get(Customer.class, 2L);
        // 第一次打印,緩存已經寫入
        System.out.println(customer);

        // 第二次查詢
        Customer customer1 = session.get(Customer.class, 2L);
        // 第二次打印,從緩存中取出數據
        System.out.println(customer1);
        // 事務提交
        transaction.commit();

        // 釋放資源
        session.close();
    }
}
/**
只執(zhí)行了一條SQL語句,第二次查詢的結果是從緩存中取出的 
Hibernate: 
    select
        customer0_.cust_id as cust_id1_0_0_,
        customer0_.cust_name as cust_nam2_0_0_,
        customer0_.cust_source as cust_sou3_0_0_,
        customer0_.cust_industry as cust_ind4_0_0_,
        customer0_.cust_level as cust_lev5_0_0_,
        customer0_.cust_phone as cust_pho6_0_0_,
        customer0_.cust_mobile as cust_mob7_0_0_ 
    from
        customer customer0_ 
    where
        customer0_.cust_id=?
Customer(cust_id=2, cust_name=我, cust_source=null, cust_industry=null, cust_level=null, cust_phone=null, cust_mobile=null)
Customer(cust_id=2, cust_name=我, cust_source=null, cust_industry=null, cust_level=null, cust_phone=null, cust_mobile=null)

*/

二級緩存

二級緩存是SessionFactory級別的緩存
現在自己去配置,默認是關閉的,現在企業(yè)都不會使用,現在大多使用redis

緩存和快照區(qū)域

當實體對象變成持久態(tài)對象的時候,和數據庫表關聯后。在session中會保存兩份數據的副本。
一份是緩存,一個是快照。
緩存的作用:用于提高查詢的效率
快照的作用:用于更新數據,作對比使用。

  1. 使用id進行查詢數據庫,將查詢的結果放置到session一級緩存中,同時復制一份放入session快照中
  2. 當使用commit的時候,同時清理session的一級緩存(flush)
  3. 當清理session中緩存時,會使用UID判斷一級緩存中的對象和快照的對象進行比對
  4. 如快照和緩存中的對象中的屬性值發(fā)生變化,則會執(zhí)行update語句,此時更新數據庫,更新成一級緩存中的數據
  5. 如快照和緩存中的對象中的屬性值無變化,則不執(zhí)行update語句
    目的: 確保和數據庫中的數據保持一致

這就是持久層對象為什么更新屬性值,數據庫的值為什么更新為持久層對象的屬性值的原因
注意: 在一個事務中,如果為某持久化對象set了新的值,那么在提交事務時,Hibernate就會去比較你哪些持久化對象發(fā)生了變化,如果找到了,就會自動更新到數據庫中。(注意,如果在事務外做的操作,Hibernate是不會幫你更新的)

事務

事務: 事務是邏輯上的一組操作,要么全部成功.要么全部失敗

事務的特性

  1. 原子性
    事務開始后所有操作,要么全部做完,要么全部不做,不可能停滯在中間環(huán)節(jié)。事務執(zhí)行過程中出錯,會回滾到事務開始前的狀態(tài),所有的操作就像沒有發(fā)生一樣。也就是說事務是一個不可分割的整體.
  2. 隔離性
    同一時間,只允許一個事務請求同一數據,不同的事務之間彼此沒有任何干擾。比如A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉賬。
  3. 持久性
    事務完成后,事務對數據庫的所有更新將被保存到數據庫,不能回滾。
  4. 一致性
    事務開始前和結束后,數據庫的完整性約束沒有被破壞 。比如A向B轉賬,不可能A扣了錢,B卻沒收到。

事務的隔離級別

事務隔離級別

Hibernate 設置事務隔離級別

在核心配置文件 hibernat.cfg.xml 當中,通過數字代表不同隔離級別

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

hibernate事務的處理

為什么要在業(yè)務層使用事務?

在業(yè)務層使用事務時,必須得要保證獲取事物的連接和dao層操作的連接是同一個,否則就管理了不對應的操作


TIM截圖20191117121353.png
  1. 使用jdbc當中事務業(yè)務層處理方法
    1.1. 向下傳遞

    就開始在業(yè)務層先創(chuàng)建好一個連接,傳給dao層,讓dao層使用這個連接執(zhí)行操作

    1.2 使用ThreadLocal對象

    在service方法當中把創(chuàng)建的連接綁定到對應的threadLocal當中
    在dao方法當中,通過當前的線程獲得連接對象

  2. hibernate當中處理方法

    2.1. Hibernate框架,內部已經綁定好了ThreadLocal

    2.2. 在SessionFactory中,提供了一個方法,getCurrentSession()方法

    2.3. 獲取當前線程中的session,此方法默認不能用

    2.4. 通過配置完成

     ```xml
       <!-- 在核心配置文件當中配置 -->
       <property name="current_session_context_class">thread</property>
       <!-- 創(chuàng)建一個session綁定到當前線程通過它來操作時, 不需要 close,執(zhí)行結束后,會自動的close() --> 
     ```
    

    2.5. 測試代碼

       <!--pom文件中導入的jar包 -->
       <dependencies>
           <!-- 測試Jar包 Junit-->
           <dependency>
               <groupId>junit</groupId>
               <artifactId>junit</artifactId>
               <version>4.12</version>
               <scope>test</scope>
           </dependency>
    
           <!--Hibernate的Jar包-->
           <dependency>
               <groupId>org.hibernate</groupId>
               <artifactId>hibernate-core</artifactId>
               <version>5.3.13.Final</version>
           </dependency>
    
           <!--連接池和mysql連接jar-->
           <dependency>
               <groupId>mysql</groupId>
               <artifactId>mysql-connector-java</artifactId>
               <version>5.1.45</version>
           </dependency>
    
           <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid</artifactId>
                  <version>1.1.20</version>
           </dependency>
    
           <!-- Lombok -->
           <dependency>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>1.18.10</version>
               <scope>provided</scope>
           </dependency>
    
           <!--log4j日志jar-->
           <dependency>
               <groupId>log4j</groupId>
               <artifactId>log4j</artifactId>
               <version>1.2.17</version>
           </dependency>
    
       </dependencies>
    
    <!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>
            <!-- 設置連接數據庫的url和用戶名和密碼和數據庫驅動-->
            <property name="url">jdbc:mysql:///hibernate</property>
            <property name="username">root</property>
            <property name="password">123456</property>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <!-- 打印SQL語句 -->
            <property name="hibernate.show_sql">true</property>
            <!-- 格式化SQL -->
            <property name="hibernate.format_sql">true</property>
            <!-- 設置hibernate的方言 -->
            <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
            <!-- 設置自動創(chuàng)建表 -->
            <property name="hibernate.hbm2ddl.auto">update</property>
    
            <!--阿里巴巴 Druid 連接池 注意:如果使用Druid連接池的話需要把數據基本連接屬性改為Druid的屬性-->
            <property name="hibernate.connection.provider_class">
                com.alibaba.druid.support.hibernate.DruidConnectionProvider
            </property>
    
            <!-- 配置初始化大小、最小、最大 -->
            <property name="initialSize">1</property>
            <property name="minIdle">5</property>
            <property name="maxActive">20</property>
            <!-- 配置獲取連接等待超時的時間 -->
            <property name="maxWait">60000</property>
            <!-- 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis">60000</property>
            <!-- 配置一個連接在池中最小生存的時間,單位是毫秒 -->
            <property name="minEvictableIdleTimeMillis">300000</property>
    
            <!-- 設置事務隔離級別 -->
            <property name="hibernate.connection.isolation">4</property>
    
            <!-- 創(chuàng)建一個session綁定到當前線程 -->
            <property name="current_session_context_class">thread</property>
            
            <!-- 加載映射(mapper)文件 -->
            <mapping resource="mapper/customer.hbm.xml"></mapping>
        </session-factory>
    </hibernate-configuration>
    
    
    public class HibernateUtils {
    
        public static final SessionFactory sessionFactory;
    
        static {
            // 讀取配置,并創(chuàng)建工廠類SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        }
    
        /**
         * 獲取Session對象
         * @return Session
         */
        public static Session getSession () {
            return sessionFactory.openSession();
        }
    
        /**
         * 獲取Session對象,不過是從內部已經綁定好了ThreadLocal,獲取的Session對象
         * @return Session
         */
        public static Session getCurrentSession(){
            return sessionFactory.getCurrentSession();
        }
    
    }
    
    
    public class HibernateTest{
    
       @Test
       public void hibernateTransaction () {
           // 獲取Session對象
           Session session = HibernateUtils.getCurrentSession();
           // 開啟事務
           Transaction transaction = session.beginTransaction();
    
           // 只要有一個對象執(zhí)行失敗,該事務就會回滾,要么全部成功,要么全部失敗
    
           // 刪除操作
           Customer customer1 = new Customer();
           customer1.setCust_id(2L);
           session.delete(customer1);
    
           // 插入操作
           Customer customer = new Customer();
           customer.setCust_name("王五");
           session.save(customer);
    
           transaction.commit();
           // 在配置文件設置了
       }
    }
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容