如有轉載,請申明:
轉載自 IT天宇: http://www.lxweimin.com/p/50964e92c5fb
前言
博客正式轉到簡書,實在忍受不了 CSDN 的模版,有廣告或界面難看還可以忍,但有些模版還有 bug,作為程序員忍無可忍,修個 bug 真的有那么難嗎,這么多年了。
接著上篇,先說個段子。
三四年前如果有人問我 Android/Ios 如何入門,我可能會推薦上百 G 的資料,但如果現在問我,我只會推薦一本書《app開發從上架到上吊》
你可能覺得我危言聳聽,但今年的移動開發行情真的很差,看到很多幾年經驗的一個月才收到幾個面試通知,沒有經驗的就更絕望了。
好吧,今天不是來討論行情的。
前段時間寫了一篇 struts2 的筆記,有好心的老司機告訴我,struts2 已經被拋棄了。但話是這么說,面試的時候,難免碰到問 struts2,如果這個時候表現得一臉懵逼,那估計 offer 就沒有了。所以雖然現在 hibernate 用得不多了,但還是得復習一下。
目錄
- 環境搭建
- 實體類映射
- 核心配置詳解
- 一級緩存
- 關系映射
- 抓取策略
- HQL
- QBC
- 其他配置
- 事務
- 二級緩存
正文
<a id="1"></a>
1.環境搭建
導包
根據需要選擇手動導入 jar 包,或者用依賴管理工具。
- 下載
http://hibernate.org/orm/downloads/
必須導入的是lib\required
下面 jar 包 - Gradle
compile "org.hibernate:hibernate-core:5.2.9.Final"
此外,為了連接數據庫,還需要數據庫的驅動包。
配置
核心配置文件以 hibernate.cfg.xml 命名,放在類路徑下(Idea 放在 resources 下)
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<!-- SessionFactory,相當于之前學習連接池配置 -->
<session-factory>
<!-- 1 基本4項 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///db01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
</session-factory>
</hibernate-configuration>
<a id="2"> </a>
2.實體類映射
數據庫
create database db01;
use db01;
實體類
public class User {
private Integer uid;
private String username;
private String password;
// 省略 get set 方法
}
映射實體類
可以用 xml 或者 注解來映射
xml
放在實體類同目錄下,名字和實體類相同,擴展名為 .hbm.xml
例如: ser.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.ittianyu.a_hello.User" table="t_user">
<!-- 主鍵 -->
<id name="uid">
<generator class="native"></generator>
</id>
<!-- 普通屬性 -->
<property name="username"></property>
<property name="password"></property>
</class>
</hibernate-mapping>
核心配置文件中加上映射文件位置
<mapping resource="com/ittianyu/hibernate/helloworld/User.hbm.xml"/>
注解
// name 對應表名稱
@Entity
@Table(name = "t_user")
public class User {
// 主鍵
@Id
@GeneratedValue
private Integer uid;
private String username;
private String password;
// 省略 get set 方法
}
在核心配置文件中加上映射文件位置
<mapping class="com.ittianyu.hibernate.helloworld.User" />
測試
public class HelloWorld {
@Test
public void hello() {
// username, password
User user = new User("123456", "123");
// 1.加載配置文件
Configuration configure = new Configuration().configure();
// 2.獲得session factory對象
SessionFactory sessionFactory = configure.buildSessionFactory();
// 3.創建session
Session session = sessionFactory.openSession();
// 4.開啟事務
Transaction transaction = session.beginTransaction();
// 5.保存并提交事務
session.save(user);
transaction.commit();
// 6.釋放資源
session.close();
sessionFactory.close();
}
}
<a id="3"> </a>
3.核心配置詳解
核心 api
Configuration
常用方法
- 構造方法:默認加載 hibernate.properties
- configure() 方法:默認加載 hibernate.cfg.xml
- configure(String) 方法:加載指定配置文件
手動添加映射
// 手動加載指定的配置文件
config.addResource("com/ittianyu/a_hello/User.hbm.xml");
// 手動加載指定類,對應的映射文件 User--> User.hbm.xml
config.addClass(User.class);
SessionFactory
- SessionFactory 相當于java web連接池,用于管理所有session
- 獲得方式:config.buildSessionFactory();
- sessionFactory hibernate緩存配置信息 (數據庫配置信息、映射文件,預定義HQL語句 等)
- SessionFactory線程安全,可以是成員變量,多個線程同時訪問時,不會出現線程并發訪問問題
- 開啟一個 session:
factory.openSession();
- 獲取和當前線程綁定的會話(需要配置):
factory.getCurrentSession();
<property name="hibernate.current_session_context_class">thread</property>
Session
- Session 相當于 JDBC的 Connection -- 會話
- 通過session操作PO對象 --增刪改查
- session單線程,線程不安全,不能編寫成成員變量。
- Api:
save 保存 update 更新 delete 刪除 get 通過id查詢,如果沒有 null load 通過id查詢,如果沒有拋異常 createQuery("hql") 獲得Query對象 createCriteria(Class) 獲得Criteria對象
Transaction
開啟事務 beginTransaction()
獲得事務 getTransaction()
提交事務:commit()
回滾事務:rollback()
和 spring 整合后,無需手動管理
Query
- hibernate執行hql語句
- hql語句:hibernate提供面向對象查詢語句,使用對象(類)和屬性進行查詢。區分大小寫。
- 獲得
session.createQuery("hql")
- 方法:
list() 查詢所有 uniqueResult() 獲得一個結果。如果沒有查詢到返回null,如果查詢多條拋異常。 setFirstResult(int) 分頁,開始索引數startIndex setMaxResults(int) 分頁,每頁顯示個數 pageSize
Criteria
- QBC(query by criteria),hibernate提供純面向對象查詢語言,提供直接使用PO對象進行操作。
- 獲得方式:Criteria criteria = session.createCriteria(User.class);
- 條件
criteria.add(Restrictions.eq("username", "tom")); Restrictions.gt(propertyName, value) 大于 Restrictions.ge(propertyName, value) 大于等于 Restrictions.lt(propertyName, value) 小于 Restrictions.le(propertyName, value) 小于等于 Restrictions.like(propertyName, value) 模糊查詢,注意:模糊查詢值需要使用 % _
工具類
public class HibernateUtils {
private static SessionFactory sessionFactory;
static {
Configuration configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
sessionFactory.close();
}
});
}
public static Session openSession() {
return sessionFactory.openSession();
}
public static Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
public static void main(String[] args) {
Session session = openSession();
System.out.println(session);
session.close();
}
}
核心配置
基本配置
<!-- SessionFactory,相當于之前學習連接池配置 -->
<session-factory>
<!-- 1 基本4項 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///h_day01_db</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
<!-- 2 與本地線程綁定 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 3 方言:為不同的數據庫,不同的版本,生成sql語句(DQL查詢語句)提供依據
* mysql 字符串 varchar
* orcale 字符串 varchar2
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 4 sql語句 -->
<!-- 顯示sql語句 -->
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<!-- 5 自動創建表(了解) ,學習中使用,開發不使用的。
* 開發中DBA 先創建表,之后根據表生產 PO類
* 取值:
update:【】
如果表不存在,將創建表。
如果表已經存在,通過hbm映射文件更新表(添加)。(映射文件必須是數據庫對應)
表中的列可以多,不負責刪除。
create :如果表存在,先刪除,再創建。程序結束時,之前創建的表不刪除。【】
create-drop:與create幾乎一樣。如果factory.close()執行,將在JVM關閉同時,將創建的表刪除了。(測試)
validate:校驗 hbm映射文件 和 表的列是否對應,如果對應正常執行,如果不對應拋出異常。(測試)
-->
<property name="hibernate.hbm2ddl.auto">create</property>
<!-- 6 java web 6.0 存放一個問題
* BeanFactory 空指針異常
異常提示:org.hibernate.HibernateException: Unable to get the default Bean Validation factory
* 解決方案:取消bean校驗
-->
<property name="javax.persistence.validation.mode">none</property>
<!-- 添加映射文件
<mapping >添加映射文件
resource 設置 xml配置文件 (addResource(xml))
class 配置類 (addClass(User.class)) 配置的是全限定類名
-->
<mapping resource="com/ittianyu/a_hello/User.hbm.xml"/>
</session-factory>
主鍵種類
- 自然主鍵: 在業務中,某個屬性符合主鍵的三個要求.那么該屬性可以作為主鍵列.
- 代理主鍵: 在業務中,不存符合以上3個條件的屬性,那么就增加一個沒有意義的列.作為主鍵.
類型對應
Java數據類型 | Hibernate數據類型 | 標準SQL數據類型(不同DB有差異) |
---|---|---|
byte、java.lang.Byte | byte | TINYINT |
short、java.lang.Short | short | SMALLINT |
int、java.lang.Integer | integer | INGEGER |
long、java.lang.Long | long | BIGINT |
float、java.lang.Float | float | FLOAT |
double、java.lang.Double | double | DOUBLE |
java.math.BigDecimal | big_decimal | NUMERIC |
char、java.lang.Character | character | CHAR(1) |
boolean、java.lang.Boolean | boolean | BIT |
java.lang.String | string | VARCHAR |
boolean、java.lang.Boolean | yes_no | CHAR(1)('Y'或'N') |
boolean、java.lang.Boolean | true_false | CHAR(1)('Y'或'N') |
java.util.Date、java.sql.Date | date | DATE |
java.util.Date、java.sql.Time | time | TIME |
java.util.Date、java.sql.Timestamp | timestamp | TIMESTAMP |
java.util.Calendar | calendar | TIMESTAMP |
java.util.Calendar | calendar_date | DATE |
byte[] | binary | VARBINARY、BLOB |
java.lang.String | text | CLOB |
java.io.Serializable | serializable | VARBINARY、BLOB |
java.sql.Clob | clob | CLOB |
java.sql.Blob | blob | BLOB |
java.lang.Class | class | VARCHAR |
java.util.Locale | locale | VARCHAR |
java.util.TimeZone | timezone | VARCHAR |
java.util.Currency | currency | VARCHAR |
普通屬性
<hibernate-mapping>
package 用于配置PO類所在包
例如: package="com.ittianyu.d_hbm"
<class> 配置 PO類 和 表 之間對應關系
name:PO類全限定類名
例如:name="com.ittianyu.d_hbm.Person"
如果配置 package,name的取值可以是簡單類名 name="Person"
table : 數據庫對應的表名
dynamic-insert="false" 是否支持動態生成insert語句
dynamic-update="false" 是否支持動態生成update語句
如果設置true,hibernate底層將判斷提供數據是否為null,如果為null,insert或update語句將沒有此項。
普通字段
<property>
name : PO類的屬性
column : 表中的列名,默認name的值相同
type:表中列的類型。默認hibernate自己通過getter獲得類型,一般情況不用設置
取值1: hibernate類型
string 字符串
integer 整形
取值2: java類型 (全限定類名)
java.lang.String 字符串
取值3:數據庫類型
varchar(長度) 字符串
int 整形
<property name="birthday">
<column name="birthday" sql-type="datetime"></column>
</property>
javabean 一般使用類型 java.util.Date
jdbc規范提供3中
java類型 mysql類型
java.sql.Date date
java.sql.time time
java.sql.timestamp timestamp
null datetime
以上三個類型都是java.util.Date子類
length : 列的長度。默認值:255
not-null : 是否為null
unique : 是否唯一
access:設置映射使用PO類屬性或字段
property : 使用PO類屬性,必須提供setter、getter方法
field : 使用PO類字段,一般很少使用。
insert 生成insert語句時,是否使用當前字段。
update 生成update語句時,是否使用當前字段。
默認情況:hibernate生成insert或update語句,使用配置文件所有項
注意:配置文件如果使用關鍵字,列名必須使用重音符
主鍵
<id>配置主鍵
name:屬性名稱
access="" 設置使用屬性還是字段
column="" 表的列名
length="" 長度
type="" 類型
<generator> class屬性用于設置主鍵生成策略
1.increment 由hibernate自己維護自動增長
底層通過先查詢max值,再+1策略
不建議使用,存在線程并發問題
2.identity hibernate底層采用數據庫本身自動增長列
例如:mysql auto_increment
3.sequence hibernate底層采用數據庫序列
例如:oracle 提供序列
4.hilo
</generator>
5.native 根據底層數據庫的能力選擇 identity、sequence 或者 hilo 中的一個?!尽?##以上策略使用整形,long, short 或者 int 類型
6.uuid 采用字符串唯一值【】
##以上策略 代理主鍵,有hibernate維護。
7.assigned 自然主鍵,由程序自己維護。【】
<a id="4"> </a>
4.一級緩存
對象狀態
三種狀態
- 瞬時態:transient,session沒有緩存對象,數據庫也沒有對應記錄。
OID特點:沒有值 - 持久態:persistent,session緩存對象,數據庫最終會有記錄。(事務沒有提交)
OID特點:有值 - 脫管態:detached,session沒有緩存對象,數據庫有記錄。
OID特點:有值
轉換
一級緩存
一級緩存:又稱為session級別的緩存。當獲得一次會話(session),hibernate在session中創建多個集合(map),用于存放操作數據(PO對象),為程序優化服務,如果之后需要相應的數據,hibernate優先從session緩存中獲取,如果有就使用;如果沒有再查詢數據庫。當session關閉時,一級緩存銷毀。
@Test
public void demo02(){
//證明一級緩存
Session session = factory.openSession();
session.beginTransaction();
//1 查詢 id = 1
User user = (User) session.get(User.class, 1);
System.out.println(user);
//2 再查詢 -- 不執行select語句,將從一級緩存獲得
User user2 = (User) session.get(User.class, 1);
System.out.println(user2);
session.getTransaction().commit();
session.close();
}
可以調用方法清除一級緩存
//清除
//session.clear();
session.evict(user);
快照
與一級緩存一樣的存放位置,對一級緩存數據備份。保證數據庫的數據與 一級緩存的數據必須一致。如果一級緩存修改了,在執行commit提交時,將自動刷新一級緩存,執行update語句,將一級緩存的數據更新到數據庫。
當緩存和數據庫數據不一樣且在提交之前,可以調用 refresh 強制刷新緩存。
<a id="5"> </a>
5.關系映射
一對一
一對一關系一般是可以整合成一張表,也可以分成兩張表。
維護兩張表的關系可以選擇外鍵也可以選擇讓主鍵同步。
實體類
Address.java
public class Address {
private Integer id;
private String name;
private Company company;
// 省略 get set
}
Company.java
public class Company {
private Integer id;
private String name;
private Address address;
// 省略 get set
}
外鍵維護關系
Address.hbm.xml
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<!--
主鍵與外鍵表的主鍵同步
-->
<generator class="foreign">
<param name="property">company</param>
</generator>
</id>
<property name="name" column="name"/>
<!--
需要在同步主鍵的一方加上 constrained="true" 使用給主鍵加上外鍵約束
-->
<one-to-one name="company" class="Company" constrained="true" />
</class>
Company.hbm.xml
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Company" table="t_company_ref" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
one-to-one 中使用了 property-ref :當前類哪個屬性是引用外鍵
放棄維護外鍵
-->
<one-to-one name="address" class="Address" property-ref="company" />
</class>
主鍵同步關系
Address.hbm.xml
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<!--
主鍵與外鍵表的主鍵同步
-->
<generator class="foreign">
<param name="property">company</param>
</generator>
</id>
<property name="name" column="name"/>
<!--
需要在同步主鍵的一方加上 constrained="true" 使用給主鍵加上外鍵約束
-->
<one-to-one name="company" class="Company" constrained="true" />
</class>
Company.hbm.xml
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Company" table="t_company_sync" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
在另一個表需要修改主鍵生成策略為 外鍵
-->
<one-to-one name="address" class="Address" />
</class>
一對多
實體類
Customer.java
public class Customer {
private Integer id;
private String name;
private Set<Order> orders = new HashSet<>();
// 省略 get set
}
Order.java
public class Order {
private Integer id;
private String name;
private Customer customer;
// 省略 get set
}
映射文件
Customer.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.ittianyu.hibernate.onetomany">
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Customer" table="t_customer" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
inverse 為 true 表示放棄維護關系,留給對方來維護,
一般是一對多中 一的一方放棄,由多的一放維護,
這個時候刪除對象時,需要手動將關聯的對象外鍵引用移除
-->
<set name="orders" inverse="true">
<key column="cid"></key>
<one-to-many class="Order" />
</set>
</class>
</hibernate-mapping>
Order.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.ittianyu.hibernate.onetomany">
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Order" table="t_order" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<many-to-one name="customer" column="cid" class="Customer" />
</class>
</hibernate-mapping>
多對多
實體類
Course.java
public class Course {
private Integer id;
private String name;
private Set<Student> students = new HashSet<>();
// 省略 get set
}
Student.java
public class Student {
private Integer id;
private String name;
private Set<Course> courses = new HashSet<>();
// 省略 get set
}
映射文件
Course.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.ittianyu.hibernate.manytomany">
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Course" table="t_course" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
many to many 中,需要給 set 加上 table 名
放棄維護外鍵
-->
<set name="students" table="t_student_course" inverse="true">
<key column="cid"></key>
<many-to-many class="Student" column="sid" />
</set>
</class>
</hibernate-mapping>
Student.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.ittianyu.hibernate.manytomany">
<!--
dynamic-insert 和 dynamic-update 為 true 時,sql語句中只有值變化或者不為空的屬性才會加上,用于更新部分屬性
-->
<class name="Student" table="t_studennt" dynamic-insert="true" dynamic-update="true">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="name" column="name"/>
<!--
many to many 中,需要給 set 加上 table 名
外鍵表由 student 維護,并啟用級聯,所以 course 中要放棄維護
-->
<set name="courses" table="t_student_course" cascade="save-update">
<key column="sid"></key>
<many-to-many class="Course" column="cid" />
</set>
</class>
</hibernate-mapping>
級聯
cascade 表示指定級聯操作的類型。
- save-update : 增加或更新 A 時,自動增加或更新 B。
- delete : 刪除 A 時,自動刪除 B
- all : 上面兩項效果疊加
- delete-orphan (孤兒刪除) : 刪除所有和當前對象解除關聯關系的對象
- all-delete-orphan : all + delete-orphan 效果疊加
<set name="courses" table="t_student_course" cascade="save-update">
<key column="sid"></key>
<many-to-many class="Course" column="cid" />
</set>
<a id="6"> </a>
6.抓取策略
檢索方式
- 立即檢索:立即查詢,在執行查詢語句時,立即查詢所有的數據。
- 延遲檢索:延遲查詢,在執行查詢語句之后,在需要時在查詢。(懶加載)
檢索策略
- 類級別檢索:當前的類的屬性獲取是否需要延遲。
- 關聯級別的檢索:當前類 關聯 另一個類是否需要延遲。
類級別檢索
- get:立即檢索。get方法一執行,立即查詢所有字段的數據。
- load:延遲檢索。默認情況,load方法執行后,如果只使用OID的值不進行查詢,如果要使用其他屬性值將查詢??梢耘渲檬欠裱舆t檢索:
<class lazy="true | false"> lazy 默認值true,表示延遲檢索,如果設置false表示立即檢索。
關聯級別檢索
容器<set> 提供兩個屬性:fetch、lazy,用于控制關聯檢索。
- fetch:確定使用sql格式
- join:底層使用迫切左外連接
- select:使用多個select語句(默認值)
- subselect:使用子查詢
- lazy:關聯對象是否延遲。
- false:立即
- true:延遲(默認值)
- extra:極其懶惰,調用 size 時,sql 查詢 count。(用于只需要獲取個數的時候)
批量查詢
一次加載多行數據,用于減少 sql 語句數量
<set batch-size="5">
比如: 當客戶關聯查詢訂單時,默認給每一個客戶生產一個select語句查詢訂單。開啟批量查詢后,使用in語句減少查詢訂單語句個數。
默認:select * from t_order where customer_id = ?
批量:select * from t_order where customer_id in (?,?,?,?)
檢索總結
檢索策略| 優點| 缺點| 優先考慮使用的場合
----| ----| ----
立即檢索| 對應用程序完全透明,不管對象處于持久化狀態還是游離狀態,應用程序都可以從一個對象導航到關聯的對象| (1)select語句多
(2)可能會加載應用程序不需要訪問的對象,浪費許多內存空間。| (1)類級別
(2)應用程序需要立即訪問的對象
(3)使用了二級緩存
延遲檢索| 由應用程序決定需要加載哪些對象,可以避免執行多余的select語句,以及避免加載應用程序不需要訪問的對象。因此能提高檢索性能,并節省內存空間。| 應用程序如果希望訪問游離狀態的代理類實例,必須保證她在持久化狀態時已經被初始化。| (1)一對多或者多對多關聯
(2)應用程序不需要立即訪問或者根本不會訪問的對象
表連接檢索| (1)對應用程序完全透明,不管對象處于持久化狀態還是游離狀態,都可從一個對象導航到另一個對象。
(2)使用了外連接,select語句少| (1)可能會加載應用程序不需要訪問的對象,浪費內存。
(2)復雜的數據庫表連接也會影響檢索性能。| (1)多對一或一對一關聯
(2)需要立即訪問的對象
(3)數據庫有良好的表連接性能。
<a id="7"> </a>
7.HQL
查詢所有
//1 使用簡單類名 , 存在自動導包
// * Customer.hbm.xml <hibernate-mapping auto-import="true">
// Query query = session.createQuery("from Customer");
//2 使用全限定類名
Query query = session.createQuery("from com.ittianyu.bean.Customer");
// 獲取結果
List<Customer> allCustomer = query.list();
條件查詢
//1 指定數據,cid OID名稱
// Query query = session.createQuery("from Customer where cid = 1");
//2 如果使用id,也可以(了解)
// Query query = session.createQuery("from Customer where id = 1");
//3 對象別名 ,格式: 類 [as] 別名
// Query query = session.createQuery("from Customer as c where c.cid = 1");
//4 查詢所有項,mysql--> select * from...
Query query = session.createQuery("select c from Customer as c where c.cid = 1");
Customer customer = (Customer) query.uniqueResult();
投影查詢
//1 默認
//如果單列 ,select c.cname from,需要List<Object>
//如果多列,select c.cid,c.cname from ,需要List<Object[]> ,list存放每行,Object[]多列
// Query query = session.createQuery("select c.cid,c.cname from Customer c");
//2 將查詢部分數據,設置Customer對象中
// * 格式:new Customer(c.cid,c.cname)
// * 注意:Customer必須提供相應的構造方法。
// * 如果投影使用oid,結果脫管態對象。
Query query = session.createQuery("select new Customer(c.cid,c.cname) from Customer c");
List<Customer> allCustomer = query.list();
排序
Query query = session.createQuery("from Customer order by cid desc");
List<Customer> allCustomer = query.list();
分頁
Query query = session.createQuery("from Customer");
// * 開始索引 , startIndex 算法: startIndex = (pageNum - 1) * pageSize;
// *** pageNum 當前頁(之前的 pageCode)
query.setFirstResult(0);
// * 每頁顯示個數 , pageSize
query.setMaxResults(2);
List<Customer> allCustomer = query.list();
綁定參數
Integer cid = 1;
//方式1 索引 從 0 開始
// Query query = session.createQuery("from Customer where cid = ?");
// query.setInteger(0, cid);
//方式2 別名引用 (:別名)
Query query = session.createQuery("from Customer where cid = :xxx");
// query.setInteger("xxx", cid);
query.setParameter("xxx", cid);
Customer customer = (Customer) query.uniqueResult();
聚合函數和分組
//1
// Query query = session.createQuery("select count(*) from Customer");
//2 別名
// Query query = session.createQuery("select count(c) from Customer c");
//3 oid
Query query = session.createQuery("select count(cid) from Customer");
Long numLong = (Long) query.uniqueResult();
連接查詢
//左外連接
// List list = session.createQuery("from Customer c left outer join c.orderSet ").list();
//迫切左外鏈接 (默認數據重復)
// List list = session.createQuery("from Customer c left outer join fetch c.orderSet ").list();
//迫切左外鏈接 (去重復)
List list = session.createQuery("select distinct c from Customer c left outer join fetch c.orderSet ").list();
命名查詢
Custom.hbm.xml
...
<!--局部 命名查詢-->
<query name="findAll"><![CDATA[from Customer ]]></query>
</class>
<!--全局 命名查詢-->
<query name="findAll"><![CDATA[from Customer ]]></query>
測試
//全局
//List list = session.getNamedQuery("findAll").list();
//局部
List list = session.getNamedQuery("com.ittianyu.a_init.Customer.findAll").list();
<a id="8"> </a>
8.QBC
查詢所有
List<Customer> list = session.createCriteria(Customer.class).list();
分頁查詢
Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult(10);
criteria.setMaxResults(10);
List<Order> list = criteria.list();
排序
Criteria criteria = session.createCriteria(Customer.class);
// criteria.addOrder(org.hibernate.criterion.Order.asc("age"));
criteria.addOrder(org.hibernate.criterion.Order.desc("age"));
List<Customer> list = criteria.list();
條件查詢
// 按名稱查詢:
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.eq("cname", "tom"));
List<Customer> list = criteria.list();*/
// 模糊查詢;
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
List<Customer> list = criteria.list();*/
// 條件并列查詢
Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
criteria.add(Restrictions.ge("age", 35));
List<Customer> list = criteria.list();
離線查詢
// service 層 封裝與 session 無關的 criteria
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.eq("id", 4));
// dao 層
Session session = HibernateUtils.openSession();
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List list = criteria.list();
<a id="9"> </a>
9.其他配置
c3p0(spring 整合后直接配 dataSource)
- 導入 c3p0 包
- hibernate.cfg.xml 配置
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
log4j
- 導入包
- log4j 核心包:log4j-1.2.17.jar
- 過渡jar:slf4j-log4j12-1.7.5.jar
- 導入配置文件
- log4j.properties ,此配置文件通知log4j 如何輸出日志
<a id="10"> </a>
10.事務
隔離級別
- read uncommittd,讀未提交。存在3個問題。
- read committed,讀已提交。解決:臟讀。存在2個問題。
- repeatable read ,可重復讀。解決:臟讀、不可重復讀。存在1個問題。
- serializable,串行化。單事務。沒有問題。
hibernate 中配置
<property name="hibernate.connection.isolation">4</property>
對照上面的分別是 1 2 4 8,0表示沒有事務級別
鎖
悲觀鎖
采用數據庫鎖機制。丟失更新肯定會發生。
- 讀鎖:共享鎖。
select .... from ... lock in share mode;
- 寫鎖:排他鎖。(獨占)
select ... from .... for update
Hibernate 中使用
Customer customer = (Customer) session.get(Customer.class, 1 ,LockMode.UPGRADE);
樂觀鎖
在表中提供一個字段(版本字段),用于標識記錄。如果版本不一致,不允許操作。丟失更新肯定不會發生
Hibernate 中使用
- 在PO對象(javabean)提供字段,表示版本字段。
... private Integer version; ...
- 在配置文件中增加 version
<class ...> ... <version name="version" /> ...
<a id="11"> </a>
11.二級緩存
sessionFactory 級別緩存,整個應用程序共享一個會話工廠,共享一個二級緩存。
由4部分構成:
- 類級別緩存
- 集合級別緩存
- 時間戳緩存
- 查詢緩存(二級緩存的第2大部分,三級緩存)
并發訪問策略
||
---|---
transactional| 可以防止臟讀和不可重復讀,性能低
read-write| 可以防止臟讀,更新緩存時鎖定緩存數據
nonstrict-read-write| 不保證緩存和數據庫一致,為緩存設置短暫的過期時間,減少臟讀
read-only| 適用于不會被修改的數據,并發性能高
應用場景
- 適合放入二級緩存中的數據:
很少被修改
不是很重要的數據, 允許出現偶爾的并發問題 - 不適合放入二級緩存中的數據:
經常被修改
財務數據, 絕對不允許出現并發問題
與其他應用數據共享的數據
二級緩存提供商
- EHCache: 可作為進程(單機)范圍內的緩存, 存放數據的物理介質可以是內存或硬盤, 對 Hibernate 的查詢緩存提供了支持。--支持集群。
- OpenSymphony `:可作為進程范圍內的緩存, 存放數據的物理介質可以是內存或硬盤, 提供了豐富的緩存數據過期策略, 對 Hibernate 的查詢緩存提供了支持
- SwarmCache: 可作為集群范圍內的緩存, 但不支持 Hibernate 的查詢緩存
- JBossCache:可作為集群范圍內的緩存, 支持 Hibernate 的查詢緩存
開啟二級緩存
- 導包 hibernate-ehcache-5.2.8.Final.jar
- 配置
<!--二級緩存 #hibernate.cache.region.factory_class org.hibernate.cache.internal.EhCacheRegionFactory --> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
使用二級緩存
類緩存
<!--
類緩存
-->
<class-cache class="com.ittianyu.hibernate.onetomany.Order" usage="read-only"/>
<class-cache class="com.ittianyu.hibernate.onetomany.Customer" usage="read-only"/>
集合緩存
<collection-cache collection="com.ittianyu.hibernate.onetomany.Customer.orders" usage="read-only" />
查詢緩存
將HQL語句 與 查詢結果進行綁定。通過HQL相同語句可以緩存內容。
-
配置
#hibernate.cache.use_query_cache true 啟用 HQL查詢緩存 <property name="hibernate.cache.use_query_cache">true</property>
-
使用
Query query = session.createQuery("from Customer"); query.setCacheable(true);// 標記為緩存