Hibernate快速入門筆記

smlxs_lbb發表於2018-04-09

[toc]

Hibernate它是一個輕量級的jdbc封裝,也就是說,我們可以使用hibernate來完成原來我們使用jdbc完成操作,就是與資料庫的互動操作。它是在dao層去使用的。

物件關係對映(英語:Object Relation Mapping,簡稱ORM,或O/RM,或O/R mapping)。簡單說,我們使用orm可以將我們的物件與我們的類去進行對映,使的我們可以去操作物件就完成對錶的操作。

一、Hibernate檔案目錄結構

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的核心配置檔案它有兩種方式:

  1. hibernate.cfg.xml
  2. hibernate.properties

我們在開發中使用比較多的是hibernate.cfg.xml這種方式,原因它的配置能力更強,易於修改 我們主要學習的是hibernate.cfg.xml配置

  1. 可以載入資料庫相關資訊
  2. hibernate相關配置
  3. 載入對映配置檔案
<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> 
複製程式碼
  1. 統一宣告包名,這樣在<class>中就不需要寫類的全名. <hibernate-mapping package="cn.itheima.domain">
  2. 關於<class>標籤配置 name屬性:類的全名稱 table 表的名稱,可以省略,這時表的名稱就與類名一致 catalog屬性:資料庫名稱 可以省略.如果省略,參考核心配置檔案中url路徑中的庫名稱
  3. 關於<id>標籤 首先它必須存在。<id>是用於建立類中的屬性與表中的主鍵對映。 name 類中的屬性名稱 column 表中的主鍵名稱 column它也可以省略,這時列名就與類中屬性名稱一致 length 欄位長度 type屬性 指定型別 <generator>它主要是描述主鍵生成策略.
  4. 關於<property>標籤 它是描述類中屬性與表中非主鍵的對映關係

關於hibernate的對映檔案中型別問題

對於type屬性它的取值,可以有三種:

  1. java中的資料型別
  2. hibernate中的資料型別
  3. SQL的資料型別

Hibernate快速入門筆記

三、Hibernate的執行原理和常用API

hibernate工作原理

  1. 通過Configuration().configure();讀取並解析hibernate.cfg.xml配置檔案。
  2. 由hibernate.cfg.xml中的<mappingresource="com/xx/User.hbm.xml"/>讀取解析對映資訊。
  3. 通過config.buildSessionFactory();//得到sessionFactory。
  4. sessionFactory.openSession();//得到session。
  5. session.beginTransaction();//開啟事務。
  6. persistent operate;
  7. session.getTransaction().commit();//提交事務
  8. 關閉session;
  9. 關閉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連線池,應該怎樣處理?

  1. 我們要匯入c3p0的相關jar包 在hibernate/lib/options下有關於c3p0連線池jar包

  2. 在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結果

  1. 在Customer類中生成以name,address為引數的構造,注意,無引數構造也要有。
  2. 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編寫規則

  1. 必須提供一個無引數的public構造方法
  2. 所有屬性要private ,對外提供public 的get/set方法
  3. 在PO類必須提供一個標識屬性,讓它與資料庫中的主鍵對應,我們管這個屬性叫OID
  4. PO類中的屬性儘量使用基本資料型別的包裝類. Int-Integer double--Double float-Float
  5. PO類它不能使用final修飾符

OID作用

OID指的是與資料庫中表的主鍵對應的屬性。

Hibernate框架它是通過OID來區分不同的PO物件,如果在記憶體中有兩個相同的OID物件,那麼hibernate認為它們是同一個物件。

為什麼PO類屬性它要使用包裝型別

使用基本資料型別是沒有辦法去描述不存在概念,如果使用包裝型別,它就是一個物件,對於物件它的預設值是null。

PO類不可以使用final修飾?(hibernate中的get/load方法的區別)

  1. get直接得到了一個持久化型別物件,它就是立即查詢操作。 load它得到的是持久化類開的代理型別物件(子類物件)。它預設採用了一種延遲策略來查詢資料。

  2. 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持久化物件狀態

持久化物件三種狀態

  1. 瞬時態:也叫做臨時態或自由態,它一般指我們new出來的物件,它不存在OID,與hibernate session無關聯,在資料庫中也無記錄。它使用完成後,會被jvm直接回收掉,它只是用於資訊攜帶。 簡單說:無OID 與資料庫中的資訊無關聯,不在session管理範圍內。

  2. 持久態:在hibernate session管理範圍內,它具有持久化標識OID它的特點,在事務未提交前一直是持久態,當它發生改變時,hibernate是可以檢測到的。 簡單說:有OID 由session管理,在資料庫中有可能有,也有可有沒有。

  3. 託管態:也叫做遊離態或離線態,它是指持久態物件失去了與session的關聯,託管態物件它存在OID,在資料庫中有可能存在,也有可能不存在。 對於託管態物件,它發生改變時hibernet不能檢測到。

持久化類三種狀態切換

判斷持久化類物件三種狀態:

  1. 是否有OID
  2. 判斷是否與session關聯

Hibernate快速入門筆記

  1. 瞬時態(new 出來的) 瞬時------持久 save saveOrUpdate 瞬時-----脫管(遊離) 手動設定oid

  2. .持久態 它是由session管理 持久-------瞬時 delete() 被刪除後持久化物件不在建議使用 持久-----脫管 注意:session它的快取就是所說的一級快取 evict(清除一級快取 中指定的一個物件) clear(清空一級快取) close(關閉,清空一級快取)

  3. .脫管態 (它是無法直接獲取) 脫管-----瞬時 直接將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的一級快取存在的目的就是為了減少對資料庫訪問。

持久化物件具有自動更新資料庫能力

Hibernate快速入門筆記

一級快取常用API

一級快取特點

  1. 當我們通過session的save,update saveOrupdate進行操作時,如果一級快取中沒有物件,會將這些物件從資料庫中查詢到,儲存到一級快取。
  2. 當我們通過session的load,get,Query的list等方法進行操作時,會先判斷一級快取中是否存在,如果沒有才會從資料庫獲取,並且將查詢的資料儲存到一級快取中。
  3. 當呼叫session的close方法時,session快取清空。 clear 清空一級快取. evict 清空一級快取中指定的一個物件。 refresh重新查詢資料庫,用資料庫中資訊來更新一級快取與快照

Hibernate常用API-Session補充

update

  1. udpate操作它主要是針對於脫管物件,持久物件具有自動更新能力。
  2. Update操作時,如果物件是一個脫管物件,可以操作,它會將脫管物件轉換成持久物件在操作。
  3. 如果在session中出現相同的oid兩個物件,會產生異常。
  4. 脫管物件的oid如果在資料表中不存在,會報異常。
  5. 所以:在操作中,建議我們通過持久化物件來直接修改其操作。

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設定在主表的一方,外來鍵在哪一個表中,我們就讓哪一方來維護外來鍵。

物件導航

Hibernate快速入門筆記

cascade總結

使用cascade可以完成級聯操作 它可常用取值

  1. none這是一個預設值
  2. save-update,當我們配置它時,底層使用save update或save-update完成操作,級聯儲存臨時物件,如果是遊離物件,會執行update.
  3. delete 級聯刪除
  4. delete-ophan 刪除與當前物件解除關係的物件。
  5. all 它包含了save-update delete操作
  6. 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快速入門筆記

Hibernate關聯對映-一對一

一對一操作有兩種對映方式:

  1. 在任意一方新增外來鍵
  2. 主鍵對映

建立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操作。

  1. 導航物件圖檢索方式,根據已載入的物件導航到其它物件
  2. OID檢索方式,按照物件的OID來檢索物件
  3. HQL檢索方式,使用物件導向的HQL查詢語言
  4. QBC檢索方式,使用QBC(Query by Criteria)API來檢索物件,這種API封裝了基於字串形式的查詢語句,提供了更加物件導向的查詢介面
  5. 本地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查詢。 基本步驟:

  1. 得到Session
  2. 編寫HQL語句
  3. 通過session.createQuery(hql)建立一個Query物件
  4. 為Query物件設定條件引數
  5. 執行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步驟

  1. 通過Session得到一個Criteria物件 session.createCriteria()
  2. 設定條件 Criterion例項 它的獲取可以通過Restrictions類提供靜態。 Criteria的add方法用於新增查詢條件
  3. 呼叫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多表操作分類

  1. 交叉連線
  2. 內連線 a) 顯示內連線 b) 隱式內連線 c) 迫切內連線
  3. 外連線 左外連線 迫切左外連線 右外連線 注意:在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的方式,在實際開發中我們一般使用的是前兩種。

  1. Session物件的生命週期與本地執行緒繫結(ThreadLocal)
  2. Session物件的生命週期與JTA事務繫結(分散式事務管理)
  3. Hibernate委託程式來管理Session的生命週期

本地執行緒繫結Session

  1. 需要在hibernate.cfg.xml檔案配置
  2. 在獲取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方法採用的策略立即載入。

檢索策略分為兩種:

  1. 類級別檢索
  2. 關聯級別檢索

類級別檢索

類級別檢索是通過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可取值有:

  1. SELECT 多條簡單的sql (預設值)
  2. JOIN 採用迫切左外連線。如果fetch選擇的是join方案,那麼lazy它會失效。
  3. SUBSELECT 將生成子查詢的SQL

lazy可取值有:

  1. TURE 延遲檢索 (預設值)
  2. FALSE 立即檢索
  3. EXTRA 加強延遲檢索(及其懶惰)

One的上的fetch與lazy

在多的一方如何查詢一的主方資訊。例如:獲取到一個訂單物件,要查詢客戶資訊。

Fetch可取值

  1. select 預設值,代表傳送一條或多條簡單的select語句
  2. join 傳送一條迫切左外連線。如果fetch值為join,那麼lazy失效。

lazy可取值

  1. false 不採用延遲載入
  2. proxy 預設值 是否採用延遲,需要另一方的類級別延遲策略來決定
  3. no-proxy 不用研究

批量抓取

我們在查詢多個物件的關聯物件時,可以採用批量抓取方式來對程式進行優化。

要想實現批量抓取,可以在配置檔案中 batch-size屬性來設定 ,可以使用註解 @BatchSize(size=4)。

無論是根據哪一方來查詢別一方,在進行批量抓取時,都是在父方來設定,如果是要查詢子資訊,那麼我們是在上來設定batch-size,如果是從子方來查詢父方, 也是在父方設定在設定batch-size。

相關文章