Hibernate的幾種查詢方式和二級(jí)緩存
一. Hibernate的查詢方式
主要包括以下幾種
1) Get/load主鍵查詢
2) 對(duì)象導(dǎo)航查詢
3) HQL查詢仑嗅, Hibernate Query language hibernate 提供的面向?qū)ο蟮牟樵冋Z(yǔ)言。
4) Criteria 查詢搓蚪, 完全面向?qū)ο蟮牟樵儯≦uery By Criteria ,QBC)
5) SQLQuery钞澳, 本地SQL查詢
缺點(diǎn):不能跨數(shù)據(jù)庫(kù)平臺(tái): 如果修改了數(shù)據(jù)庫(kù),sql語(yǔ)句有肯能要改
使用場(chǎng)景: 對(duì)于復(fù)雜sql靠抑,hql實(shí)現(xiàn)不了的情況偷厦,可以使用本地sql查詢捆愁。
1.1 HQL查詢,先創(chuàng)建兩個(gè)實(shí)體類,并且配置好映射
Dept.java
package com.mrq.entity;
import java.util.HashSet;
import java.util.Set;
public class Dept {
private Integer deptId;
private String deptName;
private Set<Employee> emps = new HashSet<>();//一對(duì)多:一個(gè)部門對(duì)應(yīng)多個(gè)員工
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public Set<Employee> getEmps() {
return emps;
}
public void setEmps(Set<Employee> emps) {
this.emps = emps;
}
@Override
public String toString() {
return "Dept [deptId=" + deptId + ", deptName=" + deptName + ", emps=" + emps + "]";
}
public Dept() {
// TODO Auto-generated constructor stub
}
public Dept(Integer deptId, String deptName) {
super();
this.deptId = deptId;
this.deptName = deptName;
}
}
Employee.java
package com.mrq.entity;
public class Employee {
private Integer empId;
private String empName;
private Double salary;
private Dept dept; //多對(duì)一:多個(gè)員工對(duì)應(yīng)一個(gè)部門
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Employee [empId=" + empId + ", empName=" + empName + "]";
}
}
映射文件
Dept.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mrq.entity" auto-import="true">
<class name="Dept" table="t_dept">
<id name="deptId">
<generator class="native"></generator>
</id>
<property name="deptName" type="string"></property>
<!--
key:Employee對(duì)應(yīng)的外鍵名稱
-->
<set name="emps"><!-- table="t_employee"可省略 -->
<key column="dept_id"></key>
<one-to-many class="Employee" />
</set>
</class>
<!-- 存放sql語(yǔ)句 -->
<query name="getAll">
<![CDATA[
from Dept d where deptId < ?
]]>
</query>
</hibernate-mapping>
Employee.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.mrq.entity">
<class name="Employee" table="t_employee">
<id name="empId">
<generator class="native"></generator>
</id>
<property name="empName" type="string"></property>
<property name="salary" type="double"></property>
<many-to-one name="dept" class="Dept" column="dept_id"></many-to-one>
</class>
</hibernate-mapping>
- 各種查詢對(duì)應(yīng)的格式和要點(diǎn)
App.java
/*
* 1) Get/load主鍵查詢
2) 對(duì)象導(dǎo)航查詢
3) HQL查詢, Hibernate Query language hibernate 提供的面向?qū)ο蟮牟樵冋Z(yǔ)言脸爱。
4) Criteria 查詢遇汞, 完全面向?qū)ο蟮牟樵儯≦uery By Criteria ,QBC)
5) SQLQuery, 本地SQL查詢
*/
//1.HQL查詢
@Test
public void testQuery() {
Session session = sf.openSession();
session.beginTransaction();
//1.主鍵查詢
Dept dept = (Dept) session.get(Dept.class, 1);
System.out.println(dept);
/*
* 2.對(duì)象導(dǎo)航查詢:通過一個(gè)對(duì)象查詢另外的對(duì)象的信息,比如通過部門,查詢?cè)摬块T下的員工和員工信息
* */
Dept dept = (Dept)session.get(Dept.class, 1);
System.out.println(dept.getEmps());
//3.HQL查詢
//方式一:
Query query = session.createQuery("from Dept");
System.out.println(query.list());
//方式二
Query query = session.createQuery("select d from Dept d");
System.out.println(query.list());
//查詢指定的column
//1.返回特定對(duì)象數(shù)組Object[]
Query query = session.createQuery("select d.deptId,d.deptName from Dept d");
System.out.println(query.list());
//2.返回封裝的對(duì)象:要提供對(duì)應(yīng)的構(gòu)造函數(shù)
Query query = session.createQuery("select new Dept(d.deptId,d.deptName) from Dept d");
System.out.println(query.list());
//條件查詢:一個(gè)條件/多個(gè)條件and or/between and/模糊查詢
//1.使用?占位符
Query query = session.createQuery("from Dept d where deptName=?");
query.setParameter(0, "技術(shù)部");
System.out.println(query.list());
//2.使用命名參數(shù):command
Query query = session.createQuery("from Dept d where deptId= :id or deptName= :name");
query.setParameter("id", 1);
query.setParameter("name", "技術(shù)部");
System.out.println(query.list());
//范圍查詢 between
Query query = session.createQuery("from Dept d where deptId between ? and ?");
query.setParameter(0, 1);
query.setParameter(1, 5);
System.out.println(query.list());
//模糊查詢 like
Query query = session.createQuery("from Dept d where deptName like ?");
query.setParameter(0, "%部%");
System.out.println(query.list());
//聚合函數(shù)查詢
Query query = session.createQuery("select count(*) from Dept");
Long count = (Long) query.uniqueResult();
System.out.println(count);
//分組查詢:查找相同部門的員工
Query query = session.createQuery("select e.dept,count(*) from Employee"
+ " e group by e.dept");
System.out.println(query.list());
session.getTransaction().commit();
session.close();
}
- 常見的連接查詢
@Test
public void join() {
Session session = sf.openSession();
session.beginTransaction();
//1.內(nèi)連接
Query query = session.createQuery("from Dept d inner join d.emps");
System.out.println(query.list());
//2.左外連接
Query query = session.createQuery("from Dept d left join d.emps");
System.out.println(query.list());
//3.右外鏈接
Query query = session.createQuery("from Dept d right join d.emps");
System.out.println(query.list());
session.getTransaction().commit();
session.close();
}
- 迫切連接fetch查詢
public void fetch() {
Session session = sf.openSession();
session.beginTransaction();
//1.迫切內(nèi)連接,使用fetch,會(huì)把右表的數(shù)據(jù)填充到左表數(shù)據(jù)中
// Query query = session.createQuery("from Dept d inner join fetch d.emps");
// System.out.println(query.list());
//2.迫切左外連接
Query query = session.createQuery("from Dept d left join fetch d.emps");
System.out.println(query.list());
session.getTransaction().commit();
session.close();
}
- 從配置文件寫HQL語(yǔ)句
在Dept.hbm.xml進(jìn)行配置,加入query節(jié)點(diǎn)
<!-- 存放sql語(yǔ)句 -->
<query name="getAll">
<![CDATA[
from Dept d where deptId < ?
]]>
</query>
測(cè)試:
@Test
public void hqlQuery() {
Session session = sf.openSession();
session.beginTransaction();
Query query = session.getNamedQuery("getAll");
query.setParameter(0, 10);
System.out.println(query.list());
session.getTransaction().commit();
session.close();
}
- Criteria查詢使用基本方式
//Criteria 查詢
@Test
public void criteriaQuery() {
Session session = sf.openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Employee.class);
//條件約束
//criteria.add(Restrictions.eq("empId", 12));//empId=12的對(duì)象
criteria.add(Restrictions.idEq(12));//也可以使用ID查詢
System.out.println(criteria.list());
session.getTransaction().commit();
session.close();
}
- HQL分頁(yè)查詢
//分頁(yè)查詢
@Test
public void pageQuery() {
Session session = sf.openSession();
session.beginTransaction();
Query query = session.createQuery("from Employee");
ScrollableResults scrollableResults = query.scroll();//得到結(jié)果集
scrollableResults.last();//滾動(dòng)到最后一行
int totalCount = scrollableResults.getRowNumber()+1;//得到滾動(dòng)的記錄數(shù)
//設(shè)置分頁(yè)參數(shù)
query.setFirstResult(0);
query.setMaxResults(5);
System.out.println(query.list());
System.out.println("總的記錄數(shù) "+totalCount);
session.getTransaction().commit();
session.close();
}
- Hibernate支持原生SQL語(yǔ)句的使用
//支持原生的SQL查詢
@Test
public void sql() {
Session session = sf.openSession();
session.beginTransaction();
SQLQuery q = session.createSQLQuery("SELECT * FROM t_Dept limit 4;")
.addEntity(Dept.class); // 也可以自動(dòng)封裝對(duì)象
System.out.println(q.list());
session.getTransaction().commit();
session.close();
}
二. Hibernate的二級(jí)緩存
前面了解了Hibernate的一級(jí)緩存:session緩存
Hibernate提供的緩存
有一級(jí)緩存簿废、二級(jí)緩存空入。 目的是為了減少對(duì)數(shù)據(jù)庫(kù)的訪問次數(shù),提升程序執(zhí)行效率族檬!
一級(jí)緩存:
基于Session的緩存歪赢,緩存內(nèi)容只在當(dāng)前session有效,session關(guān)閉单料,緩存內(nèi)容失效轨淌!
特點(diǎn):
作用范圍較小看尼! 緩存的事件短递鹉。
緩存效果不明顯。
二級(jí)緩存:
Hibernate提供了基于應(yīng)用程序級(jí)別的緩存藏斩, 可以跨多個(gè)session躏结,即不同的session都可以訪問緩存數(shù)據(jù)。 這個(gè)換存也叫二級(jí)緩存狰域。
Hibernate提供的二級(jí)緩存有默認(rèn)的實(shí)現(xiàn)媳拴,且是一種可插配的緩存框架!如果用戶想用二級(jí)緩存兆览,只需要在hibernate.cfg.xml中配置即可屈溉; 不想用,直接移除抬探,不影響代碼子巾。
如果用戶覺得hibernate提供的框架框架不好用,自己可以換其他的緩存框架或自己實(shí)現(xiàn)緩存框架都可以小压。
- 使用二級(jí)緩存進(jìn)行的配置
#hibernate.cache.use_second_level_cache false【二級(jí)緩存默認(rèn)不開啟线梗,需要手動(dòng)開啟】
#hibernate.cache.use_query_cache true 【開啟查詢緩存】
## choose a cache implementation 【二級(jí)緩存框架的實(shí)現(xiàn)】
#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider 默認(rèn)實(shí)現(xiàn)
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
二級(jí)緩存,使用步驟
1) 開啟二級(jí)緩存
2)指定緩存框架
3)指定那些類加入二級(jí)緩存
4)測(cè)試
測(cè)試二級(jí)緩存怠益!
緩存策略
<class-cache usage="read-only"/> 放入二級(jí)緩存的對(duì)象仪搔,只讀;
<class-cache usage="nonstrict-read-write"/> 非嚴(yán)格的讀寫
<class-cache usage="read-write"/> 讀寫; 放入二級(jí)緩存的對(duì)象可以讀蜻牢、寫烤咧;
<class-cache usage="transactional"/> (基于事務(wù)的策略)
在hibernate.cfg.xml中配置二級(jí)緩存
<!--****************** 【二級(jí)緩存配置】****************** -->
<!-- a. 開啟二級(jí)緩存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- b. 指定使用哪一個(gè)緩存框架(默認(rèn)提供的) -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<!-- 開啟查詢緩存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<!-- c. 指定哪一些類偏陪,需要加入二級(jí)緩存 -->
<class-cache usage="read-write" class="com.mrq.entity.Dept"/>
<class-cache usage="read-only" class="com.mrq.entity.Employee"/>
<!-- 集合緩存[集合緩存的元素對(duì)象,也加加入二級(jí)緩存] -->
<collection-cache usage="read-write" collection="com.mrq.entity.Dept.emps"/>
測(cè)試二級(jí)緩存:
@Test
public void testCache() {
Session session1 = sf.openSession();
session1.beginTransaction();
//進(jìn)行一次查詢
Dept dept = (Dept)session1.get(Dept.class,5);
dept.getEmps().size();
System.out.println(dept);
session1.getTransaction().commit();
session1.close();
System.out.println("開啟第二次查詢");
Session session2 = sf.openSession();
session2.beginTransaction();
dept = (Dept)session2.get(Dept.class,5);
dept.getEmps().size();
System.out.println(dept);
session2.getTransaction().commit();
session2.close();
}
不使用二級(jí)緩存的輸出:可以得出兩次查詢都是同樣的操作:向數(shù)據(jù)庫(kù)查詢
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept dept0_ where dept0_.deptId=?
Hibernate: select emps0_.dept_id as dept4_0_1_, emps0_.empId as empId1_, emps0_.empId as empId1_0_, emps0_.empName as empName1_0_, emps0_.salary as salary1_0_, emps0_.dept_id as dept4_1_0_ from t_employee emps0_ where emps0_.dept_id=?
Dept [deptId=5, deptName=技術(shù)部3, emps=[Employee [empId=10, empName=李四3], Employee [empId=9, empName=張三3]]]
開啟第二次查詢
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept dept0_ where dept0_.deptId=?
Hibernate: select emps0_.dept_id as dept4_0_1_, emps0_.empId as empId1_, emps0_.empId as empId1_0_, emps0_.empName as empName1_0_, emps0_.salary as salary1_0_, emps0_.dept_id as dept4_1_0_ from t_employee emps0_ where emps0_.dept_id=?
Dept [deptId=5, deptName=技術(shù)部3, emps=[Employee [empId=9, empName=張三3], Employee [empId=10, empName=李四3]]]
開啟二級(jí)緩存的輸出:第一次查詢向數(shù)據(jù)庫(kù)發(fā)送了SQL語(yǔ)句,第二次查詢沒有向數(shù)據(jù)庫(kù)查詢,而是從緩存中獲取
Hibernate: select dept0_.deptId as deptId0_0_, dept0_.deptName as deptName0_0_ from t_dept dept0_ where dept0_.deptId=?
Hibernate: select emps0_.dept_id as dept4_0_1_, emps0_.empId as empId1_, emps0_.empId as empId1_0_, emps0_.empName as empName1_0_, emps0_.salary as salary1_0_, emps0_.dept_id as dept4_1_0_ from t_employee emps0_ where emps0_.dept_id=?
Dept [deptId=5, deptName=技術(shù)部3, emps=[Employee [empId=9, empName=張三3], Employee [empId=10, empName=李四3]]]
開啟第二次查詢
Dept [deptId=5, deptName=技術(shù)部3, emps=[Employee [empId=9, empName=張三3], Employee [empId=10, empName=李四3]]]
2.1 list二級(jí)緩存策略,可以使用setCacheable(true)顯示指定從二級(jí)緩存獲取或者放入二級(jí)緩存中
Session session1 = sf.openSession();
session1.beginTransaction();
//setCacheable 指定從二級(jí)緩存找,或者放入二級(jí)緩存
Query query = session1.createQuery("from Dept").setCacheable(true);
System.out.println(query.list());
session1.getTransaction().commit();
session1.close();
System.out.println("第二次查詢");
Session session2 = sf.openSession();
session2.beginTransaction();
query = session2.createQuery("from Dept").setCacheable(true);
System.out.println(query.list());
session2.getTransaction().commit();
session2.close();
四. 項(xiàng)目中session的創(chuàng)建方式
session.openSession();方法每次都會(huì)創(chuàng)建一個(gè)新的session
session.getCurrentSession();方法則是從當(dāng)前線程獲取session或者創(chuàng)建session放入線程中,使用這種方式,需要在配置文件進(jìn)行配置:
<property name="hibernate.current_session_context_class">thread</property>
測(cè)試代碼
@Test
public void testSession() {
//openSession: 創(chuàng)建Session, 每次都會(huì)創(chuàng)建一個(gè)新的session
Session session1 = sf.openSession();
Session session2 = sf.openSession();
System.out.println(session1 == session2);
session1.close();
session2.close();
//getCurrentSession 創(chuàng)建或者獲取session
// 線程的方式創(chuàng)建session
// 一定要配置:<property name="hibernate.current_session_context_class">thread</property>
Session session3 = sf.getCurrentSession();// 創(chuàng)建session煮嫌,綁定到線程
Session session4 = sf.getCurrentSession();// 從當(dāng)前訪問線程獲取session
System.out.println(session3 == session4);
// 關(guān)閉 【以線程方式創(chuàng)建的session竹挡,可以不用關(guān)閉; 線程結(jié)束session自動(dòng)關(guān)閉】
//session3.close();
//session4.close(); 報(bào)錯(cuò)立膛,因?yàn)橥粋€(gè)session已經(jīng)關(guān)閉了揪罕!
}
結(jié)果輸出:false true