如有轉(zhuǎn)載瓶颠,請申明:
轉(zhuǎn)載自 IT天宇: http://www.reibang.com/p/50964e92c5fb
前言
博客正式轉(zhuǎn)到簡書拟赊,實(shí)在忍受不了 CSDN 的模版,有廣告或界面難看還可以忍步清,但有些模版還有 bug要门,作為程序員忍無可忍,修個(gè) bug 真的有那么難嗎廓啊,這么多年了欢搜。
接著上篇,先說個(gè)段子谴轮。
三四年前如果有人問我 Android/Ios 如何入門炒瘟,我可能會(huì)推薦上百 G 的資料,但如果現(xiàn)在問我第步,我只會(huì)推薦一本書《app開發(fā)從上架到上吊》
你可能覺得我危言聳聽疮装,但今年的移動(dòng)開發(fā)行情真的很差,看到很多幾年經(jīng)驗(yàn)的一個(gè)月才收到幾個(gè)面試通知粘都,沒有經(jīng)驗(yàn)的就更絕望了廓推。
好吧,今天不是來討論行情的翩隧。
前段時(shí)間寫了一篇 struts2 的筆記樊展,有好心的老司機(jī)告訴我,struts2 已經(jīng)被拋棄了堆生。但話是這么說专缠,面試的時(shí)候,難免碰到問 struts2淑仆,如果這個(gè)時(shí)候表現(xiàn)得一臉懵逼涝婉,那估計(jì) offer 就沒有了。所以雖然現(xiàn)在 hibernate 用得不多了蔗怠,但還是得復(fù)習(xí)一下墩弯。
目錄
- 環(huán)境搭建
- 實(shí)體類映射
- 核心配置詳解
- 一級緩存
- 關(guān)系映射
- 抓取策略
- HQL
- QBC
- 其他配置
- 事務(wù)
- 二級緩存
正文
<a id="1"></a>
1.環(huán)境搭建
導(dǎo)包
根據(jù)需要選擇手動(dòng)導(dǎo)入 jar 包,或者用依賴管理工具寞射。
- 下載
http://hibernate.org/orm/downloads/
必須導(dǎo)入的是lib\required
下面 jar 包 - Gradle
compile "org.hibernate:hibernate-core:5.2.9.Final"
此外最住,為了連接數(shù)據(jù)庫,還需要數(shù)據(jù)庫的驅(qū)動(dòng)包怠惶。
配置
核心配置文件以 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轧粟,相當(dāng)于之前學(xué)習(xí)連接池配置 -->
<session-factory>
<!-- 1 基本4項(xiàng) -->
<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.實(shí)體類映射
數(shù)據(jù)庫
create database db01;
use db01;
實(shí)體類
public class User {
private Integer uid;
private String username;
private String password;
// 省略 get set 方法
}
映射實(shí)體類
可以用 xml 或者 注解來映射
xml
放在實(shí)體類同目錄下策治,名字和實(shí)體類相同脓魏,擴(kuò)展名為 .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 對應(yīng)表名稱
@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.創(chuàng)建session
Session session = sessionFactory.openSession();
// 4.開啟事務(wù)
Transaction transaction = session.beginTransaction();
// 5.保存并提交事務(wù)
session.save(user);
transaction.commit();
// 6.釋放資源
session.close();
sessionFactory.close();
}
}
<a id="3"> </a>
3.核心配置詳解
核心 api
Configuration
常用方法
- 構(gòu)造方法:默認(rèn)加載 hibernate.properties
- configure() 方法:默認(rèn)加載 hibernate.cfg.xml
- configure(String) 方法:加載指定配置文件
手動(dòng)添加映射
// 手動(dòng)加載指定的配置文件
config.addResource("com/ittianyu/a_hello/User.hbm.xml");
// 手動(dòng)加載指定類,對應(yīng)的映射文件 User--> User.hbm.xml
config.addClass(User.class);
SessionFactory
- SessionFactory 相當(dāng)于java web連接池通惫,用于管理所有session
- 獲得方式:config.buildSessionFactory();
- sessionFactory hibernate緩存配置信息 (數(shù)據(jù)庫配置信息茂翔、映射文件,預(yù)定義HQL語句 等)
- SessionFactory線程安全履腋,可以是成員變量珊燎,多個(gè)線程同時(shí)訪問時(shí),不會(huì)出現(xiàn)線程并發(fā)訪問問題
- 開啟一個(gè) session:
factory.openSession();
- 獲取和當(dāng)前線程綁定的會(huì)話(需要配置):
factory.getCurrentSession();
<property name="hibernate.current_session_context_class">thread</property>
Session
- Session 相當(dāng)于 JDBC的 Connection -- 會(huì)話
- 通過session操作PO對象 --增刪改查
- session單線程遵湖,線程不安全悔政,不能編寫成成員變量。
- Api:
save 保存 update 更新 delete 刪除 get 通過id查詢延旧,如果沒有 null load 通過id查詢谋国,如果沒有拋異常 createQuery("hql") 獲得Query對象 createCriteria(Class) 獲得Criteria對象
Transaction
開啟事務(wù) beginTransaction()
獲得事務(wù) getTransaction()
提交事務(wù):commit()
回滾事務(wù):rollback()
和 spring 整合后,無需手動(dòng)管理
Query
- hibernate執(zhí)行hql語句
- hql語句:hibernate提供面向?qū)ο蟛樵冋Z句迁沫,使用對象(類)和屬性進(jìn)行查詢芦瘾。區(qū)分大小寫。
- 獲得
session.createQuery("hql")
- 方法:
list() 查詢所有 uniqueResult() 獲得一個(gè)結(jié)果集畅。如果沒有查詢到返回null近弟,如果查詢多條拋異常。 setFirstResult(int) 分頁挺智,開始索引數(shù)startIndex setMaxResults(int) 分頁祷愉,每頁顯示個(gè)數(shù) pageSize
Criteria
- QBC(query by criteria),hibernate提供純面向?qū)ο蟛樵冋Z言逃贝,提供直接使用PO對象進(jìn)行操作谣辞。
- 獲得方式: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沐扳,相當(dāng)于之前學(xué)習(xí)連接池配置 -->
<session-factory>
<!-- 1 基本4項(xiàng) -->
<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 方言:為不同的數(shù)據(jù)庫泥从,不同的版本,生成sql語句(DQL查詢語句)提供依據(jù)
* 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 自動(dòng)創(chuàng)建表(了解) 沪摄,學(xué)習(xí)中使用躯嫉,開發(fā)不使用的。
* 開發(fā)中DBA 先創(chuàng)建表杨拐,之后根據(jù)表生產(chǎn) PO類
* 取值:
update:【】
如果表不存在祈餐,將創(chuàng)建表。
如果表已經(jīng)存在哄陶,通過hbm映射文件更新表(添加)帆阳。(映射文件必須是數(shù)據(jù)庫對應(yīng))
表中的列可以多,不負(fù)責(zé)刪除屋吨。
create :如果表存在蜒谤,先刪除山宾,再創(chuàng)建。程序結(jié)束時(shí)鳍徽,之前創(chuàng)建的表不刪除资锰。【】
create-drop:與create幾乎一樣阶祭。如果factory.close()執(zhí)行绷杜,將在JVM關(guān)閉同時(shí),將創(chuàng)建的表刪除了濒募。(測試)
validate:校驗(yàn) hbm映射文件 和 表的列是否對應(yīng)鞭盟,如果對應(yīng)正常執(zhí)行,如果不對應(yīng)拋出異常萨咳。(測試)
-->
<property name="hibernate.hbm2ddl.auto">create</property>
<!-- 6 java web 6.0 存放一個(gè)問題
* BeanFactory 空指針異常
異常提示:org.hibernate.HibernateException: Unable to get the default Bean Validation factory
* 解決方案:取消bean校驗(yàn)
-->
<property name="javax.persistence.validation.mode">none</property>
<!-- 添加映射文件
<mapping >添加映射文件
resource 設(shè)置 xml配置文件 (addResource(xml))
class 配置類 (addClass(User.class)) 配置的是全限定類名
-->
<mapping resource="com/ittianyu/a_hello/User.hbm.xml"/>
</session-factory>
主鍵種類
- 自然主鍵: 在業(yè)務(wù)中,某個(gè)屬性符合主鍵的三個(gè)要求.那么該屬性可以作為主鍵列.
- 代理主鍵: 在業(yè)務(wù)中,不存符合以上3個(gè)條件的屬性,那么就增加一個(gè)沒有意義的列.作為主鍵.
類型對應(yīng)
Java數(shù)據(jù)類型 | Hibernate數(shù)據(jù)類型 | 標(biāo)準(zhǔn)SQL數(shù)據(jù)類型(不同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類 和 表 之間對應(yīng)關(guān)系
name:PO類全限定類名
例如:name="com.ittianyu.d_hbm.Person"
如果配置 package茸习,name的取值可以是簡單類名 name="Person"
table : 數(shù)據(jù)庫對應(yīng)的表名
dynamic-insert="false" 是否支持動(dòng)態(tài)生成insert語句
dynamic-update="false" 是否支持動(dòng)態(tài)生成update語句
如果設(shè)置true,hibernate底層將判斷提供數(shù)據(jù)是否為null壁肋,如果為null号胚,insert或update語句將沒有此項(xiàng)。
普通字段
<property>
name : PO類的屬性
column : 表中的列名浸遗,默認(rèn)name的值相同
type:表中列的類型猫胁。默認(rèn)hibernate自己通過getter獲得類型,一般情況不用設(shè)置
取值1: hibernate類型
string 字符串
integer 整形
取值2: java類型 (全限定類名)
java.lang.String 字符串
取值3:數(shù)據(jù)庫類型
varchar(長度) 字符串
int 整形
<property name="birthday">
<column name="birthday" sql-type="datetime"></column>
</property>
javabean 一般使用類型 java.util.Date
jdbc規(guī)范提供3中
java類型 mysql類型
java.sql.Date date
java.sql.time time
java.sql.timestamp timestamp
null datetime
以上三個(gè)類型都是java.util.Date子類
length : 列的長度跛锌。默認(rèn)值:255
not-null : 是否為null
unique : 是否唯一
access:設(shè)置映射使用PO類屬性或字段
property : 使用PO類屬性弃秆,必須提供setter、getter方法
field : 使用PO類字段,一般很少使用菠赚。
insert 生成insert語句時(shí)盼樟,是否使用當(dāng)前字段。
update 生成update語句時(shí)锈至,是否使用當(dāng)前字段。
默認(rèn)情況:hibernate生成insert或update語句译秦,使用配置文件所有項(xiàng)
注意:配置文件如果使用關(guān)鍵字峡捡,列名必須使用重音符
主鍵
<id>配置主鍵
name:屬性名稱
access="" 設(shè)置使用屬性還是字段
column="" 表的列名
length="" 長度
type="" 類型
<generator> class屬性用于設(shè)置主鍵生成策略
1.increment 由hibernate自己維護(hù)自動(dòng)增長
底層通過先查詢max值,再+1策略
不建議使用筑悴,存在線程并發(fā)問題
2.identity hibernate底層采用數(shù)據(jù)庫本身自動(dòng)增長列
例如:mysql auto_increment
3.sequence hibernate底層采用數(shù)據(jù)庫序列
例如:oracle 提供序列
4.hilo
</generator>
5.native 根據(jù)底層數(shù)據(jù)庫的能力選擇 identity们拙、sequence 或者 hilo 中的一個(gè)「罅撸【】
##以上策略使用整形砚婆,long, short 或者 int 類型
6.uuid 采用字符串唯一值【】
##以上策略 代理主鍵,有hibernate維護(hù)突勇。
7.assigned 自然主鍵装盯,由程序自己維護(hù)〖撞觯【】
<a id="4"> </a>
4.一級緩存
對象狀態(tài)
三種狀態(tài)
- 瞬時(shí)態(tài):transient埂奈,session沒有緩存對象,數(shù)據(jù)庫也沒有對應(yīng)記錄定躏。
OID特點(diǎn):沒有值 - 持久態(tài):persistent账磺,session緩存對象,數(shù)據(jù)庫最終會(huì)有記錄痊远。(事務(wù)沒有提交)
OID特點(diǎn):有值 - 脫管態(tài):detached垮抗,session沒有緩存對象,數(shù)據(jù)庫有記錄碧聪。
OID特點(diǎn):有值
轉(zhuǎn)換
一級緩存
一級緩存:又稱為session級別的緩存冒版。當(dāng)獲得一次會(huì)話(session),hibernate在session中創(chuàng)建多個(gè)集合(map)矾削,用于存放操作數(shù)據(jù)(PO對象)壤玫,為程序優(yōu)化服務(wù),如果之后需要相應(yīng)的數(shù)據(jù)哼凯,hibernate優(yōu)先從session緩存中獲取欲间,如果有就使用;如果沒有再查詢數(shù)據(jù)庫断部。當(dāng)session關(guān)閉時(shí)猎贴,一級緩存銷毀。
@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 再查詢 -- 不執(zhí)行select語句,將從一級緩存獲得
User user2 = (User) session.get(User.class, 1);
System.out.println(user2);
session.getTransaction().commit();
session.close();
}
可以調(diào)用方法清除一級緩存
//清除
//session.clear();
session.evict(user);
快照
與一級緩存一樣的存放位置她渴,對一級緩存數(shù)據(jù)備份达址。保證數(shù)據(jù)庫的數(shù)據(jù)與 一級緩存的數(shù)據(jù)必須一致。如果一級緩存修改了趁耗,在執(zhí)行commit提交時(shí)沉唠,將自動(dòng)刷新一級緩存,執(zhí)行update語句苛败,將一級緩存的數(shù)據(jù)更新到數(shù)據(jù)庫满葛。
當(dāng)緩存和數(shù)據(jù)庫數(shù)據(jù)不一樣且在提交之前,可以調(diào)用 refresh 強(qiáng)制刷新緩存罢屈。
<a id="5"> </a>
5.關(guān)系映射
一對一
一對一關(guān)系一般是可以整合成一張表嘀韧,也可以分成兩張表。
維護(hù)兩張表的關(guān)系可以選擇外鍵也可以選擇讓主鍵同步缠捌。
實(shí)體類
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
}
外鍵維護(hù)關(guān)系
Address.hbm.xml
<!--
dynamic-insert 和 dynamic-update 為 true 時(shí)锄贷,sql語句中只有值變化或者不為空的屬性才會(huì)加上,用于更新部分屬性
-->
<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 時(shí)曼月,sql語句中只有值變化或者不為空的屬性才會(huì)加上谊却,用于更新部分屬性
-->
<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 :當(dāng)前類哪個(gè)屬性是引用外鍵
放棄維護(hù)外鍵
-->
<one-to-one name="address" class="Address" property-ref="company" />
</class>
主鍵同步關(guān)系
Address.hbm.xml
<!--
dynamic-insert 和 dynamic-update 為 true 時(shí),sql語句中只有值變化或者不為空的屬性才會(huì)加上十嘿,用于更新部分屬性
-->
<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 時(shí)因惭,sql語句中只有值變化或者不為空的屬性才會(huì)加上,用于更新部分屬性
-->
<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"/>
<!--
在另一個(gè)表需要修改主鍵生成策略為 外鍵
-->
<one-to-one name="address" class="Address" />
</class>
一對多
實(shí)體類
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 時(shí)绩衷,sql語句中只有值變化或者不為空的屬性才會(huì)加上蹦魔,用于更新部分屬性
-->
<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 表示放棄維護(hù)關(guān)系,留給對方來維護(hù)咳燕,
一般是一對多中 一的一方放棄勿决,由多的一放維護(hù),
這個(gè)時(shí)候刪除對象時(shí)招盲,需要手動(dòng)將關(guān)聯(lián)的對象外鍵引用移除
-->
<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 時(shí)畜份,sql語句中只有值變化或者不為空的屬性才會(huì)加上温眉,用于更新部分屬性
-->
<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>
多對多
實(shí)體類
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 時(shí),sql語句中只有值變化或者不為空的屬性才會(huì)加上,用于更新部分屬性
-->
<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 名
放棄維護(hù)外鍵
-->
<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 時(shí)雁仲,sql語句中只有值變化或者不為空的屬性才會(huì)加上昔字,用于更新部分屬性
-->
<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 維護(hù),并啟用級聯(lián)礼饱,所以 course 中要放棄維護(hù)
-->
<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>
級聯(lián)
cascade 表示指定級聯(lián)操作的類型坏为。
- save-update : 增加或更新 A 時(shí)究驴,自動(dòng)增加或更新 B。
- delete : 刪除 A 時(shí)匀伏,自動(dòng)刪除 B
- all : 上面兩項(xiàng)效果疊加
- delete-orphan (孤兒刪除) : 刪除所有和當(dāng)前對象解除關(guān)聯(lián)關(guān)系的對象
- 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.抓取策略
檢索方式
- 立即檢索:立即查詢洒忧,在執(zhí)行查詢語句時(shí),立即查詢所有的數(shù)據(jù)够颠。
- 延遲檢索:延遲查詢熙侍,在執(zhí)行查詢語句之后,在需要時(shí)在查詢履磨。(懶加載)
檢索策略
- 類級別檢索:當(dāng)前的類的屬性獲取是否需要延遲核行。
- 關(guān)聯(lián)級別的檢索:當(dāng)前類 關(guān)聯(lián) 另一個(gè)類是否需要延遲。
類級別檢索
- get:立即檢索蹬耘。get方法一執(zhí)行,立即查詢所有字段的數(shù)據(jù)减余。
- load:延遲檢索综苔。默認(rèn)情況,load方法執(zhí)行后位岔,如果只使用OID的值不進(jìn)行查詢如筛,如果要使用其他屬性值將查詢∈闾В可以配置是否延遲檢索:
<class lazy="true | false"> lazy 默認(rèn)值true杨刨,表示延遲檢索,如果設(shè)置false表示立即檢索擦剑。
關(guān)聯(lián)級別檢索
容器<set> 提供兩個(gè)屬性:fetch妖胀、lazy,用于控制關(guān)聯(lián)檢索惠勒。
- fetch:確定使用sql格式
- join:底層使用迫切左外連接
- select:使用多個(gè)select語句(默認(rèn)值)
- subselect:使用子查詢
- lazy:關(guān)聯(lián)對象是否延遲赚抡。
- false:立即
- true:延遲(默認(rèn)值)
- extra:極其懶惰,調(diào)用 size 時(shí)纠屋,sql 查詢 count涂臣。(用于只需要獲取個(gè)數(shù)的時(shí)候)
批量查詢
一次加載多行數(shù)據(jù),用于減少 sql 語句數(shù)量
<set batch-size="5">
比如: 當(dāng)客戶關(guān)聯(lián)查詢訂單時(shí)售担,默認(rèn)給每一個(gè)客戶生產(chǎn)一個(gè)select語句查詢訂單赁遗。開啟批量查詢后,使用in語句減少查詢訂單語句個(gè)數(shù)族铆。
默認(rèn):select * from t_order where customer_id = ?
批量:select * from t_order where customer_id in (?,?,?,?)
檢索總結(jié)
檢索策略| 優(yōu)點(diǎn)| 缺點(diǎn)| 優(yōu)先考慮使用的場合
----| ----| ----
立即檢索| 對應(yīng)用程序完全透明岩四,不管對象處于持久化狀態(tài)還是游離狀態(tài),應(yīng)用程序都可以從一個(gè)對象導(dǎo)航到關(guān)聯(lián)的對象| (1)select語句多
(2)可能會(huì)加載應(yīng)用程序不需要訪問的對象骑素,浪費(fèi)許多內(nèi)存空間炫乓。| (1)類級別
(2)應(yīng)用程序需要立即訪問的對象
(3)使用了二級緩存
延遲檢索| 由應(yīng)用程序決定需要加載哪些對象刚夺,可以避免執(zhí)行多余的select語句,以及避免加載應(yīng)用程序不需要訪問的對象末捣。因此能提高檢索性能侠姑,并節(jié)省內(nèi)存空間。| 應(yīng)用程序如果希望訪問游離狀態(tài)的代理類實(shí)例箩做,必須保證她在持久化狀態(tài)時(shí)已經(jīng)被初始化莽红。| (1)一對多或者多對多關(guān)聯(lián)
(2)應(yīng)用程序不需要立即訪問或者根本不會(huì)訪問的對象
表連接檢索| (1)對應(yīng)用程序完全透明,不管對象處于持久化狀態(tài)還是游離狀態(tài)邦邦,都可從一個(gè)對象導(dǎo)航到另一個(gè)對象安吁。
(2)使用了外連接,select語句少|(zhì) (1)可能會(huì)加載應(yīng)用程序不需要訪問的對象燃辖,浪費(fèi)內(nèi)存鬼店。
(2)復(fù)雜的數(shù)據(jù)庫表連接也會(huì)影響檢索性能。| (1)多對一或一對一關(guān)聯(lián)
(2)需要立即訪問的對象
(3)數(shù)據(jù)庫有良好的表連接性能黔龟。
<a id="7"> </a>
7.HQL
查詢所有
//1 使用簡單類名 妇智, 存在自動(dòng)導(dǎo)包
// * Customer.hbm.xml <hibernate-mapping auto-import="true">
// Query query = session.createQuery("from Customer");
//2 使用全限定類名
Query query = session.createQuery("from com.ittianyu.bean.Customer");
// 獲取結(jié)果
List<Customer> allCustomer = query.list();
條件查詢
//1 指定數(shù)據(jù),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 查詢所有項(xiàng)巍棱,mysql--> select * from...
Query query = session.createQuery("select c from Customer as c where c.cid = 1");
Customer customer = (Customer) query.uniqueResult();
投影查詢
//1 默認(rèn)
//如果單列 ,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 將查詢部分?jǐn)?shù)據(jù)到踏,設(shè)置Customer對象中
// * 格式:new Customer(c.cid,c.cname)
// * 注意:Customer必須提供相應(yīng)的構(gòu)造方法。
// * 如果投影使用oid尚猿,結(jié)果脫管態(tài)對象夭禽。
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 當(dāng)前頁(之前的 pageCode)
query.setFirstResult(0);
// * 每頁顯示個(gè)數(shù) , pageSize
query.setMaxResults(2);
List<Customer> allCustomer = query.list();
綁定參數(shù)
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();
聚合函數(shù)和分組
//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();
//迫切左外鏈接 (默認(rèn)數(shù)據(jù)重復(fù))
// List list = session.createQuery("from Customer c left outer join fetch c.orderSet ").list();
//迫切左外鏈接 (去重復(fù))
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 無關(guān)的 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)
- 導(dǎo)入 c3p0 包
- hibernate.cfg.xml 配置
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
log4j
- 導(dǎo)入包
- log4j 核心包:log4j-1.2.17.jar
- 過渡jar:slf4j-log4j12-1.7.5.jar
- 導(dǎo)入配置文件
- log4j.properties 谊路,此配置文件通知log4j 如何輸出日志
<a id="10"> </a>
10.事務(wù)
隔離級別
- read uncommittd讹躯,讀未提交。存在3個(gè)問題缠劝。
- read committed潮梯,讀已提交。解決:臟讀惨恭。存在2個(gè)問題秉馏。
- repeatable read ,可重復(fù)讀脱羡。解決:臟讀萝究、不可重復(fù)讀免都。存在1個(gè)問題。
- serializable帆竹,串行化绕娘。單事務(wù)。沒有問題栽连。
hibernate 中配置
<property name="hibernate.connection.isolation">4</property>
對照上面的分別是 1 2 4 8险领,0表示沒有事務(wù)級別
鎖
悲觀鎖
采用數(shù)據(jù)庫鎖機(jī)制。丟失更新肯定會(huì)發(fā)生秒紧。
- 讀鎖:共享鎖绢陌。
select .... from ... lock in share mode;
- 寫鎖:排他鎖。(獨(dú)占)
select ... from .... for update
Hibernate 中使用
Customer customer = (Customer) session.get(Customer.class, 1 ,LockMode.UPGRADE);
樂觀鎖
在表中提供一個(gè)字段(版本字段)熔恢,用于標(biāo)識(shí)記錄脐湾。如果版本不一致,不允許操作叙淌。丟失更新肯定不會(huì)發(fā)生
Hibernate 中使用
- 在PO對象(javabean)提供字段沥割,表示版本字段。
... private Integer version; ...
- 在配置文件中增加 version
<class ...> ... <version name="version" /> ...
<a id="11"> </a>
11.二級緩存
sessionFactory 級別緩存凿菩,整個(gè)應(yīng)用程序共享一個(gè)會(huì)話工廠,共享一個(gè)二級緩存帜讲。
由4部分構(gòu)成:
- 類級別緩存
- 集合級別緩存
- 時(shí)間戳緩存
- 查詢緩存(二級緩存的第2大部分,三級緩存)
并發(fā)訪問策略
||
---|---
transactional| 可以防止臟讀和不可重復(fù)讀衅谷,性能低
read-write| 可以防止臟讀,更新緩存時(shí)鎖定緩存數(shù)據(jù)
nonstrict-read-write| 不保證緩存和數(shù)據(jù)庫一致似将,為緩存設(shè)置短暫的過期時(shí)間获黔,減少臟讀
read-only| 適用于不會(huì)被修改的數(shù)據(jù),并發(fā)性能高
應(yīng)用場景
- 適合放入二級緩存中的數(shù)據(jù):
很少被修改
不是很重要的數(shù)據(jù), 允許出現(xiàn)偶爾的并發(fā)問題 - 不適合放入二級緩存中的數(shù)據(jù):
經(jīng)常被修改
財(cái)務(wù)數(shù)據(jù), 絕對不允許出現(xiàn)并發(fā)問題
與其他應(yīng)用數(shù)據(jù)共享的數(shù)據(jù)
二級緩存提供商
- EHCache: 可作為進(jìn)程(單機(jī))范圍內(nèi)的緩存, 存放數(shù)據(jù)的物理介質(zhì)可以是內(nèi)存或硬盤, 對 Hibernate 的查詢緩存提供了支持在验。--支持集群玷氏。
- OpenSymphony `:可作為進(jìn)程范圍內(nèi)的緩存, 存放數(shù)據(jù)的物理介質(zhì)可以是內(nèi)存或硬盤, 提供了豐富的緩存數(shù)據(jù)過期策略, 對 Hibernate 的查詢緩存提供了支持
- SwarmCache: 可作為集群范圍內(nèi)的緩存, 但不支持 Hibernate 的查詢緩存
- JBossCache:可作為集群范圍內(nèi)的緩存, 支持 Hibernate 的查詢緩存
開啟二級緩存
- 導(dǎo)包 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語句 與 查詢結(jié)果進(jìn)行綁定。通過HQL相同語句可以緩存內(nèi)容腋舌。
-
配置
#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);// 標(biāo)記為緩存