全面解析Hibernate關(guān)聯(lián)操作、查詢操作、高級特性、并發(fā)處理機制
本文所需的數(shù)據(jù)庫初始文件,Hibernate常用操作的完整示例代碼(包含所有Hibernate操作所需jar文件)提供下載學(xué)習(xí):http://download.csdn.net/detail/daijin888888/9551724
1、Hibernate關(guān)聯(lián)映射
1)什么是關(guān)聯(lián)映射?
如果表之間具有關(guān)聯(lián)關(guān)系,Hibernate允許我們在hbm.xml中描述他們的關(guān)聯(lián)關(guān)系,然后在我們操作其中一張表時,自動的根據(jù)這種關(guān)系操作到另外的關(guān)系表,那么這種關(guān)聯(lián)關(guān)系的設(shè)置,我們稱之為關(guān)聯(lián)映射。
2)關(guān)聯(lián)映射的好處?
一次訪問可以關(guān)聯(lián)操作多張表
--關(guān)聯(lián)查詢出關(guān)系表的數(shù)據(jù)
--關(guān)聯(lián)新增、修改關(guān)系表的數(shù)據(jù)
--關(guān)聯(lián)刪除關(guān)系表的數(shù)據(jù)
3)關(guān)聯(lián)映射實現(xiàn)步驟
--了解表之間的關(guān)系,且明確關(guān)系字段
--在實體類中追加關(guān)聯(lián)屬性,用于封裝關(guān)聯(lián)的數(shù)據(jù)
--需要配置hbm.xml文件,設(shè)置關(guān)聯(lián)關(guān)系
*2、一對多關(guān)聯(lián)
例:假如一個Account有多條Service記錄,希望在查詢account數(shù)據(jù)時,自動連帶著查詢出它對應(yīng)的service數(shù)據(jù)。
1)表關(guān)系
一對多的關(guān)系,關(guān)系字段是service.account_id。(即Service表中要有與Account表關(guān)聯(lián)的字段account_id,該字段對應(yīng)Account表中的id)
2)追加關(guān)系屬性
在Account類中,追加關(guān)聯(lián)屬性services,其類型為Set<Service>。
private Set<Service> services = new HashSet<Service>();
3)設(shè)置關(guān)聯(lián)關(guān)系
--語法
在Account.hbm.xml中,設(shè)置一對多關(guān)系
<set name="關(guān)聯(lián)屬性名"> <key column="關(guān)聯(lián)字段名"/> <one-to-many class="關(guān)聯(lián)表的實體類名"/> </set> --實現(xiàn) <set name="services"> <key column="account_id"/> <one-to-many class="com.*.entity.Service"/> </set>
4)示例代碼
已知:
--關(guān)聯(lián)屬性services
--關(guān)聯(lián)字段account_id
--關(guān)聯(lián)對象Service --> 表名service
<span style="font-size:14px;">public void testFind() { Session session = HibernateUtil.getSession(); Account a = (Account) session.load(Account.class, 1010); System.out.println("---顯示Account賬號信息---"); System.out.println(a.getId() + " " + a.getRealName() + " " + a.getIdcardNo()); System.out.println("---顯示當(dāng)前賬號下的業(yè)務(wù)賬號---"); Set<Service> services = a.getServices(); for (Service s : services) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } System.out.println(a.toString()); }</span>
*3、多對一關(guān)聯(lián)
例:希望在查詢service數(shù)據(jù)后,可以自動的查詢出它對應(yīng)的account數(shù)據(jù)。
1)關(guān)聯(lián)關(guān)系
service與account具有多對一的關(guān)系,關(guān)系字段為service.account_id
2)追加關(guān)聯(lián)屬性
--在Service實體類中追加關(guān)聯(lián)屬性account,其類型為Account。
private Account account;
此后accountId屬性可以去掉,可以通過getAccount().getId()方法來得到account_id的值
3)設(shè)置關(guān)聯(lián)關(guān)系
a、語法:
--在Service.hbm.xml中,追加關(guān)聯(lián)關(guān)系的配置
--<many-to-one name="關(guān)聯(lián)屬性名"
column="關(guān)系字段名"
class="關(guān)聯(lián)表的實體類名"/>
b、實現(xiàn):
<many-to-one name="account"
column="account_id"
class="com.*.entity.Account"/>
4)示例代碼
已知:
--關(guān)聯(lián)屬性account
--關(guān)聯(lián)字段account_id
--關(guān)聯(lián)對象Account --> 表名account,主鍵id
<span style="font-size:14px;">public void testFind() { Session session = HibernateUtil.getSession(); Service s = (Service) session.get(Service.class, 2002); System.out.println("----顯示業(yè)務(wù)賬號信息---"); System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); System.out.println("----顯示相關(guān)賬務(wù)賬號信息---"); System.out.println(s.getAccount().getId() + " " + s.getAccount().getRealName()); }</span>
*4、關(guān)聯(lián)操作
1)關(guān)聯(lián)查詢
如果需要當(dāng)前對象和關(guān)聯(lián)屬性一起采用一個SQL語句實例化,可以采用下面方法:
a、(不推薦)修改hbm.xml中的關(guān)聯(lián)屬性映射
lazy屬性:
true表示啟用延遲加載;
false表示關(guān)閉延遲加載
fetch屬性:
join表示采用連接方法與主對象一起查詢,此時,lazy="false"失效;
select(默認(rèn))表示單獨發(fā)送一個SQL查詢關(guān)聯(lián)數(shù)據(jù)
b、(推薦)通過HQL及join fetch語法
--from Account a join fetch a.services where a.id=?
含義:在查詢Account對象時,將services關(guān)聯(lián)屬性數(shù)據(jù)采用表連接方式一并查出來。
--from Service s join fetch s.account where s.id=?
含義:在查詢Service對象時,將account關(guān)聯(lián)屬性數(shù)據(jù)采用表連接方式一并查出來。
--from Service s join fetch s.account where s.account.id=?
注意:
--HQL中寫的是對象和屬性
--join fetch后面沒有on子句,而fetch的是關(guān)聯(lián)屬性
--query.setInteger來設(shè)置整型參數(shù)值,下標(biāo)從0開始。
--如果明確該HQL只會返回一條記錄,可以使用query.uniqueResult()方法返回唯一的記錄
c、示例代碼(改寫上述一對多關(guān)聯(lián)的查詢代碼)
<span style="font-size:14px;">public void testFind() { Session session = HibernateUtil.getSession(); // Account a = (Account) session.load(Account.class, 1010); String hql = "from Account a join fetch a.services where a.id=?"; Query query = session.createQuery(hql); query.setInteger(0, 1010);// ?從0開始 Account a = (Account) query.uniqueResult();// 單行查詢可采用 System.out.println("---顯示Account賬號信息---"); System.out.println(a.getId() + " " + a.getRealName() + " " + a.getIdcardNo()); System.out.println("---顯示當(dāng)前賬號下的業(yè)務(wù)賬號---"); Set<Service> services = a.getServices(); for (Service s : services) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } System.out.println(a.toString()); }</span>
2)級聯(lián)添加、級聯(lián)修改
a、當(dāng)表具有關(guān)聯(lián)關(guān)系時,Hibernate不僅僅提供關(guān)聯(lián)查詢的功能,還具有關(guān)聯(lián)添加、修改、刪除關(guān)聯(lián)表中數(shù)據(jù)的能力,這種能力稱之為級聯(lián)操作。
b、如何實現(xiàn)級聯(lián)操作
需要在關(guān)聯(lián)屬性設(shè)置的位置,追加屬性cascade
--none:默認(rèn)不支持級聯(lián)
--save-update:支持級聯(lián)添加與更新
--delete:支持級聯(lián)刪除
--all:支持級聯(lián)添加、更新、刪除
c、說明
通常1對多的表關(guān)系,1的一方是主表,多的一方是從表,往往是需要在添加、更新、刪除主表時連帶添加、更新、刪除從表的數(shù)據(jù)。如:刪除賬務(wù)賬號時,要連帶刪除業(yè)務(wù)賬號的數(shù)據(jù)
3)級聯(lián)刪除
a、設(shè)置cascade="delete"或cascade="all"就可以支持級聯(lián)刪除
b、通常情況下,需要在1的一方set標(biāo)簽處,追加屬性inverse="true"
c、session.delete(obj);obj需要是持久對象,不能new,需要load/get取出
d、批量刪除的方法:
級聯(lián)刪除采用n+1個delete語句清除主表和外鍵表的關(guān)聯(lián)數(shù)據(jù)。
如果是批量刪除,不推薦級聯(lián)刪除,建議采用HQL編寫delete刪除語句。
delete from Service where account.id=?//刪除Service表中所有account.id=?的數(shù)據(jù),(該句替代級聯(lián)操作中的n個delete語句)
delete from Account where id=?
4)inverse屬性(理解)詳細(xì)說明點這里
是否交出關(guān)系維護(hù)的控制權(quán)。即默認(rèn)情況下,Account和Service對象之間的關(guān)系由雙方負(fù)責(zé)維護(hù)。意思是對Account或Service對象做級聯(lián)操作時,需要執(zhí)行update語句將關(guān)聯(lián)字段設(shè)置成相同ID。如果需要取消某一方的關(guān)系維護(hù)工作,可以在關(guān)聯(lián)屬性部分添加inverse="true"設(shè)置,這樣可以避免update語句執(zhí)行。
true:交出控制權(quán),當(dāng)前的對象不負(fù)責(zé)維護(hù)兩張表的關(guān)聯(lián)關(guān)系
false:不交出控制權(quán),當(dāng)前的對象要負(fù)責(zé)維護(hù)兩張表的關(guān)聯(lián)關(guān)系
提示:往往是一的一方(即<one-to-many>映射部分)設(shè)置為inverse="true",這樣在對一方級聯(lián)操作時,可以避免大量的update更新語句。
*5、多對多關(guān)聯(lián)
例:管理員admin_info和角色role_info具有多對多的關(guān)系,希望在查詢管理員時能夠連帶著查詢出他對應(yīng)的角色。 數(shù)據(jù)庫設(shè)計時,需采用3張表表示。
ADMIN_INFO(管理員)
ADMIN_ROLE(管理員和角色關(guān)系)
ROLE(角色)
1)關(guān)系字段
關(guān)系字段位于他們的中間表admin_role中,
admin_id=admin_info.id
role_id=role_info.id
2)追加關(guān)聯(lián)屬性在管理員的實體類中追加角色相關(guān)屬性
Set<Role> roles
3)在Admin.hbm.xml中追加關(guān)聯(lián)映射配置
--語法 <set name="關(guān)聯(lián)屬性名" table="中間表名"> <key column="Admin的關(guān)聯(lián)字段名"/> <many-to-many class="關(guān)聯(lián)表的實體類名" column="關(guān)聯(lián)表的關(guān)系字段名"/> </set> --代碼 <set name="roles" table="admin_role"> <key column="admin_id"/> <many-to-many class="com.*.entity.Role" column="role_id"/> </set>
4)級聯(lián)操作
cascade表示支持級聯(lián)操作,操作的是另一方的表,而并不是表示級聯(lián)操作中間表。對于中間表的維護(hù),不需要寫cascade屬性。
5)inverse
通常情況下,多對多關(guān)聯(lián),不需要寫inverse="true",原因是另一方在插入數(shù)據(jù)時,可能沒有管中間表的數(shù)據(jù),需要當(dāng)前的一方來維護(hù),因此不能寫inverse="true",否則的話,雙方都不維護(hù)這個關(guān)系,數(shù)據(jù)上有問題。
6)示例Java代碼
<span style="font-size:14px;"> // 移除角色 @Test public void testDeleteRole() { Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); try { Admin a = (Admin) session.load(Admin.class, 1); Role r1 = (Role) session.load(Role.class, 1); a.getRoles().remove(r1); session.update(a); tx.commit(); } catch (HibernateException e) { e.printStackTrace(); tx.rollback(); } finally { session.close(); } } // 追加角色 @Test public void testAddRole() { Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); try { Admin a = (Admin) session.load(Admin.class, 1); Role r1 = (Role) session.load(Role.class, 1); Role r2 = (Role) session.load(Role.class, 43); Role r3 = (Role) session.load(Role.class, 44); a.getRoles().add(r1); a.getRoles().add(r2); a.getRoles().add(r3); session.update(a); tx.commit(); } catch (HibernateException e) { e.printStackTrace(); tx.rollback(); } finally { session.close(); } } @Test public void testFind() { Session session = HibernateUtil.getSession(); Transaction tx = session.beginTransaction(); try { Admin a = (Admin) session.load(Admin.class, 1); System.out.println("----顯示管理員信息---"); System.out.println(a.getId() + " " + a.getName() + " " + a.getTelephone()); System.out.println("----顯示管理員角色信息---"); for (Role role : a.getRoles()) { System.out.println(role.getName() + " "); } tx.commit(); } catch (HibernateException e) { e.printStackTrace(); tx.rollback(); } finally { session.close(); } }</span>
6、繼承關(guān)聯(lián)
例:在電商網(wǎng)站上搜索商品,比如輸入iphone來搜索,搜索結(jié)果中可以包含手機、手機膜、手機殼、充電器、耳機等等相關(guān)的產(chǎn)品信息。這種功能,我們可以在設(shè)計表時用一種特殊的一對一的關(guān)系來表現(xiàn)。即將所有商品通用的屬性提取到一張公共的表中product,具體商品表中只保存該商品特有的屬性,那么具體商品表和product表具有一對一的關(guān)系。在搜索時只搜索product表,就可以將相關(guān)的信息模糊查找出來。
--通用信息表product(id,name,price,desc)
--書的商品表book(id,authod,publishing,words)
--希望在操作book表時,能夠自動的將通用的字段維護(hù)到product表中。
1)明確表的關(guān)系
book與product具有一對一的關(guān)系,這種關(guān)系的目的是為了復(fù)用product表中的字段,像是一種繼承關(guān)系
2)實體類
Book extends Product
3)配置文件中體現(xiàn)關(guān)聯(lián)關(guān)系
--父類型同原先的配置文件寫法一致
--子類型具有特殊性
<joined-subclass name="類型名" table="表名" extends="父類名稱"> <key column="關(guān)聯(lián)字段名"/> <property name="" type="" column=""/> ... </joined-subclass>
4)繼承關(guān)系中,由于2張表具有類似于父子的關(guān)系,那么子表中必須引用父表中的數(shù)據(jù),不存在不引用的情況,即必須在維護(hù)子表同時維護(hù)父表。所以這是固定的情況,就不用寫cascade,inverse。
5)描述類別(了解)
<joined-subclass>數(shù)據(jù)庫有父類表和子類表
<union-subclass>數(shù)據(jù)庫有子類表、沒有父類表(子類表已包含父類表字段,無父類表,但有父類實體對象)
<subclass>數(shù)據(jù)庫有父類子類都用一個表(設(shè)計較亂,即沒有進(jìn)行表拆分,很少用)
*7、Hibernate查詢
1)*HQL查詢(Hibernate Query Language)
屬于面向?qū)ο蟛樵冋Z句,針對Hibernate映射過來的POJO進(jìn)行查詢,從而實現(xiàn)對數(shù)據(jù)庫的查詢。
a、以非主鍵做條件查詢
--條件參數(shù)以?表示,query.setString(0,"");
--條件參數(shù)以:x表示,query.setString("x","");
示例代碼:
<span style="font-size:14px;"> // 測試按非主鍵做條件查詢 @Test public void testFind1() { String hql = "from Service where account.id=? and unixHost=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setInteger(0, 1011); query.setString(1, "192.168.0.23"); List<Service> list = query.list(); for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } session.close(); } // 等價于testFind1,采用“:標(biāo)識符”替代 @Test public void testFind2() { String hql = "from Service where account.id=:aid and unixHost=:host"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setInteger("aid", 1011); query.setString("host", "192.168.0.23"); List<Service> list = query.list(); for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } session.close(); }</span>
b、只查詢一部分屬性
--默認(rèn)返回的集合中封裝的是Object[]
--new Service(id,unixHost,osUserName)使返回的結(jié)合中封裝的是Service對象,
注意:
Service中需要追加響應(yīng)的構(gòu)造器;
不要丟掉無參構(gòu)造器;
示例代碼:
<span style="font-size:14px;"> // 獲取部分字段結(jié)果,默認(rèn)采用Object[]封裝數(shù)據(jù) @Test public void testFind3() { String hql = "select s.id,s.unixHost,s.osUserName from Service s where s.account.id=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setInteger(0, 1011); List<Object[]> list = query.list(); for (Object[] objs : list) { System.out.println(objs[0] + " " + objs[1] + " " + objs[2] + " "); } session.close(); } // 等價于testFind3,需要添加對應(yīng)的構(gòu)造方法 @Test public void testFind4() { String hql = "select new Service(s.id,s.unixHost,s.osUserName) from Service s where s.account.id=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setInteger(0, 1011); List<Service> list = query.list(); for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } session.close(); }</span>
c、HQL定義在配置文件中(了解即可)
--通過query元素在配置文件中定義HQL
--query元素寫在class的后面
--session.getNamedQuery(HQL名);
示例代碼:<query/>與<class/>在hbm.xml中同級擺放
<span style="font-size:14px;"><query name="findAll"><!-- CDATA內(nèi)包括純文本字段,防止出現(xiàn)特殊字符 --> <![CDATA[ from Service ]]> </query></span> <span style="font-size:14px;"> // 將HQL定義到hbm.xml中(只適用靜態(tài)HQL語句結(jié)構(gòu)) @Test public void testFind5() { Session session = HibernateUtil.getSession(); // 獲取hbm.xml中<query>定義的hql語句 Query query = session.getNamedQuery("findAll"); List<Service> list = query.list(); for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } session.close(); }</span>
d、分頁查詢
--查詢記錄
query.setFirstResult((page-1)*pageSize);
query.setMaxResults(pageSize);
--查詢總頁數(shù)
select count(*) from Service
示例代碼:
<span style="font-size:14px;"> // 測試分頁查詢 @Test public void testFind6() { int page = 2; String hql = "from Service order by id"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); // 追加分頁參數(shù)設(shè)置 query.setFirstResult((page - 1) * 3);// 設(shè)置抓起記錄的起點,從0開始 query.setMaxResults(3);// 設(shè)置最大抓取數(shù)量 List<Service> list = query.list();// 執(zhí)行查詢,如果沒有分頁設(shè)置就查所有值 for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } session.close(); } // select count(*) from SERVICE @Test public void testFind7() { String hql = "select count(*) from Service"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); Long size = (Long) query.uniqueResult(); System.out.println("記錄總數(shù):" + size); session.close(); }</span>
e、關(guān)聯(lián)查詢(記住一種即可)
--from Service s,Account a
where s.account.id=a.id
--from Service s inner join s.account a
--select s.account.realName from Service s
總結(jié):
HQL與SQL的相同點:
--支持select,from,where,group,order by子句
--支持inner join,left join等連接
--支持>,<,>=,<=,in,not in,between,like等條件
--支持分組統(tǒng)計函數(shù)count,sum,max,min,avg
HQL與SQL的不同點:
--HQL語句區(qū)分大小寫,即大小寫敏感。關(guān)鍵字不區(qū)分。
--HQL中寫的是對象名和屬性名,而不是表名和字段名
--不支持join on中的on子句
--不支持select *
--不支持?jǐn)?shù)據(jù)庫函數(shù),比如日期函數(shù)to_date()、字符函數(shù)to_char()等
2)Criteria查詢(不夠直觀,了解即可)
使用Hibernate的API來拼一個HQL
Criteria c = session.createCriteria(Service.class);
示例代碼:
<span style="font-size:14px;"> // 使用Hibernate的API來拼一個HQL @Test public void testFind1() { Session session = HibernateUtil.getSession(); Criteria c = session.createCriteria(Service.class); c.add(Restrictions.and(Restrictions.like("osUserName", "huang%"), Restrictions.eq("unixHost", "192.168.0.26")));// 追加查詢條件 List<Service> list = c.list();// 執(zhí)行查詢,如果沒有分頁設(shè)置就查所有值 c.addOrder(Order.desc("id"));// 追加排序 // c.setFirstResult(arg0);//分頁 // c.setMaxResults(arg0); for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName() + " " + s.getUnixHost()); } session.close(); }</span>
3)SQL查詢
直接幫助我們調(diào)用JDBC來執(zhí)行SQL查詢
SQLQuery sqlQuery = session.createSQLQuery(sql);
示例代碼:
<span style="font-size:14px;"> @Test public void testFind1() { String sql = "select * from Service"; Session session = HibernateUtil.getSession(); SQLQuery sqlQuery = session.createSQLQuery(sql); sqlQuery.setFirstResult(0);// 分頁 sqlQuery.setMaxResults(3); // 默認(rèn)采用數(shù)組封裝一條記錄 List<Object[]> list = sqlQuery.list(); for (Object[] objs : list) { System.out.println(objs[0] + " " + objs[2]); } session.close(); } //同testFind1(),指定封裝記錄的實體 @Test public void testFind2() { String sql = "select * from Service"; Session session = HibernateUtil.getSession(); SQLQuery sqlQuery = session.createSQLQuery(sql); sqlQuery.setFirstResult(0);// 分頁 sqlQuery.setMaxResults(3); // 指定封裝記錄的實體類 sqlQuery.addEntity(Service.class); // 采用指定的Service類型封裝一條記錄 List<Service> list = sqlQuery.list(); for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName()); } session.close(); }</span>
8、Hibernate高級特性(了解)
1)二級緩存
a、二級緩存(默認(rèn)關(guān)閉)
--二級緩存是SessionFactory級別的緩存,由SessionFactory負(fù)責(zé)管理
--緩存的也是實體對象
--緩存數(shù)據(jù)可以被不同的Session間共享
--適用環(huán)境:對象數(shù)據(jù)頻繁共享;對象數(shù)據(jù)變化頻率小
b、二級緩存的使用步驟
--導(dǎo)緩存包ehcache.jar
--導(dǎo)入緩存配置文件ehcache.xml
--在hibernate.cfg.xml中,設(shè)置開啟二級緩存,并且設(shè)置緩存驅(qū)動類
<span style="font-size:14px;"><!-- 使用二級緩存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 指定二級緩存組件的驅(qū)動類ehcache.jar --> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property></span>
--在要緩存的POJO的關(guān)系映射文件hbm.xml中設(shè)置元素<cache usage="readonly"/>
示例代碼:
<span style="font-size:14px;"><span style="font-size:14px;"> @Test public void testFind1() { // 第一次查詢,使用session1 Session session1 = HibernateUtil.getSession(); Service s1 = (Service) session1.get(Service.class, 2002); System.out.println(s1.getOsUserName() + " " + s1.getUnixHost()); session1.close(); // 第二次查詢,使用session2(配置二級緩存后,兩次查詢均到二級緩存中取數(shù)據(jù)) // HibernateUtil.getSessionFactory().evict(Service.class);// 移除后則還是查詢兩次 Session session2 = HibernateUtil.getSession(); Service s2 = (Service) session2.get(Service.class, 2002); System.out.println(s2.getOsUserName() + " " + s2.getUnixHost()); }</span></span>
2)查詢緩存
a、查詢緩存
一級和二級緩存只能是緩存單個對象。如果遇到某個字符串結(jié)果、數(shù)組結(jié)果或者List集合,可以使用查詢緩存存儲。
--可以看成是特殊的二級緩存
--使用的是二級緩存的緩存空間
--緩存的是除實體對象之外的數(shù)據(jù)類型
--依賴于二級緩存,默認(rèn)是關(guān)閉的,需開啟二級緩存才能使用。
b、使用步驟
--開啟二級緩存
--在hibernate.cfg.xml中設(shè)置開啟查詢緩存
<span style="font-size:14px;"><!-- 開啟查詢緩存 --> <property name="hibernate.cache.use_query_cache">true</property></span>
--query.list()查詢之前,設(shè)置允許進(jìn)行查詢緩存,query.setCacheable(true);
c、使用環(huán)境
--需要頻繁執(zhí)行的相同的查詢語句
--查詢結(jié)果集內(nèi)容改變頻率小
--結(jié)果集數(shù)據(jù)量不要太多
提示:相同的SQL,第一次去數(shù)據(jù)庫查詢,后續(xù)幾次從緩存取出。因為緩存的是SQL+結(jié)果集內(nèi)容
示例代碼:
<span style="font-size:14px;"><span style="font-size:14px;"> @Test public void testFind() { find(); System.out.println("-------"); find(); } private void find() { String hql = "from Service where account.id=?"; Session session = HibernateUtil.getSession(); Query query = session.createQuery(hql); query.setInteger(0, 1011); // 啟用查詢緩存執(zhí)行 query.setCacheable(true); List<Service> list = query.list(); for (Service s : list) { System.out.println(s.getId() + " " + s.getOsUserName()); } }</span></span>
9、Hibernate并發(fā)處理-加鎖
舉例:
模擬12306購票機制,假設(shè)當(dāng)前有一張火車票的表tickets(id,line,amount,version),模擬多人同時購票的場景。
1)悲觀鎖
--程序悲觀的認(rèn)為,每個訪問者都存在并發(fā)的問題,于是會對每條數(shù)據(jù)加鎖,那么只有當(dāng)前持有鎖的訪問者釋放該鎖時,下一個訪問者才能訪問數(shù)據(jù),這種機制稱之為悲觀鎖。
--就是說無論如何都要給一條數(shù)據(jù)加鎖,不管該數(shù)據(jù)是否會發(fā)生并發(fā)的問題。
--特點
效率低,安全性高
--實現(xiàn):
get(Class,id,LockMode.UPGRADE)
select * from emp for update
注:這里利用的是數(shù)據(jù)庫自帶的for update子句進(jìn)行加鎖,并非是Hibernate自己發(fā)明的加鎖機制。
2)樂觀鎖
--程序樂觀的認(rèn)為,每個訪問者都不會有并發(fā)的問題產(chǎn)生,因此不去加鎖。而是當(dāng)數(shù)據(jù)更新時,判斷該數(shù)據(jù)是否發(fā)生了版本的變化,如果發(fā)生變化,則說明在此期間有并發(fā)產(chǎn)生,因此報錯給予提示,本次更新失敗。
--借助版本字段,當(dāng)?shù)谝粋€用戶更新提交后,Hibernate會將該字段更新加1,這樣后續(xù)用戶提交的對象版本字段比數(shù)據(jù)庫中的小,即在更新時發(fā)現(xiàn)版本變了,就拋出異常并更新失敗。
--一個成功,其他失敗
--特點
效率高,用戶體驗差
--實現(xiàn)
a、需要在表中追加版本字段,用于記錄數(shù)據(jù)的版本;
b、實體類中追加版本屬性;
c、hbm.xml中追加版本的配置<version name="" type="" column="">
3)如何選擇
--如果是并發(fā)量大的情況,應(yīng)選擇樂觀鎖
--如果是并發(fā)量小的情況,應(yīng)選擇悲觀鎖
以上所述是小編給大家介紹的 全面解析Hibernate關(guān)聯(lián)操作、查詢操作、高級特性、并發(fā)處理機制的相關(guān)知識,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Java詳細(xì)分析String類與StringBuffer和StringBuilder的使用方法
當(dāng)對字符串進(jìn)行修改的時候,需要使用 StringBuffer 和 StringBuilder類,和String類不同的是,StringBuffer和 StringBuilder類的對象能夠被多次的修改,并且不產(chǎn)生新的未使用對象2022-04-04Java?C++算法題解leetcode801使序列遞增的最小交換次數(shù)
這篇文章主要為大家介紹了Java?C++題解leetcode801使序列遞增的最小交換次數(shù)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10一個JAVA小項目--Web應(yīng)用自動生成Word
前段時間接到一個Web應(yīng)用自動生成Word的需求,現(xiàn)整理了下一些關(guān)鍵步驟拿來分享一下。2014-05-05RestTemplat中關(guān)于getForobject方法的使用
這篇文章主要介紹了RestTemplat中關(guān)于getForobject方法的使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07java 方法重寫與權(quán)限修飾符以及多態(tài)和抽象類詳解概念和用法
重寫是子類對父類的允許訪問的方法的實現(xiàn)過程進(jìn)行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫,權(quán)限修飾符用于控制被修飾變量、方法、類的可見范圍,說明了面向?qū)ο蟮姆庋b性,所以我們要適用他們盡可能的讓權(quán)限降到最低,從而安全性提高2021-10-10SpringBoot實現(xiàn)定時任務(wù)的三種方式小結(jié)
這篇文章主要介紹了SpringBoot實現(xiàn)定時任務(wù)的三種方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11