上連接到Hibernate--002 session簡單操作數據庫、Hibernate中存儲數據的三種狀態、髒數據與刷新緩存。
https://blog.csdn.net/hekaikai666/article/details/81876861
回顧與總結:
簡述上一章的主要內容:
(一)session、sessionFactory、Configuration三大接口的簡介
(二)Hibernate簡單操作數據庫的方式
(三)Hibernate照片那個存數據數據的三種狀態
遊離態、持久態、臨時態
(四)Hibernate中數據的髒讀、與刷新緩存機制
接下來的全都是重點喲!
一、Hibernate中數據查詢的三種方式
(一)HQL語句(重點)
1.HQL(Hibernate Query Language):Hibernate中定義的一種結構化查詢語句,是一種面向Java實體類的面向對象查詢語言,與SQL有着相似的語法結構,但是與SQL麼有一絲絲的關係。
2.SQL和HQL的區別:
a.SQL是一種面向數據庫的查詢語言,是按照表名,列名來查詢的。(SELECT 列名1,列名2,... FROM ...)而HQL是一種面向實體類的查詢語言,是按照類名,屬性名來查詢的(SELECT 屬性名1,屬性名2,... FROM 表名)。
b.HQL不支持“SELECT * ” 的寫法,因爲默認的爲"SELECT * "。
c.HQL不支持分組函數以外的其他函數(分組函數:COUNT()/AVG()/MIN()/MAX()/SUM())。
d.HQL不支持JOIN...ON...的連接查詢
3.HQL的使用。(重中之重)
a.通過HQL語句來查詢(執行HQL的方法有:list() iterate() uniqueResult())
b.使用步驟:
b_1:獲取session對象
b_2:定義HQL語句
b_3:創建query對象瘋長HQL語句
b_4:調用query方法執行HQL語句(lis() iterate() nuiqueResult())
b_5:關閉session
HQL查詢:使用三種方法(list() iteate() uniqueRequest())
// 定義查詢所有的HQL語句(Dept是類名,deptNo是屬性)
String hql = "from Dept where deptNo=1";
// 創建一個查詢對象Query對象封裝一個HQL語句
Query query = session.createQuery(hql);
// 當確定返回值只有一個值,調用uniqueResult
Dept dept = (Dept) query.uniqueResult();
System.out.println(dept);
// 根據id查詢名字
// 執行HQL語句(調用list方法執行HGQL語句)
List<Dept> depts = query.list();
System.out.println(depts);
// 關閉session
hql = "select deptName from Dept where deptNo=1";
query = session.createQuery(hql);
String str = (String) query.uniqueResult();
System.out.println(str);
Iterator<Dept> iterate = query.iterate();
while (iterate.hasNext()) {
Dept dept1 = iterate.next();
System.out.println(dept1.getDeptNo());
System.out.println(dept1.getDeptName());
System.out.println(dept1.getDeptLocation());
System.out.println("-----------------------------------");
}
// 關閉session對象
session.close();
執行HQL的三種方法:
list():當調用此方法時會立即向數據庫中發送一條查詢語句(SQL),查詢所有的對象信息並保存到list<T>集合中;
iterate():當調用此方法時會啓動延時加載,向數據庫中發送一條查詢SQL語句,先查詢所有對象的id屬性值,返回的是代理對象(只有id),然後將這些id對象保存到iterate迭代器對象中,當我們迭代某個對象的時候,需要查詢此個對象的非id屬性,會拿出此個對象的id,根據對象的id查詢此個對象的完整信息。
uniqueResult():非延時加載,當確定返回對象只有一份時使用。
4.給帶佔位符的HQL語句賦值(三種佔位符---?佔位符 | :屬性名 佔位符 |命名參數給定佔位符)
a.如果佔位符是"?":通過佔位符下標,下標從"0"開始賦值
query.setString(下標,屬性值); query.setParameter(下標,屬性值);
b.如果站位符是":屬性名" :通過屬性賦值
query.setString(屬性名,屬性值); query.setParameter(屬性名,屬性值);
c.命名參數給佔位符賦值:
map:key:屬性名;value:屬性值
query.setProperties(map);
案例:
// ?佔位符
// 定義一個帶佔位符的hql語句
String hql = "from Dept where deptName=? and deptLocation=?";
//創建一個查詢對象Query對象封裝一個HQL語句
Query query = session.createQuery(hql);
// 給佔位符賦值(下標從0開始)
query.setString(0, "凱凱");
query.setString(1, "西安");
// 給佔位符賦值(下標從0開始)
query.setParameter(0, "凱凱");
query.setParameter(1, "西安");
// 執行hql語句
Dept dept = (Dept) query.uniqueResult();
System.out.println(dept);
// :屬性名 佔位符
// 定義一個帶佔位符的hql語句
String hql1 = "from Dept where deptName=:deptName and deptLocation=:deptLocation";
//創建一個查詢對象Qu【】ery對象封裝一個HQL語句
Query query = session.createQuery(hql);
// 給佔位符賦值(下標從0開始)
query.setString(0, "凱凱");
query.setString(1, "西安");
// 給佔位符賦值(下標從0開始)
query.setParameter(0, "凱凱");
query.setParameter(1, "西安");
// 執行hql語句
Dept dept = (Dept) query.uniqueResult();
System.out.println(dept);
// 命名參數 佔位符(要求佔位符必須是屬性)
// 定義一個帶佔位符的hql語句
String hql1 = "from Dept where deptName=:deptName and deptLocation=:deptLocation";
//創建一個查詢對象Query對象封裝一個HQL語句
Query query = session.createQuery(hql);
Map<String, String> map = new HashMap<String,String>();
map.put("deptName", "凱凱");
map.put("loactiom", "西安");
query.setProperties(map);
Dept dept1 = (Dept) query.uniqueResult();
System.out.println(dept1);
5.分頁查詢
在之前學過的SQL語句查詢中分頁會用一個關鍵字來查(limit)例如:SELECT * FROM emp limit ?,?;需要給定的兩個參數爲從那一條開始查P1,需要查詢多少條P2,所以分頁查詢HQL中也必須知道這倆參數:page(當前頁碼) pageCount(頁面容量)。
page pageCount P1 P2
1 3 0 3
2 3 1 3
3 3 2 3
4 3 3 3
由此可得:P1 = (page-1)*pageCount; P2 = pageCount;
由此可得String hql = "SELECT COUNT(*) FROM emp" --> rows;
總頁碼數爲:long totalPages = rows % pageCount == 0 ? rows / pageCount : rows / pageCount + 1;
// 定義hql
String hql = "from Emp";
// 創建query對象封裝hql
Query query = session.createQuery(hql);
// 獲取頁碼和頁容
int page = 2;
int pageCount = 2;
// 通過對象設置開始的條數
query.setFirstResult((page - 1) * pageCount);
// 設置查詢結果的最大條數
query.setMaxResults(pageCount);
// 執行HQL語句,獲取當前頁面內容
List<Emp> emps = query.list();
for (Emp emp : emps) {
System.out.println(emp);
}
// 查看總共有多少頁
// 先查詢總共有多少條記錄
hql = "select count(*) from Emp";
// 封裝hql語句
query = session.createQuery(hql);
//獲取返回的總行數
long rows = (long) query.uniqueResult();
// 獲取拿到的頁碼數(分頁)
long totalPages = rows % pageCount == 0 ? rows / pageCount : rows / pageCount + 1;
System.out.println("總條數:" + rows);
System.out.println("總頁數:" + totalPages);
// 關閉session
session.close();
(二)動態參數查詢
主要藉助兩個API(這兩API內容過多,掌握其幾個方法即可,關於動態查詢其他方法詳析,請參照之後的貼子)
Criteria:主要用於封裝多個動態查詢條件、並執行查詢操作
Restrictions:用於封裝一種某一個動態查詢的參數,提供了很多靜態方法,用於代替查詢調條件
案例:
// 創建一個Criteria對象,可以封裝多個動態查詢參數
Criteria criteria = session.createCriteria(Emp.class);
// 實例化一個時間格式類
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// 如:查詢職位爲ClERK的員工信息|||Restrictions封裝某一個動態查詢參數..
criteria.add(Restrictions.eq("ejob", "CLERK"));
// 在添加一個動態查詢參數,查詢薪水大於5000的員工信息
criteria.add(Restrictions.gt("salary", Double.valueOf(5000)))
.add(Restrictions.between("hiredate", df.parse("2015-01-01"), df.parse("2017-12-31")));
// 再添加一個動態查詢參數
// 查詢2014年入職的並且薪水小於5000的員工信息
criteria = session.createCriteria(Emp.class);
criteria.add(Restrictions.and(Restrictions.gt("hiredate", df.parse("2014-01-01")),
Restrictions.lt("salary", Double.valueOf(5000))));
// 執行查詢
List<Emp> ls = criteria.list();
// 遍歷獲取
for (Emp emp : ls) {
System.out.println(emp);
}
(三)原生SQL查詢
定義SQL語句;
使用SQLQuery封裝SQL語句【SQLQuery query = session.createSQLQuery(sql).addEntity(Emp.class);】;
通過query執行SQL語句;
// 定義SQL語句
String sql = "select * from Emp";
// 創建SQL查詢,用於封裝SQL語句
SQLQuery query = session.createSQLQuery(sql).addEntity(Emp.class);
// 執行SQL語句
List<Emp> ls = query.list();
// 遍歷獲取值
for (Emp emp : ls) {
System.out.println(emp);
}
// 關閉session對象
session.close();
注:因爲原生SQL查詢和動態參數查詢都是比較簡單的查詢方式,只需要瞭解幾個常用API的方法,所以在這裏不做詳細的解釋,如果需要使用或者需要詳細的解析,可以聯繫博主小凱,可以給你詳細的解釋。
二、Hibernate的關聯查詢
在關係型數據庫中數據的存儲方式需要按照三大範式和第四範式的標準執行,所以對於多種表結構單表查詢肯定不實用,那麼對於HQL這種簡單的單表操作方式就用不上了,Hibernate中封裝了許多的映射方法,用於查詢多表之間的操作(這裏只演示兩表操作,與多表操作基本一致)所以就有了關係數據庫中的一對多,多對一,多對多,一對一的層級關係來管理關係數據表。
(一)一對多(one-to-many)與多對一(many-to-one)
【例】一個學生student有多本數書books,多本書books對應一個學生student
操作流程:
a.建表
#學生表
CREATE TABLE student(
s_id int primary key,
s_name varchar(20));
#圖書表
CREATE TABLE book(
b_id int primary key,
b_name varchar(20),
b_author varchar(20),
b_studentid int);
#建立外鍵關係
alter table book add foreign key(b_studentid) reference student(s_id);
b.建實體類
一方實體類 -s_id -s_name -Set<Book> set #表示多方
多方實體類 -b_id -b_name -b_author Student student #表示一方
public class Student implements Serializable {
private int s_id;
private String s_name;
// 表示多方集合:一個學生有多本書
private Set<Book> books = new HashSet<Book>();
此處省略空參構造方法,多參構造方法,get方法,set方法,toString方法
}
public class Book implements Serializable {
private int b_id;
private String b_name;
private String b_author;
// 表示當前book屬於哪個student
private Student student;
此處省略空參構造方法,多參構造方法,get方法,set方法,toString方法
}
c. 映射文件
一方映射:student.hbm.xml
<set name="多方集合屬性名">
<key column="外鍵"/>
<one-to-many class="多方實體類全限定名"/>
</set>
多方映射:book.hbm.xml
<many-to-one name="一方實體類屬性名" column="外鍵" class="一方實體類全限定名"/>
<class name="com.lanou.beans3.Student" table="student">
<id name="s_id" type="int" column="s_id">
<generator class="assigned" />
</id>
<property name="s_name" type="string" column="s_name" />
<!-- 一對多的關聯映射 -->
<set name="books" lazy="false" cascade="all">
<key column="b_studentid"/>
<one-to-many class="com.lanou.beans3.Book"/>
</set>
</class>
<class name="com.lanou.beans3.Book" table="book">
<id name="b_id" type="int" column="b_id">
<generator class="assigned" />
</id>
<property name="b_name" type="string" column="b_name" />
<property name="b_author" type="string" column="b_author" />
<!-- 定義多對一的關聯映射 -->
<many-to-one name="student" column="b_studentid" lazy="false"
cascade="delete" class="com.lanou.beans3.Student" />
</class>
屬性解釋:
一對多的關聯映射
name:第一方實體類中用來描述多方的集合屬性名;column:多方表中用於維持關係的外鍵名稱;class:多方實體類全限定名lazy:true默認延時加載,只加載當前對象 false:取消延時加載,級聯查詢所關聯的對象信息;cascade:級聯操作;值:none:默認值,只操作當前對象;save-update:級聯的添加或者修改其關聯對象;delete:級聯的刪除其關聯對象;all:包含save-update/delete。
定義多對一的關聯映射
name:多方實體類中用於描述一方實體類屬性名 ;column:多方表中用於維持關係的外鍵名稱 ;class:一方實體類全限定名;lazy:取消延時加載(false)默認值,延遲加載,使用時獲取的是代理對象(proxy) 延遲加載,使用時獲取的是非代理對象(no-proxy)
d.級聯操作
d.級聯操作
d_1.一對多的級聯查詢(查詢id爲1的學生有哪些書)
d_2.一對多的級聯修改(給id爲2的學生添加兩本書)
d_3.一對多的級聯添加(添加一個學生,並級聯添加兩本圖書)
d_4.多對一的級聯查詢(查詢金瓶梅這本書屬於哪個學生)
d_5.多對一的級聯刪除(刪除金瓶梅這本書以及它所關聯的學生)
底層原理實現:
1、先插多方的外鍵b_studentid
2、通過外鍵b_studentid查詢到所關聯的Student(s_id)
3、再從book中查詢哪些書是b_studentid等於s_id
4、刪除這些book
5、刪除這些學生
以級聯查詢爲例
// 查詢參數爲id的student對象
Student student = (Student) session.get(Student.class, 1);
System.out.println(student.getS_id());
System.out.println(student.getS_name());
// 獲取其多方關聯的集合信息
Set<Book> books = student.getBooks();
for (Book book : books) {
System.out.println(book.getB_id());
}
(二)多對多(many-to-many)
【例】一個員工employee會做多個項目project;一個項目project可能會由多個員工employee完成
a.建表
#employee員工表
create table employee(e_id int primary key,e_name varchar(20));
#project項目表
create table project(p_id int primary key,p_name varchar(20));
#中間表(關係表)
create table proemp(id int primary key,auto_increament,rempid int,#表示員工rproid int #表示項目);
b.實體類
Employee:--int e_id --String e_name --Set<Project> projects
Project:--int p_id --String p_name --Set<Employee> employees
c.映射文件
<set name="描述對方的集合屬性" table="中間表" cascade="all">
<key column="當前表在中間表的描述id" />
<many-to-many class="對方實體類全限定名" column="對方表在中間表的描述id" />
</set>
(二)一對一(one-to-one)
一對一映射必須要有一個關聯,就要考慮其關聯的外鍵還是主鍵,單向還是雙向,這種關聯肯定要和自己本身的屬性有,所以可以分爲單向外鍵關聯,雙向外鍵關聯,單向主鍵關聯,雙向主鍵關聯
【例】一個用戶user對應一行身份證card;一張身份證card對應一個用戶user
(單向外鍵關聯)(雙向外鍵關聯)(單向主鍵關聯)(單向主鍵關聯)
a.建表
#用戶表
create table user(u_id int primary key,u_name varchar(20),u_address varchar(20));
#身份證表
create table card(c_id int primary key,c_No varchar());
b.建實體類
User --u_id --u_name --u_address --Card card
Card --c_id --c_No
c.映射文件
<one-to-one name="當前類中描述對方的實體類" class="對方實體類全限定名" cascade="all"/>
這裏,Hibernate中所有存在性查詢已經完全結束了,但是他的加載過程還遠遠沒有結束,在下一貼中將會分享關聯映射和緩存機制,這纔是Hibernate的第四優勢。