Hibernate
面向對象操作模型數據庫->阻抗不匹配
ORM(Object Relation Mapping->對象關係映射)->完成對象與關係之間的轉換
主流ORM框架:Hibernate Toplink OJB
(一)
簡單使用步驟:
詳見:http://blog.csdn.net/qq_28796345/article/details/52513814
(1)創建項目
(2)導入jar包
(3)創建實體對象Usre
(4)創建User.hbm.xml映射文件(於相應包下)
(5)創建配置文件於src下(hibernate.cfg.xml)
(6)創建測試類(初始化hibernate)
hibernate.cfg.xml:
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 數據庫驅動 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 所用數據庫 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 數據庫url -->
<property name="connection.url">jdbc:mysql:///hibernate</property>
<!-- 數據庫用戶名 -->
<property name="connection.username">root</property>
<!-- 用戶密碼 -->
<property name="connection.password">0707</property>
<!-- 創建表 -->
<property name="hbm2ddl.auto">create</property>
<!-- 映射文件位置 -->
<mapping resource="com/sw/hibernate/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
User.hbm.xml:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="com.sw.hibernate.domain">
<class name="User" table="user">
<!-- 數據庫表主鍵 column指定字段名 -->
<id name="id" column="id">
<generator class="native"/>
</id>
<!-- property爲屬性 -->
<property name="name" column="name"/>
<property name="birthday"/>
</class>
</hibernate-mapping>
工具類
初始化:
/*
*@Author swxctx
*@time 2016年9月12日
*/
package com.sw.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public final class HibernateUtil {
//工具類 用於初始化hibernate(一次初始化)
private static SessionFactory sessionFactory;
private HibernateUtil(){
}
static{
//需要進行一次初始化
Configuration cfg=new Configuration();
cfg.configure();//讀取配置文件
//初始化後得到sessionfactory
sessionFactory = cfg.buildSessionFactory();//類似於DriverManager
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession(){
return sessionFactory.openSession();
}
}
執行方法封裝:
/*
*@Author swxctx
*@time 2016年9月12日
*/
package com.sw.util;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.sw.hibernate.domain.User;
public class Tooladd {
static void addUser(User user){
Session s=null;
Transaction tx=null;
try {
//初始化
s=HibernateUtil.getSession();
//操作數據
tx=s.beginTransaction();
//保存對象
s.save(user);
//提交
tx.commit();
} catch (HibernateException he) {
// TODO: handle exception
if(tx!=null){
tx.rollback();//回滾
throw he;//拋出異常
}
}finally{
if(s!=null){
s.close();//關閉
s=null;
}
}
}
static void addUser1(User user){
Session s=null;
Transaction tx=null;
try {
//初始化
s=HibernateUtil.getSession();
//操作數據
tx=s.beginTransaction();
//保存對象
s.save(user);
//提交
tx.commit();
} finally{
if(s!=null){
s.close();//關閉
s=null;
}
}
}
}
Session接口:
get(查詢)、load(更新)、persist(保存)方法
實體對象的三種狀態與saveOrUpdate(根據id的值來確定使用save還是update方法)方法
(1)瞬時-數據庫沒有數據與之對應,超過作用域會被jvm垃圾回收。
(2)持久-數據庫沒有記錄,並且session並沒有關閉,與session有關
(3)脫管-與session沒有關係,與數據庫表有對應
HQL與Criteria
1、Hql
實例:
//查詢
static void query(String name){
Session s=null;
try {
//初始化
s=HibernateUtil.getSession();
String hql="from User as user where user.name=?";
Query query=s.createQuery(hql);
query.setString(0, name);
List<User> list=query.list();
// Object ob=query.uniqueResult();//返回object類型
for(User user:list){
System.out.println(user.getName());
}
}finally{
if(s!=null){
s.close();//關閉
s=null;
}
}
}
注:在hbm.xml文件中,應該避免屬性名與數據庫關鍵字衝突,若衝突,則在前加上反引號即可。
hql的命名參數與Query接口的分頁查詢:
from User as user where user.name=?
如上語句,如果在字段過多時則不會適用。
解決辦法:使用命名參數(爲用戶定義名字)
from User as user where user.name=:name
query.string("name",name);
Query
分頁:
Query query=s.createQuery(hql);
//從第一頁開始取結果,每頁取十條
query.setFirstResult(0);//實現分頁 第一條
query.setMaxResults(10);//每頁十條
2、Criteria(條件查詢)
實例:
static void cri(String name){
Session s=null;
try {
//初始化
s=HibernateUtil.getSession();
Criteria c=s.createCriteria(User.class);
//查找name對應的數據
c.add(Restrictions.eq("name", name));//傳入的參數(約束條件)
c.setFirstResult(0);//分頁
c.setMaxResults(10);
List<User> list=c.list();
User u=(User)c.uniqueResult();
for(User user:list){
System.out.println(user.getName());
}
}finally{
if(s!=null){
s.close();//關閉
s=null;
}
}
}
(二)
關聯
詳見:點擊打開鏈接
多對一關聯關係的映射與原理
一對多
多對一
一對一
多對多(使用中間表)->執行速率較慢,不常用
多對多關聯關係的檢索
兩個組件(對象)關聯
級聯操作-對相應的對象進行統一的操作(保存時都保存、刪除時將其數據都刪除等)
<set name="em" cascade="save-update">
<key column="depart_id"></key>
<one-to-many class="Employee"/>
</set>
inverse屬性(有序集合不能出現,不會放棄關係維護)
用於一對多中->一放棄維護與多個的關聯關係
放棄維護關係->不再更新外鍵
<list name=" " inverse="true">
(四)
繼承映射
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="com.sw.hibernate.domain">
<class name="Employee" table="Employee" discriminator-value="0">
<!-- 數據庫表主鍵 column指定字段名 -->
<id name="id" column="id">
<generator class="native"/>
</id>
<discriminator column="type" type="int"/>
<!-- property爲屬性 -->
<property name="name" column="name"/>
<!-- Department 多對一-->
<!-- depart_id 外鍵 對應於department的id-->
<many-to-one name="depart" column="depart_id" property-ref="id"/>
<!-- 對象映射 -->
<subclass name="Skiller" discriminator-value="1">
<property name="skill"/>
</subclass>
<subclass name="Sales" discriminator-value="2">
<property name="sell"/>
</subclass>
</class>
</hibernate-mapping>
每個子類映射到一張表(效率低,結構清晰)
<!-- 每個子類對應一張表 -->
<joined-subclass name="Skiller" table="skiller">
<key column="emp_id"/>
<property name="skill"/>
</joined-subclass>
<joined-subclass name="Sales" table="sales">
<key column="emp_id"/>
<property name="sell"/>
</joined-subclass>
繼承映射->1、一個類繼承體系一張表 2、每個子類各一張表
<union-subclass>->將每個表都保存完整的信息(每個子類一張表)
(五)
懶加載:Hibernate.initialize(對象)->Domain非final;
通過asm.jar與cglib.jar兩個包實現懶加載。
一對一懶加載(默認情況下hibernate會使用懶加載)->查詢主對象時不會進行懶加載
查詢重對象時,會啓用懶加載
除多對一以外,儘量不要禁用懶加載,如果禁用會產生一些意想不到的錯誤。
懶加載並不能區分空集合,當與數據表結構衝突時(數據爲空),則會出現問題。
能夠懶加載的對象都是被改寫過的代理對象,當相關聯的session沒有關閉時,訪問這些懶加載
對象(代理對象)的屬性(getId和getClass除外)。hibername會初始化這些代理,或用hibernate
.initialize(proxy)來初始化代理對象;當相關聯的session關閉後,再訪問懶加載的對象將
出現異常。
屬性亦可進行懶加載。
(六)
1、一級緩存
緩存->第一次讀取時將其存放到內存中,第二次直接從內存讀取,提高了性能。
(1)將數據放入緩存中->使用map map.put(key,user);
(2)從緩存中取出數據
(3)刪除無效數據 map.remove(key);
Session存在緩存,隨着session的關閉而消失。
第一次get(獲取)時,將數據放入緩存。
session.evict(user);//將user對象從session緩存中清除
session.clear();//清除所有緩存
使用一級緩存需要防止數據溢出(並不會限制放置的個數,直到內存溢出)
一級緩存的存在時間較短(一個請求內),所以會導致數據失效等問題,使用二級緩存可以解決這一問題。
2、二級緩存(SessionFactory級共享)
交給第三方框架處理。
通過配置文件hibernate.cfg.xml進行處理:
<!-- 打開二級緩存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 打開查詢緩存 -->
<property name="cache.use_query_cache">true</property>
<!-- 所使用的緩存機制 -->
<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
<!-- 指定需要緩存的類 -->
<class-cache usage="read-only" class="cn/itcast/hibernate/domain/User"/>
指定需要緩存的類,除了在這裏指定外,亦可在映射文件中進行配置指定:
<cache usge="read-only"/><!--放置於class內部-->
hibernate尋找數據步驟->先到以及緩存尋找,如果以及緩存不存在該數據,則到二級緩存進行尋找,
若二級緩存仍然不存在,則到數據庫中進行尋找。
分佈式緩存與中央緩存:
使用緩存的條件:
(1)讀取次數大於修改
(2)數據量不能超過內存量
(3)對數據要有獨享的控制
(4)可以容忍出現無效數據
(七)
事務
JDBC Transaction(單步事務,使用一個庫)
JTA Transaction(分佈式事務處理,使用多個庫)->跨數據庫的事務,由應用JTA容器實現。
openSession()->
getCurrentSession()->獲取當前session
ThreadLocal
OpenSessionInView
Hibernate-Mapping->多種屬性(隱藏屬性的使用)->schema
使用class類可以定義一個持久化類。
鑑別器
property屬性亦具有懶加載功能。
Hibernate映射類型。
Session是非線程安全的,生命週期較短,一般不會超過一個請求。代表一個和數據庫的連接。
SessionFactory是線程安全的,聲明週期較長,一般在整個系統內都是有效的。
保存着喝數據庫連接的相關信息。
批量更新
1、flush使一級緩存與數據庫同步。
Session.flush();
當數據量較大時,在調用clear之前先調用flush。
2、StatelessSession接口
不和一二級緩存交互,也不觸發任何事件。
N+1次查詢與懶加載。
N條記錄會出現N+1此查詢記錄。
攔截器
監聽器
本地sql查詢與命名查詢