自然主鍵和代理主鍵
關系數據庫主要依靠主鍵區(qū)分不同的記錄,主鍵又有自然主鍵和代理主鍵之分。
- 自然主鍵
自然主鍵:就是充當主鍵的字段本身具有一定的含義,是構成記錄的組成部分,比如學生的身份證號,除了充當主鍵之外,同時也是學生記錄的重要組成部分。
- 代理主鍵
代理主鍵:就是充當主鍵的字段本身不具有業(yè)務意義,只具有主鍵作用,比如自動增長的ID。
主鍵生成策略
- native
該生成主鍵生成策略是自增長,但是該生成策略會根據數據庫的不同,來調用各數據庫底層的自增長的生成策略.
- UUID
適用于字符串類型主鍵,使用hibernate中的隨機方式生成字符串主鍵
- identity
適用于short,int,long,類型的主鍵,由于采用的是hibernate的自動增長機制,所只能在單線程中使用,其內部實現過程是,首先發(fā)送一條語句,select max(id) form 表,然后將id+1作為插入數據的主鍵,如果在多線程中使用,有可能會出現重復的主鍵值,這是不允許的。
- sequence
自增長類型,采用序列方式(適用于Oracle數據庫)
- increment
適用于short,int,long類型的主鍵,適用的是數據庫底層的自動增長機制。適用于有自動增長機制的數據庫(mysql、mssql)
- assigned
hibernate放棄主鍵的管理,需要通過手動編寫程序或者用戶自己設置
持久化
持久化的劃分
- 瞬時態(tài)
瞬時態(tài)的對象是由 new 關鍵字開辟內存空間的對象,不存在持久化標識 OID(相當于主鍵值),且未與任何的 Session 實例相關聯,在數據庫中也沒有記錄,失去引用后將被 JVM 回收。瞬時對象在內存孤立存在,它是攜帶信息的載體,不和數據庫的數據有任何關聯關系。
- 持久態(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中修改持久化對象的屬性值,數據庫會自動更新為更改后的值
- 脫管態(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集合構成的
- 當執(zhí)行Session中save、update、saveOrUpdate方法
- 如果Session緩存中沒有該對象,則自動的從數據庫查詢相對應數據,查詢數據并寫入緩存中
- 當執(zhí)行l(wèi)oad、get方法,以及Query接口中的list、iterator方法時,會判斷緩存中是否存在該對象,有則返回,沒有則從數據庫中查詢,并寫入緩存中,并返回。
- 當調用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中會保存兩份數據的副本。
一份是緩存,一個是快照。
緩存的作用:用于提高查詢的效率
快照的作用:用于更新數據,作對比使用。
- 使用id進行查詢數據庫,將查詢的結果放置到session一級緩存中,同時復制一份放入session快照中
- 當使用commit的時候,同時清理session的一級緩存(flush)
- 當清理session中緩存時,會使用UID判斷一級緩存中的對象和快照的對象進行比對
- 如快照和緩存中的對象中的屬性值發(fā)生變化,則會執(zhí)行update語句,此時更新數據庫,更新成一級緩存中的數據
- 如快照和緩存中的對象中的屬性值無變化,則不執(zhí)行update語句
目的: 確保和數據庫中的數據保持一致
這就是持久層對象為什么更新屬性值,數據庫的值為什么更新為持久層對象的屬性值的原因
注意: 在一個事務中,如果為某持久化對象set了新的值,那么在提交事務時,Hibernate就會去比較你哪些持久化對象發(fā)生了變化,如果找到了,就會自動更新到數據庫中。(注意,如果在事務外做的操作,Hibernate是不會幫你更新的)
事務
事務: 事務是邏輯上的一組操作,要么全部成功.要么全部失敗
事務的特性
- 原子性
事務開始后所有操作,要么全部做完,要么全部不做,不可能停滯在中間環(huán)節(jié)。事務執(zhí)行過程中出錯,會回滾到事務開始前的狀態(tài),所有的操作就像沒有發(fā)生一樣。也就是說事務是一個不可分割的整體. - 隔離性
同一時間,只允許一個事務請求同一數據,不同的事務之間彼此沒有任何干擾。比如A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉賬。 - 持久性
事務完成后,事務對數據庫的所有更新將被保存到數據庫,不能回滾。 - 一致性
事務開始前和結束后,數據庫的完整性約束沒有被破壞 。比如A向B轉賬,不可能A扣了錢,B卻沒收到。
事務的隔離級別

Hibernate 設置事務隔離級別
在核心配置文件 hibernat.cfg.xml 當中,通過數字代表不同隔離級別
<property name="hibernate.connection.isolation">4</property>
hibernate事務的處理
為什么要在業(yè)務層使用事務?
在業(yè)務層使用事務時,必須得要保證獲取事物的連接和dao層操作的連接是同一個,否則就管理了不對應的操作

-
使用jdbc當中事務業(yè)務層處理方法
1.1. 向下傳遞就開始在業(yè)務層先創(chuàng)建好一個連接,傳給dao層,讓dao層使用這個連接執(zhí)行操作
1.2 使用ThreadLocal對象
在service方法當中把創(chuàng)建的連接綁定到對應的threadLocal當中
在dao方法當中,通過當前的線程獲得連接對象 -
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(); // 在配置文件設置了 } }