[toc]
Hibernate它是一個輕量級的jdbc封裝,也就是說,我們可以使用hibernate來完成原來我們使用jdbc完成操作,就是與資料庫的互動操作。它是在dao層去使用的。
物件關係對映(英語:Object Relation Mapping,簡稱ORM,或O/RM,或O/R mapping)。簡單說,我們使用orm可以將我們的物件與我們的類去進行對映,使的我們可以去操作物件就完成對錶的操作。
一、Hibernate檔案目錄結構
documentation目錄:存放hibernate的相關檔案與API
lib目錄:存放hibernate編譯和執行所依賴的jar包,其中required子目錄下包含了執行hibernate專案必須的jar包
project目錄:存放hibernate各種相關的原始碼與資源.
在lib/required目錄中,包含必需的jar包
快速搭建專案需要匯入的包
匯入lib/required下所有的jar 匯入資料庫的驅動jar包 日誌相關jar包 3 將hibernate/project/etc/log4j.properties檔案匯入到工程src下
二、Hibernate的配置詳解
Hibernate中我們使用時主要有兩種配置檔案
核心配置檔案 hibernate.cfg.xml
對於hibernate的核心配置檔案它有兩種方式:
- hibernate.cfg.xml
- hibernate.properties
我們在開發中使用比較多的是hibernate.cfg.xml這種方式,原因它的配置能力更強,易於修改 我們主要學習的是hibernate.cfg.xml配置
- 可以載入資料庫相關資訊
- hibernate相關配置
- 載入對映配置檔案
<session-factory>
<!-- 配置關於資料庫連線的四個項 driverClass url username password -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3366/hibernateTest</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123</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.MySQLDialect</property>
<!-- 自動建立表 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 用於設定事務提交方式 -->
<property name="hibernate.connection.autocommit">false</property>
<!-- 配置hibernate的對映檔案所在位置 -->
<mapping resource="com/lbb/hibernate/domain/Customer.hbm.xml" />
</session-factory>
複製程式碼
對於hibernate.cfg.xml配置檔案中的內容可以參考hibernate/project/etc/hibernate.properties的配置
4. 配置<property name="hibernate.hbm2ddl.auto">update</property>
屬性後,我們可以進行表的自動建立
- Create-drop 每次都會建立一個新的表,執行完成後刪除。一般在測試中使用
- Create 每次都會建立一個新的表,一般是在測試中使用
- update 如果資料庫中有表,不建立,沒有表建立,如果對映不匹配,會自動更新表結構(只能新增)
- validate 只會使用存在的表,並且會對對映關係進行校驗.
對映配置檔案 xxx.hbm.xml
對映配置檔案它的名稱是類名.hbm.xml,它一般放置在實體類所在的包下。 這個配置檔案的主要作用是建立表與類的對映關係。
<hibernate-mapping package="cn.itheima.domain">
<!-- name屬性它是實體類的全名 table 表的名稱 catalog 資料庫名稱 -->
<class name="Customer" table="t_customer"
catalog="hibernateTest">
<!-- id它是用於描述主鍵 -->
<id name="id" column="id" type="int"> <!-- java資料型別 -->
<!-- 主鍵生成策略 -->
<generator class="native"></generator>
</id>
<!-- 使用property來描述屬性與欄位的對應關係 -->
<property name="name" column="name" length="20" type="string"></property> <!-- hibernate資料型別 -->
<property name="address">
<column name="address" length="50" sql-type="varchar(50)"></column> <!-- sql資料型別 -->
</property>
<property name="sex" column="sex" length="20"></property>
</class>
</hibernate-mapping>
複製程式碼
- 統一宣告包名,這樣在
<class>
中就不需要寫類的全名.<hibernate-mapping package="cn.itheima.domain">
- 關於
<class>
標籤配置 name屬性:類的全名稱 table 表的名稱,可以省略,這時表的名稱就與類名一致 catalog屬性:資料庫名稱 可以省略.如果省略,參考核心配置檔案中url路徑中的庫名稱 - 關於
<id>
標籤 首先它必須存在。<id>
是用於建立類中的屬性與表中的主鍵對映。 name 類中的屬性名稱 column 表中的主鍵名稱 column它也可以省略,這時列名就與類中屬性名稱一致 length 欄位長度 type屬性 指定型別<generator>
它主要是描述主鍵生成策略. - 關於
<property>
標籤 它是描述類中屬性與表中非主鍵的對映關係
關於hibernate的對映檔案中型別問題
對於type屬性它的取值,可以有三種:
- java中的資料型別
- hibernate中的資料型別
- SQL的資料型別
三、Hibernate的執行原理和常用API
hibernate工作原理
- 通過Configuration().configure();讀取並解析hibernate.cfg.xml配置檔案。
- 由hibernate.cfg.xml中的
<mappingresource="com/xx/User.hbm.xml"/>
讀取解析對映資訊。 - 通過config.buildSessionFactory();//得到sessionFactory。
- sessionFactory.openSession();//得到session。
- session.beginTransaction();//開啟事務。
- persistent operate;
- session.getTransaction().commit();//提交事務
- 關閉session;
- 關閉sessionFactory;
Hibernate的核心類和介面一共有6個
分別為:Session、SessionFactory、Transaction、Query、Criteria和Configuration。這6個核心類和介面在任何開發中都會用到
// 儲存一個Customer
@Test
public void saveCustomerTest() {
// 使用hibernate的api來完成將customer資訊儲存到mysql中操作
Configuration config = new Configuration().configure(); // 載入hibernate.cfg.xml
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession(); // 相當於得到一個Connection。
// 開啟事務
Transaction transaction = session.beginTransaction();
// 操作
Customer c = new Customer();
c.setName("張三");
c.setAddress("北京");
c.setSex("男");
session.save(c);
// 事務提交
transaction.commit();
session.close();
sessionFactory.close();
}
複製程式碼
Configuration
它主要是用於載入hibernate配置
Configuration config=new Configuration().config(); 主要載入src下的hibernate.cfg.xml
Configuration config=new Configuration();主要載入的src下的hibernate.properties
Configuration config=new Configuration().config(核心配置檔名稱);載入指定的名稱的配置檔案
問題:我們是在hibernate.cfg.xml檔案中有xxx.hbm.xml檔案的位置。如果我們使用的是hibernate.properties這種核心配置,它如何載入對映配置?
Configuration config=new Configuration();//主要載入的src下的hibernate.properties
// 手動載入對映
// config.addResource("cn/itheima/domain/Customer.hbm.xml"); 直接載入對映配置檔案
// config.addClass(Customer.class); //這種方式它會直接在實體類所在包下查詢規範對映配置檔案
複製程式碼
SessionFactory
SessionFactory介面負責初始化Hibernate。它充當資料儲存源的代理,並負責建立Session物件。這裡用到了工廠模式。需要注意的是SessionFactory並不是輕量級的,因為一般情況下,一個專案通常只需要一個SessionFactory就夠,當需要操作多個資料庫時,可以為每個資料庫指定一個SessionFactory。
SessionFactory它不是輕量級的,不要頻繁建立關閉它。在一個專案中有一個SessionFactory就可以,通過SessionFactory來獲取Session進行操作。
public class HibernateUtils {
private static Configuration config;
private static SessionFactory sessionFactory;
static{
config=new Configuration().configure();
sessionFactory=config.buildSessionFactory();
}
// 是從連線池中獲取一個連線
public static Session openSession(){
return sessionFactory.openSession();
}
// 獲取一個與執行緒繫結的Session
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
}
複製程式碼
SessionFactory內部還維護了一個連線池,如果我們要想使用c3p0連線池,應該怎樣處理?
-
我們要匯入c3p0的相關jar包 在hibernate/lib/options下有關於c3p0連線池jar包
-
在hibernate.cfg.xml檔案中配置c3p0連線 可以檢視etc/hibernate.properties中關於c3p0的配置
<!-- 設定連線提供者 -->
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- c3p0連線池的配置 -->
<property name="hibernate.c3p0.max_size">20</property> <!-- 最大連線池 -->
<property name="hibernate.c3p0.min_size">5</property> <!-- 最小連線數 -->
<property name="hibernate.c3p0.timeout">120</property> <!-- 超時 -->
<property name="hibernate.c3p0.idle_test_period">3000</property> <!-- 空閒連線 -->
複製程式碼
Session
Session介面負責執行被持久化物件的CRUD操作(CRUD的任務是完成與資料庫的交流,包含了很多常見的SQL語句)。但需要注意的是Session物件是非執行緒安全的。
問題:我們如何解決session的安全問題?
我們只需要在方法內部來使用Session就可以。
問題:Session如何獲取到?
SessionFactory.openSession() ; 相當於直接通過SessionFactory建立一個新的Session,使用完成後要手動呼叫close來關閉。
SessionFactory.getCurrentSession(); 獲取一個與執行緒繫結的Session,當我們提交或事務回滾後會自動關閉。
Session常用的方法
save 儲存物件 update 修改操作 delete刪除 get/load 根據id進行查詢 savenOrUpdate 執行save或update操作 createQuery()獲取一個Query物件 createSQLQUery()獲取一個可以操作sql的SQLQuery物件 createCriteria() 獲取一個Criteria它可以完成條件查詢
Transaction
Transaction介面主要用於管理事務,它是hibernate的事務介面,對底層的事務進行了封裝。使用它可以進行事務操作。 commit 事務提交 rollback 事務回滾
問題:如果在程式中沒有開啟事務,是否存在事務? 有事務,session的每一個操作就會開啟一個事務。
預設情況下事務是不會自動提交的。
<!-- 用於設定事務提交方式 -->
<property name="hibernate.connection.autocommit">false</property>
複製程式碼
Query(重點)
Query介面讓你方便地對資料庫及持久物件進行查詢,它可以有兩種表達方式:HQL語言或本地資料庫的SQL語句。Query經常被用來繫結查詢引數、限制查詢記錄數量,並最終執行查詢操作。
通過Query主要完成查詢操作. 我們通過Query可以執行hql語句. Query query=Session.createQuery(hql);
下面這個可以執行sql語句 SQLQUery sqlQuery=Session.createSQLQuery(sql); SQLQuery是Query的子.
使用hql完成查詢所有操作
@Test
public void test1() {
Session session = HibernateUtils.openSession();
Query query = session.createQuery("from Customer");// from後面是類名
List<Customer> list = query.list();
System.out.println(list);
session.close();
}
複製程式碼
分頁查詢
// 分頁查詢 一頁顯示10條 要得到第二頁資料
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Query query = session.createQuery("from Customer");
query.setFirstResult(10);
query.setMaxResults(10);
List list = query.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
複製程式碼
查詢指定列資訊
Select name ,address from Customer; 得到的是List<Object[]>
結果
要想得到List結果
- 在Customer類中生成以name,address為引數的構造,注意,無引數構造也要有。
- Select new Customer(name,address) from Customer;
// 查詢指定列資訊
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Query query = session.createQuery("select name,address from Customer");
List<Object[]> list = query.list();
System.out.println(list);
Query query2 = session.createQuery("select new Customer(name,address) from Customer");
List list2 = query2.list();
System.out.println(list2);
session.getTransaction().commit();
session.close();
}
複製程式碼
條件查詢
無名稱引數 from Customer where name=? 對其進行賦值 query.setParameter(0,”張三”)
有名稱引數 from Customer where name=:myname; 對其進行賦值 query.setParameter(“myname”,”李四”);
如果查詢結果可以保證就是唯一 的,我們可以使用 query. uniqueResult()來得到一個單獨物件.
// 條件查詢
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// Query query = session.createQuery("select new Customer(name,address) from Customer where name = ?");
Query query = session.createQuery("from Customer where name = ?");
query.setParameter(0, "姓名0");
Customer customer = (Customer) query.uniqueResult();
System.out.println(customer);
Query query2 = session.createQuery("from Customer where name = :myname");
query2.setParameter("myname", "姓名0");
Customer customer2 = (Customer) query.uniqueResult();
System.out.println(customer2);
session.getTransaction().commit();
session.close();
}
複製程式碼
執行本地SQL 要想執行本地sql
SQLQuery sqlQuery=session.createSqlQuery(String sql);
使用addEntity方法來將結果封裝到指定的物件中,如果不封裝,得到的是List<Object[]>
如果sql中有引數,我們使用setParameter方法完成引數傳遞。
如果結果就是一個可以使用uniqueResult()來得到一個單獨物件。
// 執行本地sql----查詢全部
@Test
public void test6() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 執行select * from t_customer;
SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer");
// List<Object[]> list = sqlQuery.list();
// System.out.println(list);
// 想要將結果封裝到Customer物件中
sqlQuery.addEntity(Customer.class);
List<Customer> list = sqlQuery.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 執行本地sql----條件查詢
@Test
public void test7() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 執行select * from t_customer where name=?;
SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer where name=?");
// 對引數進行賦值
sqlQuery.setParameter(0, "姓名1");
// 想要將結果封裝到Customer物件中
sqlQuery.addEntity(Customer.class);
// List<Customer> list = sqlQuery.list();
// System.out.println(list);
Customer c = (Customer) sqlQuery.uniqueResult();
System.out.println(c);
session.getTransaction().commit();
session.close();
}
複製程式碼
Criteria
Criteria介面與Query介面非常類似,允許建立並執行物件導向的標準化查詢。值得注意的是Criteria介面也是輕量級的,它不能在Session之外使用。
首先我想使用Criteria,必須得到Criteria Criteria criteria=Session.createCriteria()
查詢所有操作 Session.createCriteria(實體類.class)得到一個Criteria物件,呼叫list查詢所有
分頁操作與query的方法一樣 setFirstResult() setMaxResults()
條件查詢 criteria.add(Restrictions.eq(“name”,”xxxx”)); criteria.add(Restrictions.or(Restricitons.eq(),Restrictions.list()…..))
我們使用Criteria可以更加物件導向去操作,它非常適合進行多條件組合查詢。
@Test
public void test8() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Customer.class);
// 查詢所有
// List list = criteria.list();
// System.out.println(list);
// 分頁查詢
// criteria.setFirstResult(10);
// criteria.setMaxResults(10);
// List list2 = criteria.list();
// System.out.println(list2);
// 多條件查詢
// 1.查詢name='姓名1'
// criteria.add(Restrictions.eq("name","姓名1"));
// Customer customer = (Customer) criteria.uniqueResult();
// System.out.println(customer);
// 2.查詢address='上海'
// criteria.add(Restrictions.eq("address", "上海"));
// Customer customer = (Customer) criteria.uniqueResult();
// System.out.println(customer);
// criteria.add(Restrictions.eq("name","姓名1"));
// criteria.add(Restrictions.eq("address", "上海"));
// Customer customer = (Customer) criteria.uniqueResult();
// System.out.println(customer);
// List list = criteria.list();
// System.out.println(list);
//查詢name='姓名1' 或者 address='上海'
criteria.add(Restrictions.or(Restrictions.eq("name", "姓名1"),Restrictions.eq("address","上海")));
List<Customer> list = criteria.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
複製程式碼
四、Hibernate持久化類與主鍵生成策略
Hibernate持久化類
什麼是持久化類?
持久化類Persistent Object (PO)其實就相當於PO=POJO+hbm對映配置。
hibernate中的PO編寫規則
- 必須提供一個無引數的public構造方法
- 所有屬性要private ,對外提供public 的get/set方法
- 在PO類必須提供一個標識屬性,讓它與資料庫中的主鍵對應,我們管這個屬性叫OID
- PO類中的屬性儘量使用基本資料型別的包裝類. Int-Integer double--Double float-Float
- PO類它不能使用final修飾符
OID作用
OID指的是與資料庫中表的主鍵對應的屬性。
Hibernate框架它是通過OID來區分不同的PO物件,如果在記憶體中有兩個相同的OID物件,那麼hibernate認為它們是同一個物件。
為什麼PO類屬性它要使用包裝型別
使用基本資料型別是沒有辦法去描述不存在概念,如果使用包裝型別,它就是一個物件,對於物件它的預設值是null。
PO類不可以使用final修飾?(hibernate中的get/load方法的區別)
-
get直接得到了一個持久化型別物件,它就是立即查詢操作。 load它得到的是持久化類開的代理型別物件(子類物件)。它預設採用了一種延遲策略來查詢資料。
-
get方法在查詢時,如果不存在返回null。 load方法在查詢時,如果 不存在,會產生異常ObjectNotFoundException。
Hibernate主鍵生成策略
Hibernate中定義的主鍵型別包括:自然主鍵和代理主鍵。
自然主鍵:具有業務含義 欄位 作為主鍵,比如:學號、身份證號。
代理主鍵:不具有業務含義 欄位作為主鍵(例如 自增id),比如:mysql自增主鍵,oracle序列生成的主鍵、uuid()方法生成的唯一序列串。建議:企業開發中使用代理主鍵!
主鍵生成器 | 描述 |
---|---|
increment | 代理主鍵。由hibernate維護一個變數,每次生成主鍵時自動以遞增。問題:如果有多個應用訪問一個資料庫,由於每個應用維護自己的主鍵,所以此時主鍵可能衝突。建議不採用。 優點:可以方便跨平臺。缺點:不適合高併發訪問 |
identity | 代理主鍵。由底層資料庫生成表識符。條件是資料庫支援自動增長資料型別。比如:mysql的自增主鍵,oracle不支援主鍵自動生成。如果資料庫支援自增建議採用。 優點:由底層資料庫維護,和hibernate無關。缺點:只能對支援自動增長的資料庫有效,例如mysql |
sequence | 代理主鍵。Hibernate根據底層資料庫序列生成識別符號。條件是資料庫支援序列。比如oracle的序列。如果資料庫支援序列建議採用。 優點:由底層資料庫維護,和hibernate無關。缺點:資料庫必須支援sequence方案例如oracle。 |
native | 代理主鍵。根據底層資料庫對自動來選擇identity、sequence、hilo.由於生成主鍵策略的控制權由hibernate控制,所以不建議採用。 優點:在專案中如果存在多個資料庫時使用。缺點:效率比較低。 |
uuid | 代理主鍵。Hibernate採用128bit位的UUID演算法來生成識別符號。該演算法能夠在網路環境中生成唯一的字串識別符號。此策略可以保證生成主鍵的唯一性,並且提供了最好的資料庫插入效能和資料庫平臺的無關性。建議採用。 優點:與資料庫無關,方便資料庫移植,效率高,不訪問資料庫就可以直接生成主鍵值,並且它能保證唯一性。缺點:uuid長度大(32位),佔用空間比較大,對應資料庫中型別 char varchar |
assigned | 自然主鍵。由java程式負責生成識別符號。不建議採用。儘量在操作中避免手動對主鍵操作 |
Hibernate持久化物件狀態
持久化物件三種狀態
-
瞬時態:也叫做臨時態或自由態,它一般指我們new出來的物件,它不存在OID,與hibernate session無關聯,在資料庫中也無記錄。它使用完成後,會被jvm直接回收掉,它只是用於資訊攜帶。 簡單說:無OID 與資料庫中的資訊無關聯,不在session管理範圍內。
-
持久態:在hibernate session管理範圍內,它具有持久化標識OID它的特點,在事務未提交前一直是持久態,當它發生改變時,hibernate是可以檢測到的。 簡單說:有OID 由session管理,在資料庫中有可能有,也有可有沒有。
-
託管態:也叫做遊離態或離線態,它是指持久態物件失去了與session的關聯,託管態物件它存在OID,在資料庫中有可能存在,也有可能不存在。 對於託管態物件,它發生改變時hibernet不能檢測到。
持久化類三種狀態切換
判斷持久化類物件三種狀態:
- 是否有OID
- 判斷是否與session關聯
-
瞬時態(new 出來的) 瞬時------持久 save saveOrUpdate 瞬時-----脫管(遊離) 手動設定oid
-
.持久態 它是由session管理 持久-------瞬時 delete() 被刪除後持久化物件不在建議使用 持久-----脫管 注意:session它的快取就是所說的一級快取 evict(清除一級快取 中指定的一個物件) clear(清空一級快取) close(關閉,清空一級快取)
-
.脫管態 (它是無法直接獲取) 脫管-----瞬時 直接將oid刪除 脫管----持久 update saveOrUpdate lock(過時)
五、Hibernate一級快取
Hibernate的一級快取就是指session快取。在Session——EventSource——SessionImpl實現類中,有如下屬性
private transient ActionQueue actionQueue;
private transient StatefulPersistenceContext persistenceContext;
複製程式碼
actionQueue它是一個行列佇列,它主要記錄crud操作的相關資訊。 persistenceContext它是持久化上下文,它其實是真正快取。
在session中定義了一系列的集合來儲存資料,它們構成session快取。只要session沒有關閉,它就會一直存在。當我們通過hibernate中的session提供的一些API例如 save get update等進行操作時,就會將持久化物件儲存到session中,當下一次在去查詢快取中具有的物件(OID值來判斷),就不會去從資料庫查詢,而是直接從快取中獲取。Hibernate的一級快取存在的目的就是為了減少對資料庫訪問。
持久化物件具有自動更新資料庫能力
一級快取常用API
一級快取特點
- 當我們通過session的save,update saveOrupdate進行操作時,如果一級快取中沒有物件,會將這些物件從資料庫中查詢到,儲存到一級快取。
- 當我們通過session的load,get,Query的list等方法進行操作時,會先判斷一級快取中是否存在,如果沒有才會從資料庫獲取,並且將查詢的資料儲存到一級快取中。
- 當呼叫session的close方法時,session快取清空。 clear 清空一級快取. evict 清空一級快取中指定的一個物件。 refresh重新查詢資料庫,用資料庫中資訊來更新一級快取與快照
Hibernate常用API-Session補充
update
- udpate操作它主要是針對於脫管物件,持久物件具有自動更新能力。
- Update操作時,如果物件是一個脫管物件,可以操作,它會將脫管物件轉換成持久物件在操作。
- 如果在session中出現相同的oid兩個物件,會產生異常。
- 脫管物件的oid如果在資料表中不存在,會報異常。
所以:在操作中,建議我們通過持久化物件來直接修改其操作。
saveOrUpdate
如果物件是一個瞬時物件 --------執行save操作 如果物件是一個脫管物件---------執行update 如果是一個持久物件-------直接返回
delete
刪除一個脫管物件,與session關聯,在刪除。注意:如果執行delete操作,先刪除一級快取,在刪除資料庫中的資料。刪除後物件id變為空,物件變為瞬時態
六、Hibernate關聯對映--資料物件三種關係
Hibernate框架基於ORM設計思想,它將關係型資料庫中的表與我們java中的類進行對映,一個物件就對應著表中的一條記錄,而表中的欄位對應著類中的屬性。資料庫中表與表之間存在著三種關係,也就是系統設計中的三種實體關係。
一對多
實體類建立
public class Customer {
private Integer id; // 主鍵
private String name; // 姓名
// 描述客戶可以有多個訂單
private Set<Order> orders = new HashSet<Order>();
public class Order {
private Integer id;
private Double money;
private String receiverInfo; // 收貨地址
// 訂單與客戶關聯
private Customer c; // 描述訂單屬於某一個客戶
複製程式碼
Hbm對映檔案編寫
<!-- 一個客戶關聯多個訂單 -->
<set name="orders" inverse="false" cascade="all">
<key column="cid" />
<one-to-many class="cn.itheima.oneToMany.Order" />
</set>
<!-- 使用set來描述在一的一方中關聯的多 Set<Order>,
它的name屬性就是set集合的名稱
key:它主要描述關聯的多的一方產生的外來鍵名稱,注意要與多的一方定義的外來鍵名稱相同
one-to-many 描述集合中的型別 -->
<!-- 多對一 -->
<many-to-one fetch="join" lazy="false" name="c" class="cn.itheima.oneToMany.Customer" column="cid" cascade="save-update">
</many-to-one>
<!--
name屬性它描述的是Order類中的一的一方的屬性名稱 Customer c;
class 代表一的一方的型別
column 描述的是一對多,在多的一方產生的外來鍵的名稱 c_customer_id
-->
複製程式碼
使用級聯操作,設定cascade=save-update
那麼在儲存一方的同時可以同時儲存另一方。
我們在開發中要配置雙向關聯配置。---------可以通過任意一方來操作對方
在操作程式碼,儘量來要進行單向關聯。------可以儘量資源浪費。
在雙向關聯中,會存在多餘的update語句。我們可以使用inverse
屬性來設定,雙向關聯時由哪一方來維護表與表之間的關係。
Inverse它的值如果為true代表,由對方來維護外來鍵。 Inverse它的值如果為false代表,由本方來維護外來鍵。 原則:inverse設定在主表的一方,外來鍵在哪一個表中,我們就讓哪一方來維護外來鍵。
物件導航
cascade總結
使用cascade可以完成級聯操作 它可常用取值
- none這是一個預設值
- save-update,當我們配置它時,底層使用save update或save-update完成操作,級聯儲存臨時物件,如果是遊離物件,會執行update.
- delete 級聯刪除
- delete-ophan 刪除與當前物件解除關係的物件。
- all 它包含了save-update delete操作
- all-delete-orphan 它包信了delete-orphan與all操作
cascade與inverse有什麼區別 cascade它是完成級聯操作 Inverse它只有在雙向關聯情況下有作用,它來指定由哪一方維護外來鍵。
七、Hibernate註解開發
PO類註解配置
- @Entity 宣告一個實體
- @Table來描述類與表對應
- @Id來宣告一個主鍵
- @GenerateValue 用它來宣告一個主鍵生成策略 預設情況下相當於native 可以選擇的主鍵生成策略 AUTO IDENTITY SEQUENCE
- @Column來定義列 注意:對於PO類中所有屬性,如果你不寫註解,預設情況下也會在表中生成對應的列。 列的名稱就是屬性的名稱
- @Temporal來宣告日期型別
可以選擇
TemporalType.DATA 只有年月日
TemporalType.TIME 只有小時分鐘秒 TemporalType.TIMESTAMP 有年月日小時分鐘秒 - @Transient設定類的屬性不在表中對映
@Entity // 定義了一個實體
@Table(name = "t_book", catalog = "hibernateTest")
public class Book {
@Id // 主鍵
// @GeneratedValue //native
@GeneratedValue(strategy = GenerationType.IDENTITY) // identity
private Integer id; // 主鍵
@Column(name = "c_name", length = 30, nullable = true)
private String name;
@Temporal(TemporalType.TIMESTAMP) // 是用來定義日期型別
private Date publicationDate; // 出版日期
@Type(type="double")
private Double price; // 價格 如果沒有新增註解,也會自動的生成在表中
@Transient
private String msg; // 現在這個屬性不想生成在表中
複製程式碼
如果我們主鍵生成策略想使用UUID型別。
@Id
@GenericGenerator(name = "myuuid", strategy = "uuid")
@GeneratedValue(generator = "myuuid")
private String id;
複製程式碼
對於我們以上講解的關於屬性配置的註解,我們也可以在其對應的getXxx方法去使用。我們最終需要在hibernate.cfg.xml檔案中將我們類中的註解配置引用生效。<mapping class="cn.itheima.oneToMany.Customer" /> <mapping class="cn.itheima.oneToMany.Order" />
一對多(多對一)
// 描述客戶可以有多個訂單
/*
* targetEntity相當於<one-to-many class="">
* mappedBy相當於inverse=true
*/
@OneToMany(targetEntity=Order.class,mappedBy="c",orphanRemoval=true)
@Cascade(org.hibernate.annotations.CascadeType.DELETE)
private Set<Order> orders = new HashSet<Order>();
// 訂單與客戶關聯
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "c_customer_id") // 指定外來鍵列
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private Customer c; // 描述訂單屬於某一個客戶
複製程式碼
對於這個示例我們需要在Customer中配置cascade操作,save-update
第一種方式,可以使用JPA提供的註解。在@OneToMany中cascade=CascadeType.ALL
第二種方式:可以使用hibernate提供的註解.如上程式碼。
擴充套件:關於hibernate註解@Cascade中的DELETE_ORPHAN過時,我們在@OneToMany中新增orphanRemoval=true
解決
Hibernate關聯對映-多對多
使用@ManyToMany來配置多對多,只需要在一端配置中間表,另一端使用mappedBy表示放置外來鍵維護權。
建立PO類 描述學生與老師.
@ManyToMany(targetEntity = Student.class, mappedBy = "teachers") // 代表由對方來維護外來鍵
@Cascade(CascadeType.ALL)
private Set<Student> students = new HashSet<Student>();
@ManyToMany(targetEntity = Teacher.class)
// 使用JoinTabl來描述中間表,並描述中間表中外來鍵與Student,Teacher的對映關係
// joinColumns它是用來描述Student與中間表中的對映關係
// inverseJoinColums它是用來描述Teacher與中間表中的對映關係
@JoinTable(name = "s_t", joinColumns = {
@JoinColumn(name = "c_student_id", referencedColumnName = "id") }, inverseJoinColumns = {
@JoinColumn(name = "c_teacher_id", referencedColumnName = "id") })
@Cascade(CascadeType.ALL)
private Set<Teacher> teachers = new HashSet<Teacher>();
複製程式碼
Hibernate關聯對映-一對一
一對一操作有兩種對映方式:
- 在任意一方新增外來鍵
- 主鍵對映
建立PO類 以人與身份證號為例
@OneToOne(targetEntity = IDCard.class, mappedBy = "user")
private IDCard idCard;
@OneToOne(/*targetEntity = User.class*/)
@JoinColumn(name = "c_user_id")
@Cascade(CascadeType.SAVE_UPDATE)
private User user;
複製程式碼
八、Hibernate檢索方式概述
對資料庫操作中,最常用的是select.使用hibernate如何select操作。
- 導航物件圖檢索方式,根據已載入的物件導航到其它物件
- OID檢索方式,按照物件的OID來檢索物件
- HQL檢索方式,使用物件導向的HQL查詢語言
- QBC檢索方式,使用QBC(Query by Criteria)API來檢索物件,這種API封裝了基於字串形式的查詢語句,提供了更加物件導向的查詢介面
- 本地SQL檢索方式,使用本地資料庫的SQL查詢語句
導航物件圖檢索方式
Customer c=session.get(Customer.class,2); c.getOrders().size() 通過在hibernate中進行對映關係,在hibernate操作時,可以通過導航方式得到 其關聯的持久化物件資訊。
OID檢索方式
Session.get(Customer.class,3); Session.load(Order.class,1); Hibernate中通過get/load方法查詢指定的物件,要通過OID來查詢。
HQL
HQL是我們在hibernate中是常用的一種檢索方式。 HQL(Hibernate Query Language)提供更加豐富靈活、更為強大的查詢能力 因此Hibernate將HQL查詢方式立為官方推薦的標準查詢方式,HQL查詢在涵蓋Criteria查詢的所有功能的前提下,提供了類似標準SQL語 句的查詢方式,同時也提供了更加物件導向的封裝。完整的HQL語句形式如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc 其中的update/delete為Hibernate3中所新新增的功能,可見HQL查詢非常類似於標準SQL查詢。 基本步驟:
- 得到Session
- 編寫HQL語句
- 通過session.createQuery(hql)建立一個Query物件
- 為Query物件設定條件引數
- 執行list查詢所有,它反胃的是List集合 uniqueResut()返回一個查詢結果。
PO類
@Entity
@Table(name = "t_customer")
@NamedQuery(name = "myHql", query = "from Customer")
@SqlResultSetMapping(name = "customerSetMapping", entities = { @EntityResult(entityClass = Customer.class, fields = {
@FieldResult(name = "id", column = "id"), @FieldResult(name = "name", column = "name") }) })
@NamedNativeQuery(name = "findCustomer", query = "select * from t_customer", resultSetMapping = "customerSetMapping")
@Proxy(lazy = true)
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; // 主鍵
private String name; // 姓名
@OneToMany(targetEntity = Order.class, mappedBy = "c")
private Set<Order> orders = new HashSet<Order>();
@Entity
@Table(name = "t_order")
@NamedQuery(name="findOrderByCustomer",query="from Order where c=:c")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Double money;
private String receiverInfo; // 收貨地址
// 訂單與客戶關聯
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "c_customer_id")
@Cascade(CascadeType.SAVE_UPDATE)
private Customer c;
複製程式碼
public class HQLTest {
// 命名查詢
@Test
public void test9() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.我要查詢張龍這個客戶的訂單
Customer c = session.get(Customer.class, 1);
Query query = session.getNamedQuery("findOrderByCustomer"); // from Order where c=:c
// 2.現在hql它的引數是一個實體
List<Order> list = query.setEntity("c", c).list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 命名查詢
@Test
public void test8() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Query query = session.getNamedQuery("myHql");
List<Customer> list = query.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 投影查詢
@Test
public void test7() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.查詢出所有的Customer的name
// String hql = "select name from Customer";
// List list = session.createQuery(hql).list();
// System.out.println(list); // [張龍, 張三丰]
// 如果只查詢一個列,得到的結果List<Object>
// 2.查詢所有的Customer的id,name
// String hql = "select id,name from Customer";
// List<Object[]> list = session.createQuery(hql).list();
// for(Object[] objs:list){
// for(Object obj:objs){
// System.out.print(obj+" ");
// }
// System.out.println();
// }
// 如果是查詢多列,得到的結果是List<Object[]>
// 3.使用投影將查詢的結果封裝到Customer物件
String hql = "select new Customer(id,name) from Customer"; // 必須在PO類中提供對應的構造方法
List<Customer> cs = session.createQuery(hql).list();
System.out.println(cs);
session.getTransaction().commit();
session.close();
}
// 分組統計操作
@Test
public void test6() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 統計操作----統計一共有多少訂單 count
// String hql="select count(*) from Order";
// Object count = session.createQuery(hql).uniqueResult();
// System.out.println(count);
// 分組統計----每一個人的訂單總價
String hql = "select sum(money) from Order group by c";
List list = session.createQuery(hql).list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 分頁檢索
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Query query = session.createQuery("from Order");
// 每頁顯示6條件 ,我們要得到第二頁資料
query.setFirstResult((2 - 1) * 6); // 設定開始位置
query.setMaxResults(6); // 設定條數
List<Order> list = query.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 條件查詢
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.根據位置來繫結引數
// 1.1建立hql
// String hql = "from Order where money>? ";
// 1.2.執行hql
// List<Order> list = session.createQuery(hql).setParameter(0,
// 2000d).list();
// 可以使用例如setString() setDouble這樣的方法去新增引數,引數的序號是從0開始.
// 2.根據名稱來繫結
// 1.1建立hql
String hql = "from Order where money>:mymoney ";
// 1.2.執行hql
List<Order> list = session.createQuery(hql).setParameter("mymoney", 2000d).list();
// 可以使用例如setString() setDouble這樣的方法去新增引數
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 排序檢索--//查詢訂單,根據訂單的價格進行排序
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.定義hql
String hql = "from Order order by money desc"; // desc 降序 預設是asc 升序
// 2.執行hql查詢訂單,根據價格進行排序
List<Order> list = session.createQuery(hql).list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 基本檢索
@Test
public void test2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.編寫HQL
String hql = "from Customer"; // from是關鍵字,後面是類名,關鍵字是不區分大小寫,但是類名是區分
// 2.通過session.createQuery(hql)
// Query query = session.createQuery(hql);
// 3.通過list方法得到資料
// List<Customer> list = query.list();
List<Customer> list = session.createQuery(hql).list();
System.out.println(list.get(0));
session.getTransaction().commit();
session.close();
}
// 準備資料(2個Customer,每一個Customer有10個Order)
@Test
public void test1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 操作
Customer c = new Customer();
c.setName("張三丰");
for (int i = 0; i < 10; i++) {
Order order = new Order();
order.setMoney(2000d + i * 10);
order.setReceiverInfo("上海");
order.setC(c);
session.save(order);
}
session.getTransaction().commit();
session.close();
}
}
複製程式碼
QBC
QBC(query by criteria),它是一種更加物件導向的檢索方式。 QBC步驟
- 通過Session得到一個Criteria物件 session.createCriteria()
- 設定條件 Criterion例項 它的獲取可以通過Restrictions類提供靜態。 Criteria的add方法用於新增查詢條件
- 呼叫list進行查詢 criterfia.list.
public class QBCTest {
// 離線的檢索
@Test
public void test6() {
// 1.得到一個DetachedCriteria
DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
dc.add(Restrictions.like("name", "張_"));
// 2.生成Criteria執行操作
Session session = HibernateUtils.openSession();
session.beginTransaction();
Criteria criteria = dc.getExecutableCriteria(session);
List<Customer> list = criteria.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 統計檢索
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.統計訂單總數
Criteria criteria = session.createCriteria(Order.class);
// Object obj =
// criteria.setProjection(Projections.rowCount()).uniqueResult();
// //統計總行數 count(id)
// System.out.println(obj);
// 2.訂單的總價格----分組統計根據客戶
// criteria.setProjection(Projections.sum("money")); //統計總金額
criteria.setProjection(
Projections.projectionList().add(Projections.sum("money")).add(Projections.groupProperty("c")));
List<Object[]> list = criteria.list(); // 在這個集合中儲存的是Object[money的統計資訊,客戶資訊]
for (Object[] objs : list) {
for (Object obj : objs) {
System.out.println(obj);
}
}
session.getTransaction().commit();
session.close();
}
// 分頁檢索
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult((2 - 1) * 6);
criteria.setMaxResults(6);
List<Order> list = criteria.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 條件檢索
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.查詢名稱叫張某客戶 張_
Criteria criteria = session.createCriteria(Customer.class);
Criterion like = Restrictions.like("name", "張_"); // 其它的條件 lt < gt > le
// <= ge>= eq==
criteria.add(like);// 新增條件
Customer c = (Customer) criteria.uniqueResult();
System.out.println(c);
// 2.查詢訂單價格在1050以上的,並且它的客戶是張某
Criteria cri = session.createCriteria(Order.class);
SimpleExpression lt = Restrictions.gt("money", 1050d); // >1050
SimpleExpression eq = Restrictions.eq("c", c); // 與c客戶相同
LogicalExpression and = Restrictions.and(lt, eq);
cri.add(and);
// List<Order> orders =
// cri.add(Restrictions.and(Restrictions.gt("money", 1050d),
// Restrictions.eq("c", c))).list();
List<Order> orders = cri.list();
System.out.println(orders);
session.getTransaction().commit();
session.close();
}
// 排序檢索
@Test
public void test2() {
// 查詢訂單資訊 根據訂單的價格進行排序
Session session = HibernateUtils.openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Order.class);
// 指定排序
// criteria.addOrder(org.hibernate.criterion.Order.desc("money")); // 降序
criteria.addOrder(org.hibernate.criterion.Order.asc("money")); // 升序
List<Order> list = criteria.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
// 基本檢索
@Test
public void test1() {
// 查詢所有Customer
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 1.得到一個Criteria物件
Criteria criteria = session.createCriteria(Customer.class);
// 2.呼叫list方法
List<Customer> list = criteria.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
}
複製程式碼
本地SQL
//測試本地sql命名查詢
@Test
public void test2(){
Session session = HibernateUtils.openSession();
session.beginTransaction();
Query query = session.getNamedQuery("findCustomer");
List list = query.list();
System.out.println(list);
session.getTransaction().commit();
session.close();
}
複製程式碼
多表操作
HQL多表操作
Hql多表操作分類
- 交叉連線
- 內連線
a) 顯示內連線
b) 隱式內連線
c) 迫切內連線
- 外連線
左外連線
迫切左外連線
右外連線注意:在hibernate中有迫切連線的概念,而sql中沒有。
//hql多表查詢
public class HQLJoinTest {
// 演示迫切左外連線
@Test
public void test5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 注意:fetch不可以與單獨條件的with一起使用
List<Customer> list = session
.createQuery("select distinct c from Customer c left outer join fetch c.orders where c.id=1").list(); // 左外連線
for (Customer c : list) {
System.out.println(c);
}
session.getTransaction().commit();
session.close();
}
// 演示外連線
@Test
public void test4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
List<Object[]> list = session.createQuery("from Customer c left outer join c.orders").list(); // 左外連線
for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
}
session.getTransaction().commit();
session.close();
}
// 迫切內連線
@Test
public void test3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 迫切內連線 inner join fetch 注意:使用迫切連線結果可能出現重複,所以要使用distinct來去重複
String hql = "select distinct c from Customer c inner join fetch c.orders";
// 底層也是執行的inner join 只不過結果封裝到物件中。
Query query = session.createQuery(hql);
List<Customer> list = query.list(); // 結果是List<>,集合中裝入的From後面的物件。
for (Customer o : list) {
System.out.println(o);
}
session.getTransaction().commit();
session.close();
}
// 測試隱式內連線
@Test
public void test2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// sql連線 select * from t_customer,t_order where 條件
String hql = "from Order o where o.c.id=1";
Query query = session.createQuery(hql);
List list = query.list();
System.out.println(list);
/*
String hql = "from Customer c,Order o where c.id=1 and c.id = o.customer.id";
Query query = session.createQuery(hql);
List<Object[]> list = query.list();
for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
}
*/
session.getTransaction().commit();
session.close();
}
// 測試顯示內連線
@Test
public void test1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// sql連線 select * from t_customer inner join t_order on 條件
String hql = "from Customer c inner join c.orders with c.id=1";
Query query = session.createQuery(hql);
List<Object[]> list = query.list(); // 結果是List<Object[]>
// 而Object[]中裝入的是Customer與Order物件。
for (Object[] objs : list) {
for (Object obj : objs) {
System.out.print(obj + "\t");
}
System.out.println();
}
session.getTransaction().commit();
session.close();
}
}
複製程式碼
九、Hibernate事務管理
Hibernate中設定事務隔離級別
在hibernate.cfg.xml檔案中配置
<!-- 設定事務隔離級別 -->
<property name="hibernate.connection.isolation ">4</property>
複製程式碼
它可取的值有 1 2 4 8
1.代表的事務隔離級別為READ UNCOMMITTED 2.代表的事務隔離級別為READ COMMITTED 4.代表的事務隔離級別為 REPEATABLE READ 8.代表的事務隔離級別為 SERIALIZABLE
Hibernate中session管理
Hibernate提供了三種管理session的方式,在實際開發中我們一般使用的是前兩種。
- Session物件的生命週期與本地執行緒繫結(ThreadLocal)
- Session物件的生命週期與JTA事務繫結(分散式事務管理)
- Hibernate委託程式來管理Session的生命週期
本地執行緒繫結Session
- 需要在hibernate.cfg.xml檔案配置
- 在獲取session時不要在使用openSession而是使用getCurrentSession()方法。
<property name="hibernate.current_session_context_class">thread</property>
Session currentSession = sessionFactory.getCurrentSession();
複製程式碼
使用getCurrentSession獲取的與執行緒繫結的session物件,在事務關閉時,session物件也會close,簡單說,就不需要我們在手動close。
十、Hibernate優化方案
HQL優化
1.使用引數繫結 2.儘量少使用NOT 3.儘量使用where來替換having 4.減少對錶的查詢 5.使用表的別名 6.實體的更新與刪除
檢索策略
延遲載入
延遲載入 是hibernate為提高程式執行的效率而提供的一種機制,即只有真正使用該物件的資料時才會建立。
load方法採用的策略延遲載入、 get方法採用的策略立即載入。
檢索策略分為兩種:
- 類級別檢索
- 關聯級別檢索
類級別檢索
類級別檢索是通過session直接檢索某一類對應的資料,例如 Customer c=session.load(Customer.class,1) Session.createQuery(“from Order”)
類級別檢索策略分為立即檢索與延遲檢索,預設是延遲檢索,類級別的檢索策略可以通過<class>
元素的lazy屬性來設定 ,預設值是true。如果將lazy設定為false,代表類級別檢索也使用立即檢索。這時load與get就一樣,都是立即檢索。
關聯級別檢索
查詢到某個物件,獲得其關聯的物件或屬性,這種稱為關聯級別檢索,例如 c.getOrders().size() c.getName() 對於關聯級別檢索我們就要研究其檢索策略(抓取策略)
抓取策略
抓取策略介紹
指的是查詢到某個物件後,通過這個物件去查詢關聯物件的資訊時的一種策略。
註解配置抓取策略
@Entity
@Table(name = "t_customer")
@Proxy(lazy = false)
@BatchSize(size=3)
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; // 主鍵
private String name; // 姓名
@OneToMany(targetEntity = Order.class, mappedBy = "c")
@Fetch(FetchMode.SELECT)
@LazyCollection(LazyCollectionOption.TRUE)
@BatchSize(size=3)
private Set<Order> orders = new HashSet<Order>();
@Entity
@Table(name = "t_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Double money;
private String receiverInfo; // 收貨地址
// 訂單與客戶關聯
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "c_customer_id")
@Cascade(CascadeType.SAVE_UPDATE)
@Fetch(FetchMode.JOIN)
@LazyToOne(LazyToOneOption.FALSE)
private Customer c;
複製程式碼
set上的fetch與lazy
set上的fetch與lazy它主要是用於設定關聯的集合資訊的抓取策略。根據一的一方去查詢多的一方 Fetch可取值有:
- SELECT 多條簡單的sql (預設值)
- JOIN 採用迫切左外連線。如果fetch選擇的是join方案,那麼lazy它會失效。
- SUBSELECT 將生成子查詢的SQL
lazy可取值有:
- TURE 延遲檢索 (預設值)
- FALSE 立即檢索
- EXTRA 加強延遲檢索(及其懶惰)
One的上的fetch與lazy
在多的一方如何查詢一的主方資訊。例如:獲取到一個訂單物件,要查詢客戶資訊。
Fetch可取值
- select 預設值,代表傳送一條或多條簡單的select語句
- join 傳送一條迫切左外連線。如果fetch值為join,那麼lazy失效。
lazy可取值
- false 不採用延遲載入
- proxy 預設值 是否採用延遲,需要另一方的類級別延遲策略來決定
- no-proxy 不用研究
批量抓取
我們在查詢多個物件的關聯物件時,可以採用批量抓取方式來對程式進行優化。
要想實現批量抓取,可以在配置檔案中 batch-size屬性來設定 ,可以使用註解 @BatchSize(size=4)。
無論是根據哪一方來查詢別一方,在進行批量抓取時,都是在父方來設定,如果是要查詢子資訊,那麼我們是在上來設定batch-size,如果是從子方來查詢父方, 也是在父方設定在設定batch-size。