持久層概述
Hibernate基礎
Hibernate映射
Hibernate數據檢索
Hibernate高級特性
Hibernate最佳實踐
Hibernate資源
Hibernate概述
Hibernate核心接口
Hibernate基礎配置
這幾個核心接口幾乎在任何實際開發中都會用到。通過這些接口,不僅可以存儲和獲得持久對象,並且能夠進行事務控制。
Configuration接口
SessionFactory接口
Session接口
Transaction接口
Query和Criteria接口
Configuration 接口負責管理Hibernate 的配置信息。它包括如下內容:Hibernate運行的底層信息:數據庫的URL、用戶名、密碼、JDBC驅動類,數據庫Dialect,數據庫連接池等。Hibernate映射文件(*.hbm.xml)。
Hibernate配置的兩種方法:
屬性文件(Hibernate.properties)。
調用代碼:Configuration config = new Configuration();
Xml文件(Hibernate.cfg.xml)。
調用代碼:Configuration config = new Configuration().configure();
數據庫連接的配置
Hibernate.dialect net.sf.Hibernate.dialect.MySQLDialect
Hibernate.connection.driver_class com.mysql.jdbc.Driver
Hibernate.connection.url jdbc:mysql://localhost/Hibernate
Hibernate.connection.username root
Hibernate.connection.password 111111
數據庫連接池的配置
Hibernate.connection.provider_class net.sf.Hibernate.connection.DBCPConnectionProvider
其它
Hibernate.show_sql true
Hibernate.jdbc.fetch_size 50
Hibernate.jdbc.batch_size 25
SessionFactory接口
應用程序從SessionFactory(會話工廠)裏獲得Session(會話)實例。它在多個應用線程間進行共享。通常情況下,整個應用只有唯一的一個會話工廠——例如在應用初始化時被創建。然而,如果你使用Hibernate訪問多個數據庫,你需要對每一個數據庫使用一個會話工廠。會話工廠緩存了生成的SQL語句和Hibernate在運行時使用的映射元數據。
創建SessionFactory
Configuration config=new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session接口
Session作爲貫穿Hibernate的持久化管理器核心,提供了衆多持久化方法,如:save,update,delete等。提供了透明地完成了對象的增刪查改操作(CRUD)。
Session不是線程安全的,它代表與數據庫之間的一次操作。
Session通過SessionFactory打開,在所有的工作完成後,需要關閉。
Session創建
Configuration config=new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
創建以後可以調用Session所提供的save、get、delete等方法進行持久化操作。
save
User user=new User();
user.setName(“John”);
session.save(user);
get
User user=(User)session.get(User.class,new Integer(1));
delete
User user=(User)session.get(User.class,new Integer(1));
sesson.delete(user);
Query查詢
Query query=session.createQuery(“from User user where user.name like ?”);
query.setParameter(0,”John”);
List list=query.List();
Criteria查詢
Criteria criteria=session.createCriteria(User.class”);
criteria.add(Restrictions.eq(“name”,”John”));
List list=criteria.List();
…
Transaction接口
它將應用代碼從底層的事務實現中抽象出來——這可能是一個JDBC事務,一個JTA用戶事務。這有助於保持Hibernate應用在不同類型的執行環境或容器中的可移植性
調用代碼:
Transaction tx = session.beginTransaction();
… …
tx.commit();
注:使用Hibernate進行操作時(增、刪、改)必須顯示的調用Transaction(默認:autoCommit=false)。
Query和Criteria接口
Query接口讓你方便地對數據庫及持久對象進行查詢,它可以有兩種表達方式:HQL語言或本地數據庫的SQL語句。Query經常被用來綁定查詢參數、限制查詢記錄數量,並最終執行查詢操作。
Criteria接口與Query接口非常類似,它允許你創建並執行面向對象的標準化查詢。
值得注意的是Query接口也是輕量級的,它不能在Session之外使用調用代碼:
Query接口
Query query=session.createQuery(“from User”);
List users=query.list();
Criteria接口
Criteria criteria=session.createCriteria(User.class);
criteria.add(Restrictions.eq(“name”,”John”));
criteria.add(Restrictions.eq(“sex”,new Integer(1));
List users=criteria.list();
應用示例
持久化類-User.java
public class User {
private Long id;
private String name;
private Date birthday;
private String email;
public User(){
}
public User(String name,Date birthday,String email){
… //Getter/Setter
}
映射文件-User.hbm.xml
<Hibernate-mapping>
<class name="com.test.um.User" table="TBL_USER">
<id name="id" column="ID">
<generator class="native"/>
</id>
<property name="name" column="NAME"/>
<property name="birthday" column="BIRTHDAY"/>
<property name="email" column="EMAIL"/>
</class>
</Hibernate-mapping>
應用-UserTest.java
public void testCreate() throws Exception{
Configuration cfg = new Configuration();
cfg.addURL(UserTest.class.getResource("/com/test/um/User.hbm.xml"));
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
SimpleDateFormat format = new SimpleDateFormat("yyyy-mm-dd");
User user = new User("Jack",format.parse("1980-04-12"),"[email protected]");
session.save(user);
tx.commit();
assertNotNull(user.getId());
session.clear();
User user_2 = (User)session.get(User.class,user.getId());
assertNotNull(user_2);
session.close();
}
保存用戶:session.save(user);
修改用戶:session.update(user);
保存或修改用戶:session.saveOrUpdate(user);
刪除用戶:session.delete(user);
刪除所有用戶:session.delete(“from User ”);
查詢用戶名爲“test”的用戶:
Query query = session.createQuery("from User where user.name = :name");
query.setParameter(“name",user.getName());
User findUser = (User) query.list().get(0);
基礎配置
Hibernate的配置文件可以有兩種格式:hibernate.properties與hibernate.cfg.xml。配置項大部分都預設了默認值,使用時只需根據實際情況對所需屬性進行配置即可。
dialect 數據庫適配器,用於對特定數據庫提供支持。
connection.driver_class 數據庫JDBC驅動類
connection.url 數據庫URL
connection. username 數據庫訪問用戶名
connection. password 數據庫訪問密碼
connection.datasource JIDI數據源。與2+3配置薦二選一
transactoin.factory_class 指定Transaction實例工廠類
jdbc.fetch_size JDBC獲取的記錄條數
jdbc.batch_size 每次批量提交閾值
show_sql 是否把執行的SQL語句輸出到控制檯
Hibernate基本數據類型
映射基礎
關聯關係映射
繼承映射
Hibernate提供了豐富的數據類型支持,其中包括了傳統的Java數據類型,如String、Integer,以及JDBC數據類型,如Clob,Blob等。
映射基礎
實體映射技術作爲類與表之間的聯繫紐帶,在ORM實現中起着至關重要的作用。對於Hibernate用戶而言,映射關係更多地體現在配置文件的維護過程中。Hibernate選用XML作爲映射配置文件的基礎形式。
實體映射的核心內容,即實體類與數據庫表之間的映射定義。Hibernate中,類表映射主要包括:
類/表映射
主鍵id映射
屬性/字段映射
複合主鍵映射
類/表映射
<class
name="ClassName"
table="tableName"
dynamic-update="true|false"
dynamic-insert="true|false"
/>
(1) name (可選): 持久化類(或者接口)的Java全限定名。 如果這個屬性不存在,Hibernate將假定這是一個非POJO的實體映射。
(2) table (可選 - 默認是類的非全限定名): 對應的數據庫表名。
(3) dynamic-update (可選, 默認爲 false): 指定用於UPDATE 的SQL將會在運行時動態生成,並且只更新那些改變過的字段。
(4) dynamic-insert (可選, 默認爲 false): 指定用於INSERT的 SQL 將會在運行時動態生成,並且只包含那些非空值字段。
主鍵-id映射
<id
name="propertyName"
type="typename"
column="column_name"
unsaved-value="any|none|null|id_value“>
<generator class="generatorClass"/>
</id>
<id
name="propertyName"
type="typename"
column="column_name"
unsaved-value="any|none|null|id_value“>
<generator class="generatorClass"/>
</id>
(1)、name (可選) :標識屬性的名稱。
(2)、type(可選):標識Hibernate類型的名字。
(3)、column(可選-默認爲屬性名):對應數據庫表的主鍵字段的名字。
(4)、unsaved-value(可選-默認爲null):這個值用來判斷對象是否要保存。
(5)、主鍵生成方式。
主鍵生成方式:
assigned
主鍵由應用邏輯產生,數據交由Hibernate保存時,主鍵值已經設置完成,無需Hibernate干預。
increment
主鍵按數值順序遞增。
identity
採用數據庫提供的主鍵生成機制,如SQL Server,MySQL中的自增長主鍵生成機制
sequence
採用數據庫提供的sequence機制生成主鍵,如Oracle Sequence
native
由Hibernate根據數據庫適配器中的定義,自動採用identity、hilo、sequence的其中一種作爲主鍵生成方式
屬性/字段映射
<property
name="propertyName"
column="column_name"
type="typename"
update="true|false"
insert="true|false"
formula=“arbitrary SQL expression”
/>
(1) Name:指定了映射類中的屬性名爲” propertyName”,此屬性將被映射到指定的庫表字段。
(2) column(可選):指定了庫表中對應映射類屬性的字段名。
(3) type(可選):指定了映射字段的數據類型
(4) update, insert (可選 - 默認爲 true) :表明在用於UPDATE 和/或 INSERT的SQL語句中是否包含這個字段。
(5) formula (可選): 一個SQL表達式,定義了這個計算(computed) 屬性的值。計算屬性沒有和它對應的數據庫字段。
複合主鍵映射
Hibernate中,通過composite-id節點對複合主鍵進行定義。
定義方式有兩種:
基於實體類屬性的複合主鍵
基於主鍵類的複合主鍵
基於實體類屬性的複合主鍵
<hibernate-mapping>
<class name=“com.sino.model.User” table=“t_user”>
<composite-id>
<key-property name=“lastname” column=“lastname” type=“string” />
<key-property name=“firstname” column=“firstname” type=“string” />
</composite-id>
<property name=“age” column=“age” type=“Integer” />
</class>
</hibernate-mapping>
User實體類:
Public class User implements Serializable{
private String firstname;
private String lastName;
private Integer age;
… //getter and setter
public boolean equals(Object object){…}
public int hashCode(){…}
}
查找方法:
User user=new User();
user.setFirstname(“小小”);
user.setLastname(“張”);
user=(User)session.load(User.class,user);
System.out.println(“User age is=>”+user.getAge());
基於主鍵類的複合主鍵
<hibernate-mapping>
<class name=“com.sino.model.User” table=“t_user”>
<composite-id name=“userpk” class=UserPK>
<key-property name=“lastname” column=“lastname” type=“string” />
<key-property name=“firstname” column=“firstname” type=“string” />
</composite-id>
<property name=“age” column=“age” type=“Integer” />
</class>
</hibernate-mapping>
UserPK主鍵類:
Public class UserPK implements Serializable{
private String firstname;
private String lastName;
… //getter and setter
… //equals and hashCode
}
User實體類:
Public class User implements Serializable{
private Integer age;
private UserPK userpk;
… //getter and setter
}
查找方法:
UserPK userpk=new UserPK();
userpk.setFirstname(“小小”);
userpk.setLastname(“張”);
User user=(User)session.load(User.class,userpk);
System.out.println(“User age is=>”+user.getAge());
關聯關係映射
一對一關聯
一對多關聯
多對多關聯
一對一關聯包括兩種形式:
主鍵關聯
惟一外鍵關聯
主鍵關聯
一對一主鍵關聯形式即兩張關聯表通過主鍵形式一對一映射關係。Hibernate中,通過one-to-one節點對一對一關係進行定義。典型實例:中國公民只允許擁有一份護照.
User.hbm.xml
<hibernate-mapping>
<class name=“com.sino.model.User” table=“t_user”>
<id name=“id” column=“id” type=“java.lang.Integer” />
<property name=“name” column=“name” type=“java.lang.String” />
<property name=“age” column=“age” type=“java.lang.Integer” />
<one-to-one name=“passport”
class=“com.sino.model.Passport”
cascade=“all”
outer-join=“true” />
</class>
</hibernate-mapping>
Passport.hbm.xml
<hibernate-mapping>
<class name=“com.sino.model.Passport” table=“t_passport”>
<id name=“id” column=“id”>
<generator class=“foreign”>
<param name=“property”>user</param>
</generator>
</id>
<one-to-one name=“user”
class=“com.sino.model.User”
constrained=“true” />
<property name=“serial” column=“serial” type=“java.lang.String” />
<property name=“expiry” column=“expiry” type=“java.lang.Integer” />
</class>
</hibernate-mapping>
對關聯對象的保存:
User user=new User();
user.setAge(new Integer(20));
user.setName(“張小小”);
Passport passport=new Passport ();
passport.setSerial(“PDR1234567”);
passport.setExpiry(new Integer(20080101);
//相互設置關聯
passport.setUser(user);
user.setPassport(passport);
Transaction tx=session.beginTransaction();
session.save(user)
tx.commit();
唯一外鍵關聯
唯一外鍵關聯的一對一關係只是多對一關係的一個特例。
假定在一個特定的用戶管理系統中,每個用戶(User)都從屬於一個用戶組
Hibernate節點定義如下
<hibernate-mapping>
<class name=“com.sino.model.User” table=“t_user”>
<id name=“id” column=“id” type=“java.lang.Integer”>
<generator class=“native” />
</id>
<property name=“name” column=“name” type=“java.lang.String” />
<property name=“age” column=“age” type=“java.lang.Integer” />
<many-to-one name=“group”
class=“com.sino.model.Group”
column=“group_id”
unique=“true” />
</class>
</hibernate-mapping>
運行測試代碼
User user=(User)session.load(User.class,new Integer(1));
System.out.println(“Group name=>”+user.getGroup().getName());
屏幕輸出:
Hibernate: select t_user.id as id1_,… left outer join t_group on t_user.group_id=t_group.group_id where t_user.id=?
Group name=>My Group
一對多關聯
一對多關聯包括兩種形式:
單向一對多關聯
雙向一對多關聯
單向一對多關聯只需在”-”方進行配置,雙向一對多需要關聯雙方均加以配置。
單向一對多關聯
User.hbm.xml
<hibernate-mapping>
<class name=“com.sino.model.User” table=“t_user”
dynamic-update=“true” dynamic-insert=“true”>
<id name=“id” column=“id” type=“java.lang.Integer” />
<property name=“name” column=“name” type=“java.lang.String” />
<property name=“age” column=“age” type=“java.lang.Integer” />
<set name=“addresses”
table=“t_address”
cascade=“all”
order_by=“zipcode asc”>
<key column=“user_id”></key>
<one-to-many class=“com.sino.model.Address” />
<set>
</class>
</hibernate-mapping>
爲已有用戶增加地址對象
Transaction tx=session.beginTransaction();
Address addr=new Address();
addr.setTel(“86028888”);
addr.sestZipcode(“517099”);
addr.setAddress(“ShenZhen”);
user.getAddresses().add(addr);
session.save(user);
tx.commit();
雙向一對多關聯
雙向一對多關聯,實際上是”一對多”和”多對一”關聯的組合。必須在主控方配置單向”一對多”關係的基礎上,在被控方配置與其對應的”多對一”關係
User.hbm.xml
<hibernate-mapping>
<class name=“com.sino.model.User” table=“t_user”
dynamic-update=“true” dynamic-insert=“true”>
<id name=“id” column=“id” type=“java.lang.Integer” />
<property name=“name” column=“name” type=“java.lang.String” />
<property name=“age” column=“age” type=“java.lang.Integer” />
<set name=“addresses” table=“t_address”
lazy=“true”
inverse=“true”
cascade=“all”
sort=“unsorted”
order_by=“zipcode asc”>
<key column=“user_id”></key>
<one-to-many class=“com.sino.model.Address” />
<set>
</class>
</hibernate-mapping>
Address.hbm.xml
<hibernate-mapping>
<class name=“com.sino.model.Address” table=“t_address”
dynamic-update=“false” dynamic-insert=“false”>
<id name=“id” column=“id” type=“java.lang.Integer” />
<property name=“tel” column=“tel” type=“java.lang.String” />
…
<many-to-one name=“user” class=“com.sino.model.User”
cascade=“all”
outer-join=“auto”
update=“true”
insert=“true”
access=“property”
column=“user_id”
not-null=“true”
/>
</class>
</hibernate-mapping>
維護關係
…
Transaction tx=session.beginTransaction();
Address addr=new Address();
addr.setTel(“86028888”);
addr.sestZipcode(“517099”);
addr.setAddress(“ShenZhen”);
addr.setUser(user);
user.getAddresses().add(addr);
session.save(user);
tx.commit();
多對多關聯
由於引入了中間表,一次讀取操作需要反覆數次查詢,多對多關聯的性能不佳,設計時應該儘量避免大量使用。
在一個權限管理系統中,一個常見的多對多的映射關係是Role與Privilege之間的映射:
Role代表”角色”(如:會計、出納)
Privilege代表某個特定資源的訪問特權(如修改財務報表,查詢財務報表)一個Role可以有多種Privilege,一個Privilege可同時屬於多個Role
Role.hbm.xml
<hibernate-mapping>
<class name=“com.sino.model.Role” table=“t_role”
dynamic-update=“false” dynamic-insert=“false”>
<id name=“id” column=“id” type=“java.lang.Integer” />
<property name=“name” column=“name” type=“java.lang.String” />
…
<set name=“privileges” table=“t_role_ privilege”
lazy=“true”
inverse=“false”
cascade=“save-update”>
<key column=“role_id”></key>
<many-to-many class=“com.sino.model.Privilege” column=“privilege_id”/>
<set>
</class>
</hibernate-mapping>
Privilege.hbm.xml
<hibernate-mapping>
<class name=“com.sino.model.Privilege” table=“t_privilege”
dynamic-update=“false” dynamic-insert=“false”>
<id name=“id” column=“id” type=“java.lang.Integer” />
<property name=“name” column=“name” type=“java.lang.String” />
…
<set name=“roles” table=“t_role_ privilege”
lazy=“true”
inverse=“true”
cascade=“save-update”>
<key column=“privilege_id”></key>
<many-to-many class=“com.sino.model.Role” column=“role_id” outer-join=“auto”/>
<set>
</class>
</hibernate-mapping>
保存關係代碼:
Role role1=new Role();
role1.setName(“Role1”);
Role role2=new Role();
role2.setName(“Role2”);
Privilege privilege1=new Privilege();
privilege1.setName(“Privilege1”);
Privilege privilege2=new Privilege();
privilege2.setName(“Privilege2”);
Privilege1.getRoles().add(role1);
Privilege1.getRoles().add(role2);
Privilege2.getRoles().add(role1);
role1.getPrivileges.add(privilege1);
role1.getPrivileges.add(privilege2);
role2.getPrivileges.add(privilege1);
Try{
Transaction tx = session.beginTransaction();
//多對多關係必須同時對關聯雙方進行保存
session.save(role1);
session.save(role2);
session.save(privilege1);
session.save(privilege2);
tx.commit();
}catch(Exception e)
…
}
繼承映射
繼承關係是關係型數據與面向對象數據結構之間的主要差異之一。
Hibernate支持三種類型的繼承關係
每個具體類一張表(Table per concrete class)
表與子類之間的獨立一對一關係
每個子類一張表(Table per subclass)
每個子類對應一張子表,並與主類共享主表
每個類分層結構一張表(Table per class hierarchy)
表與類的一對多關係
每個具體類一張表
Book.hbm.xml
<class name=“example.model.Book” table=“t_book”>
<id name=“id” column=“id” type=“java.lang.Integer”>
<generator class=“native” />
</id>
<property name=“manufacturer” column=“manufacturer” … />
…
<property name=“pageCount” column=“page_count” … />
</class>
DVD.hbm.xml
<class name=“example.model.DVD” table=“t_dvd”>
<id name=“id” column=“id” type=“java.lang.Integer”>
<generator class=“native” />
</id>
<property name=“manufacturer” column=“manufacturer” … />
…
<property name=“regionCode” column=“region_code” … />
</class>
…
查詢Product
Query query=session.createQuery(“from example.model.Product”);
List products=query.list();
每個子類一張表
Product.hbm.xml
<class name=“example.model.Product” table=“t_product”>
<id name=“id” column=“id” type=“java.lang.Integer”>
<generator class=“native” />
</id>
<property name=“manufacturer” column=“manufacturer” … />
<property name=“name” column=“name” … />
<joined-subclass name=“example.model.Book” table=“t_book”>
<key column=“id” />
<property name=“pageCount” column=“page_count” />
</joined-subclass>
<joined-subclass name=“example.model.DVD” table=“t_dvd”>
<key column=“id” />
<property name=“regionCode” column=“region_code” />
</joined-subclass>
</class>
查詢Product
Query query =session.createQuery(“from example.model.Product”);
List products=query.list();
每個類分層結構一張表
t_product表不但包括Product的id,name,manufacturter字段,同時還包含了Book的page_count字段、DVD的region_code字段,不同的類型商品通過(category)字段加以區分。
Product.hbm.xml
<class name=“example.model.Product” table=“t_product”>
<id name=“id” column=“id” type=“java.lang.Integer”>
<generator class=“native” />
</id>
<discriminator column=“category” type=“string” />
<property name=“manufacturer” column=“manufacturer” … />
<property name=“name” column=“name” … />
<subclass name=“example.model.Book” discriminator-value=“book”>
<property name=“pageCount” column=“page_count” />
</subclass>
<subclass name=“example.model.DVD” discriminator-value =“dvd”>
<property name=“regionCode” column=“region_code” />
</subclass>
</class>
查詢Book
Query query =session.createQuery(“from example.model.Book”);
List books=query.list();
Hibernate會根據配置自動進行類型識別,把t_product表中category=book的數據(Book) 全部查詢出來。
每個具體類一張表
每個子類一張表
每個類分層結構一張表