配置文件的詳解
映射文件
名稱:自定義 格式爲xml 建議: 類名.hbm.xml
路徑:自定義 建議放在domain下
導入約束
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
內容:
類和表的映射關係
class標籤
name屬性:類的全限定名
table屬性:表名(若表名和類名同樣的話,table屬性能夠省略不寫)
屬性和主鍵字段的映射關係
id標籤
name屬性:屬性名稱
column屬性:主鍵字段名稱(若列名和屬性名一致的話,column屬性能夠省略不寫)
主鍵生成策略:目前使用native
屬性和普通字段的映射關係
property標籤
name屬性:
column屬性:
/////////////////////////////////////////////////
核心配置文件
名稱: 建議使用 hibernate.cfg.xml 也可使用hibernate.properties(不建議)
路徑: 建議放在 src目錄下
導入約束:
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
配置內容:
數據庫鏈接4個基本信息 : 參考\project\etc\hibernate.properties property標籤
hibernate的屬性配置 方言,是否顯示sql,是否格式化sql... property標籤
<!--
是否由hibernate來生成表的定義語句及如何生成
常見值:
update:使用表的使用,先判斷表是否存在,若不存在自動建立表.
若存在,
再判斷配置文件和表是否對應上,
若對應不上,更新表結構
若對應上,直接使用表
瞭解值:
create:每次使用表的時候,都從新建立一張表,若以前表存在則刪除
create-drop:每次使用表的時候,都從新建立一張表,若以前表存在則刪除,當此次執行完全完成的時候,刪除此表.
validate:每次使用表的時候,須要校驗表和映射文件中的配置是否同樣,若一致則使用,若不同呢則拋異常
-->
<property name="hibernate.hbm2ddl.auto">validate</property>
映射文件的路徑 mapping標籤
<mapping resource="cn/itcast/domain/Customer.hbm.xml"/>
==================================
api的詳解
Configuration:類 配置對象
做用:
★1.加載核心配置文件
(瞭解)new Configuration():默認加載src目錄下的名稱爲 hibernate.properties的配置文件(不能配置映射文件的路徑)
★new Configuration().configure():默認加載src目錄下的名稱爲 hibernate.cfg.xml的配置文件
(瞭解)new Configuration().configure(String 配置文件的路徑):加載指定路徑下的配置文件
new Configuration().configure("config/a.xml"):加載src目錄下config目錄下的a.xml
★2.建立sessionFactory
buildSessionFactory()
(瞭解)3.加載映射文件
addResource(string );
-----------------------------------
SessionFactory:接口 session工廠
SessionFactory並非輕量級的,由於通常狀況下,一個項目一般只須要一個SessionFactory就夠
做用:
1.初始化Hibernate。
2.它充當數據存儲源的代理(底層維護了一個鏈接池)
★3.並負責建立Session對象
openSession();
---------------------
抽取一個工具類
HibernateUtils
提供一個獲取session的方法
---------------------
整合c3p0
步驟:
1.導入jar包 hibernate解壓目錄下/lib/optional/c3p0/*.jar
2.在覈心配置文件中配置c3p0提供商
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
Session:接口,至關於鏈接
Session對象是有生命週期的,它以Transaction對象的事務開始和結束邊界
線程不安全,輕量級,session不要做爲成員變量.
★做用:
1.開啓事務
beginTransaction()
2.和數據庫進行交互
Serializable save(Object obj); 返回值爲此記錄的id
update(Object ob);
delete(Object ob);
OID查詢
T get(Class<T> clazz,Serializable id);經過id獲取一個記錄,轉成相應的對象
T load(Class<T> clazz,Serializable id);經過id獲取一個記錄,轉成相應的對象
區別:
(★)get:當即發送sql語句,返回值爲對象自己
load:不會當即發送sql語句,當使用該對象的非oid屬性的時候才發送sql.返回的是代理對象
延遲加載:使用的時候才發送sql.
Transaction:接口 管理事務
commit();
rollback();
*-*-*-*-*-*-*-*-*-*-*-**-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-java
持久化類
javabean+映射文件
規範:
這個類必須是一個公共的具體的類(public class)
字段私有,必須提供公共的訪問方法
必須提供一個無參構造器
必須提供一個屬性(OID)和主鍵對應
儘可能使用包裝類修飾屬性
儘可能不要使用final修飾持久化類(若使用final修飾了load和get方法同樣了)
///////////////////////////////////////
主鍵的生成策略
主鍵的分類:
代理主鍵:給對象額外添加一個字段,沒有具體的業務,只是做爲主鍵存在.推薦使用
天然主鍵:使用對象自己一個具備業務意義的字段做爲主鍵(例如:身份證號)
hibernate中的主鍵生成策略
常見的值:
increment:使用的是hibernate的自增,有問題,數據類型必須爲 int short long,不推薦使用
★identity:使用的數據庫底層的自增策略,數據類型必須爲 int short long,表明:mysql
★sequence:使用的數據庫底層的序列策略,數據類型必須爲 int short long,表明:oracle
★★native:使用的數據庫自己的策略,能夠簡單認爲判斷數據庫支持自增仍是序列.
★uuid:使用的隨機的字符串
assigned:放棄hibernate維護主鍵,咱們本身維護.
持久化對象三種狀態(持久態★)
瞬時態:對象沒有oid值,也沒有和session關聯
持久態★:對象有oid值,也和session關聯
脫管態:對象有oid值,可是沒有和session關聯
注意:
持久態:能夠自動更新數據庫
爲什麼持久態對象能自動更新數據庫?底層依賴的是hibernate的一級緩存.
一級緩存
緩存:
介於程序和硬件之間的一種介質.
做用:
讀取數據的時候,先從緩存中查詢一下,若緩存中有,直接返回數據.若緩存中沒有,再去硬件中查詢;
hibernate是對jdbc進行的封裝.效率其實並不高,因而hibernate提供了一系列的優化策略(例如:緩存,延遲加載,抓取策略...)來提供hibernate的運行效率.
hibernate中的緩存:
*一級緩存:是hibernate自帶的,必須使用的.不能卸載.它的生命週期和session同樣.因此有人也稱之爲session級別的緩存.
(瞭解)二級緩存:不是hibernate自帶的,要想使用必須先導入jar包,在編寫配置文件纔可使用(ehcache).通常使用redis替代.
它的生命週期和sessionFactory同樣,因此有人也稱之爲sessionFactory級別的緩存
hibernate中的一級緩存工做原理:
底層就是一系列的java集合.
讀取數據的時候,優先從緩存中查詢,若緩存中有直接用;若緩存中沒有,在去數據庫中查詢,且會將查詢的結果放入緩存中一份
保存或者更新數據,一樣也會講保存或者更新過的數據放入緩存中一份.
其實將一級緩存分紅了兩塊區域(緩存區和快照區)
將數據放入了緩存區一份和快照區中一份.咱們修改數據的時候,只修改緩存區中的數據
當事務提交的時候,判斷緩存區和快照區中的數據是否一致;
若一致:啥也不幹
若不一致:發送sql語句,更新數據庫
先來證實一下一級緩存的存在.
1.對一條記錄查詢兩次
2.先保存一條記錄在查詢這條記錄.
事務
事務的概念
事務的特性:
ACID
原子性
一致性
隔離性
持久性
若不考慮隔離性會產生那些讀問題:
髒讀
不可重複讀
虛讀
經過設置數據庫的隔離級別就能夠避免上面的一些問題
1 read uncommitted
2 read committed (oracle)
4 repeatable read (mysql)
8 serializable
java中應該在service層進行事務控制
如何保證service和dao使用的是同一個事務?
只須要保證他們使用的是同一個鏈接
如何保證service和dao使用的是同一個鏈接?
方式1:向下傳遞鏈接
*方式2:ThreadLocal
hibernate中的設置隔離級別:
只須要在覈心配置文件中配置 hibernate.connection.isolation 屬性
<!-- 設置事務的隔離級別 -->
<property name="hibernate.connection.isolation">4</property>
*hibernate其實對ThreadLocal進行封裝了
步驟:
1.先在覈心配置文件中配置 hibernate.current_session_context_class 屬性 值爲:thread
<!-- 開啓與線程綁定的session -->
<property name="hibernate.current_session_context_class">thread</property>
2.獲取session的時候,經過factory.getCurrentSession();
注意:
若使用getCurrentSession()獲取的session,不須要咱們手動關閉了
////////////////////////////////////
查詢的api(*)
hibernate中提供了5中常見的查詢方式
oid查詢(get和load)
對象導航查詢(明天講)
sql查詢(直接寫sql語句)
hql查詢
qbc查詢(條件查詢的)
/////////////////////
hql查詢
先獲取Query對象
session.createQuery(String hql語句)
hql語句,幾乎和sql語句同樣.是一個面向對象的查詢語句.
hql語句須要講sql中的表名和字段名使用類名和屬性名替代.
查詢全部
hql:
from Customer
方法:
list()
uniqueResult() :返回惟一值
排序查詢
hql:
from Customer order by ....
統計查詢
hql:
select count(*) from Customer ....
分頁查詢
不要在hql中體現分頁信息
方法:
setFirstResult(int 開始的索引)
setMaxResults(int 每頁顯示的大小)
條件查詢
hql:
from Customer where 屬性1 = ? and 屬性2 = ?
方法:
setParameter(int 問號索引,Object 值);
投影查詢(查詢部分屬性)
返回的是 Object[]
//投影查詢(部分屬性) 將查詢的結果封裝到對象中
/**
* 1.須要在持久化類中提供相應的構造器
* 2.改寫hql語句
* select new 類名(屬性1,屬性2) from 類
*/
//////////////////////////
qbc查詢
query by criteria:更加面向對象的查詢方式.全是api
先獲取查詢對象 Criteria
session.createCrieria(Class clazz)
查詢全部
list()
uniqueResult()
排序查詢
addOrder(Order.asc|desc(屬性名稱))
分頁查詢
setFirstResult(int 開始的索引)
setMaxResults(int 每頁顯示的大小)
統計查詢
setProjection(Projections.sum|min|max|avg|count("屬性名"))
setProjection(Projections.rowCount())
條件查詢
add(Restrictions.like|eq|lt|gt(屬性名,值...))
離線查詢(多條件+分頁)
脫離session使用的查詢方式.
獲取離線對象的方式
DetachedCriteria dc = DetachedCriteria.forClass(Class clazz);
他的api幾乎和criteria的api一致
咱們在web層使用離線查詢對象,
用它來判斷參數和封裝參數
咱們最後須要在dao獲取一個能夠執行的查詢對象便可
Criertia cc = dc.getExecutableCriteria(session);
sql查詢
SqlQuery ss = session.createSqlQuery(String sql語句);
方法:
list
uniqueResult
setFirstResult
setMaxResults
setParameter
返回值都是:
object[]
若想要將結果封裝成某個對象
addEnity(Class clazz)mysql
*-*-*--*-*--*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*web
多表操做:
常見的關係:
一對多
多對多
實體之間的關係:
一對多:用戶和訂單,分類和商品,訂單和訂單項,客戶和聯繫人
多對多:學生和選課,訂單和商品,角色和用戶
一對一:公司和註冊地,合法夫妻,居民和身份證號
數據庫中表的設計:
一對多:根據須要在多表中添加一個外鍵,指向一表的鍵(通常是主鍵)
多對多:引入一張中間表,裏面須要存放兩位兩張表的主鍵.
一對一:
解決的方案:
1.合二爲一
2.惟一外鍵對應(就是把他們當作是一個特殊的一對多,在其中的一張表中添加外鍵,且給這個外鍵提供惟一約束)
3.主鍵對應
java中的實體設計:
一對多:
客戶和聯繫人
根據須要
在一的一方添加多的一方的集合
在多的一方添加一個一方的對象
多對多:
放入一個對方的集合便可
一對一:
放入一個對方的對象便可
案例1:一對多
客戶和聯繫人
1.建立數據庫
2.建立對應的類
根據須要
在一的一方添加多的一方的集合
在多的一方添加一個一方的對象
3.建立映射文件
在多的一方配置many-to-one標籤
name屬性:一的一方在多的一方中的對象屬性名稱
class屬性:一的一方的全限定名
column屬性:外鍵的字段名稱
在一的一方配置set標籤
name屬性:多的一方在一的一方中集合的屬性名稱
key標籤:
column屬性:外鍵的字段名稱
one-to-many標籤
class屬性:多的一方的全限定名
別忘記去核心配置文件中加載兩個映射文件
4.測試代碼
設計兩個客戶:大雄 老金
設計三個聯繫人:
大雄:熊大和熊二
老金:金三胖
有一個小小的問題:
保存的時候還發送幾條update語句?(一會講)
沒有添加任何配置,能不能只保存客戶,將他關聯的聯繫人也進行保存呢?瞬時對象異常
--------------------------
級聯操做:
級聯:操做主體的時候,將其關聯的對象也進行相同的操做.
級聯具備方向性
級聯保存更新操做:
★保存客戶,將他關聯的聯繫人也進行保存
客戶是主體,就須要在主體的一方配置級聯保存更新,在映射文件中set標籤上配置 cascade屬性 值爲:save-update
保存聯繫人,將他關聯的客戶也進行保存
聯繫人是主體,就須要在主體的一方配置級聯保存更新,在映射文件中many-to-one標籤上配置 cascade屬性 值爲:save-update
級聯刪除操做:
★刪除客戶的時候,將他關聯的聯繫人也進行刪除
客戶是主體,就須要在主體的一方配置級聯刪除,在映射文件中set標籤上配置 cascade屬性 值爲:delete 若還有其餘的級聯操做,用","隔開
刪除聯繫人的時候,將他關聯的客戶也進行刪除
聯繫人是主體,就須要在主體的一方配置級聯刪除,在映射文件中many-to-one標籤上配置 cascade屬性 值爲:delete 若還有其餘的級聯操做,用","隔開
-------------------------
★默認刪除
刪除一的一方的時候:(必須先查詢再刪除)
若咱們使用web階段的只是直接刪一的一方的會報錯;
hibernate中會先將從表中的對應數據的外鍵置爲null,而後再刪除主表中的數據.
冗餘sql(多餘sql)
咱們保存數據的時候 先保存了客戶,而後保存了聯繫人,發現除了insert語句以外,還發送幾條update語句
這個操做不影響結果
緣由:
雙方都維護外鍵 且 雙方進行了關聯操做
解決方案:
方案1:讓其中的一方放棄外鍵維護
方案2:單向關聯
咱們在開發中使用讓其中的一方放棄外鍵維護,誰放棄呢?
必須是一的一方放棄.
只須要在一的一方的映射文件中set標籤上配置 inverse="true"
cascade決定的是 操做主體的時候 是否 也操做關聯方
inverse決定的是 保存一的一方的的時候 多表中是否有外鍵
開發中:
通常都會在一的一方添加inverse 且設置cascade=save-update
案例2:多對多
用戶和角色
1.建立實體類
放入一個對方的集合
2.建立映射文件
set標籤
name屬性:對方在本身中的集合屬性名稱
table屬性:中間表表名
key標籤
column屬性:本身在中間表的外鍵信息
many-to-many標籤
class屬性:對方的全限定名
column屬性:對方在中間表中的外鍵信息
在覈心配置文件中加載映射文件
3.測試代碼
讓其中的一方放棄外鍵維護
開發中 通常讓被動方放棄
級聯保存或更新(瞭解中理解)
保存用戶的時候,將其關聯的角色也進行相同的操做
用戶是主體,須要在用戶的映射文件中配置級聯保存更新 set標籤上配置
級聯刪除:(瞭解中的瞭解)
刪除用戶的時候,將其關聯的角色也進行相同的操做
用戶是主體,須要在用戶的映射文件中配置級聯刪除 set標籤上配置
★默認刪除:(★)
先刪除中間表中的相應的記錄,而後再刪除對應表中的記錄.
其餘的操做:
例如:
給一個用戶添加角色
查詢用戶
查詢某個角色
給用戶的roles中添加角色便可
給一個用戶刪除角色
給一個用戶切換角色
注意:
hibernate是一個orm框架.查詢到的數據都是持久態數據.
擴展:
一對一:(使用惟一外鍵對應)
放入對方的對象
映射文件
使用one-to-one標籤 且在外鍵上添加一個 constrained
延遲加載和對象導航查詢
需求:
想知道客戶1的聯繫人個數
Customer c = session.get(Customer.class,1L);
c.getLinkmans().size();
想知道聯繫人1的客戶名稱
Linkman l = session.get(Linkman.class,1L);
l.getCustomer().getCust_name();
延遲加載:
使用的時候採起發送sql查詢
客戶1的聯繫人個數
由一的一方查詢多的一方默認使用的延遲加載.(開發中咱們也是這樣作的)
能不能查詢客戶的時候立馬把聯繫人也查詢出來.(瞭解)
在客戶的映射文件中的set標籤上設置一個屬性(lazy屬性) 值爲false便可
聯繫人1的客戶名稱
有多的一方查詢一的一方的時候默認使用的也是延遲加載
開發中咱們經過多的一方查詢一的一方通常使用的是當即加載. (★)
在聯繫人的映射文件中 many-to-one標籤,上設置一個屬性(lazy屬性) 值爲false便可
在set標籤上lazy屬性的常見值有
true:使用延遲加載 (默認值) ★
false:使用當即加載
在many-to-one標籤上lazy屬性的經常使用值爲 false
*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-redis
註解方式(JPA)
jpa是規範(接口),hibernate是其中的一套實現類而已
JPA:
單表操做:
環境搭建:
1.導入jar包
如果純jpa操做.須要在以前包的基礎之上額外導入一個 entitymanager.jar jar報目錄:解壓目錄/lib/jpa/hibernate-entitymanager-5.0.7.Final.jar
2.編寫核心配置文件(和以前hibernate.cfg.xml中內容大體相同)
名稱:persistence.xml
路徑:src/META-INF
導入約束:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
</persistence>
內容:
數據庫的單元
規範的實現提供商.(省略)
註解類的路徑(省略)
數據庫的鏈接信息.
hibernate的配置
導入c3p0配置
不用導入(隔離級別,事務session綁定線程配置)
注意:
最後修改一下 transaction-type="RESOURCE_LOCAL" 使用數據庫本地事務
3.抽取工具類(相似於以前的hibernateUtils) JPAUtils
以前提供了一個私有的靜態sessionfactory對象,今天提供一個 entityManagerFactory
以前還提供了一個獲取session的方法,今天提供一個獲取 EntityManager的方法
4.建立持久化類
類上的註解
@Entity //聲明本身是一個持久化類
@Table(name="cst_customer") //聲明對應的表
oid屬性上的註解
@Id //聲明它是oid
@Column(name="cust_id")//聲明對應的字段名稱 若字段名稱屬性名稱一致的話name能夠省略不寫 或者Column註解直接不寫
@GeneratedValue(strategy=GenerationType.IDENTITY)//聲明主鍵生成策略
普通屬性上的註解
@Column(name="cust_name")//聲明對應的字段名稱 若字段名稱屬性名稱一致的話name能夠省略不寫 或者Column註解直接不寫
5.入門代碼
//開啓事務
EntityTransaction tx = em.getTransaction();
tx.begin();
//保存
persist(obj);
經常使用的api
保存: persist(Object obj)
查詢:
find(Class clazz,Object id); 當即查詢
getReference(Class clazz,Object id); 延遲加載
更新:
方式1:快照方式
方式2:強制更新,將脫管態數據強制更新到數據庫中 merge(Object obj)
刪除:
remove();
查詢的api:
jpql查詢:Java持久化查詢語言
獲取查詢對象
em.createQuery(jpql語句)
查詢全部
getResultList()
getSingleResult()
分頁查詢
同樣
排序查詢
語句 + order by
統計查詢
同樣
條件查詢
問號佔位符
api
setParameter(int 第幾個問號,Object obj)
投影查詢
同樣
多表的操做:
一對多
注意:屬性名稱儘可能使用駝峯式 儘可能不要出現"_"
1.建立持久化類
多的一方
/**
* 配置多對一 @ManyToOne
* targetEntity屬性:一的一方的字節碼對象,若在對方中已經配置過,能夠省略不寫.
*
* 配置外鍵字段信息@JoinColumn
* name屬性:外鍵的字段名稱
* referencedColumnName屬性:指向的主表的鍵名稱(通常是主鍵,如果主鍵的話能夠省略不寫的)
*
* 注意:
* @JoinColumn 和 mappedBy 互斥
*/
@ManyToOne(targetEntity=Customer.class)
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
private Customer customer;
一的一方
/**
* @OneToMany 配置一對多
* targetEntity屬性:多的一方的字節碼對象
* mappedBy屬性:本身在多的一方中的屬性名稱(至關於放棄外鍵維護權)
* 如果雙方都配置映射關係的話必須在放棄的一方使用.
*/
@OneToMany(targetEntity=Linkman.class,mappedBy="customer")
private Set<Linkman> linkmans = new HashSet<Linkman>();
2.測試保存
---------------
級聯操做
在@OneToMany或者@ManyToOne標籤上配置 cascade={CasCadeType.Persist | merge | all | remove}
默認刪除:
就會報錯.
咱們能夠模擬一下模擬刪除,具體狀況看代碼.
///////////////////////
多對多
建立持久化類
被動方
@ManyToMany
targetEntity屬性:對方的字節碼對象
mappedBy屬性:本身在對方中的屬性名稱,放棄外鍵維護權
例如:
@ManyToMany(targetEntity=User.class,mappedBy="roles")
private Set<User> users = new HashSet<User>();
主動方
@ManyToMany
targetEntity屬性:對方的字節碼對象
@JoinTable :添加一張中間表
name屬性:中間表表名
JoinColumns屬性:本身在中間表中的外鍵信息 使用@JoinColumn
InverseJoinColumns屬性:對方在中間表中的外鍵信息
例如:
@ManyToMany
@JoinTable(name="sys_user_role",
joinColumns={
@JoinColumn(name="user_num",referencedColumnName="user_id")
},
inverseJoinColumns={
@JoinColumn(name="role_num",referencedColumnName="role_id")
}
)
private Set<Role> roles=new HashSet<Role>();
sql