http://hi.baidu.com/hking1987/blog/category/Java/index/1
http://blog.csdn.net/songmin3121/category/546763.aspx
一對多的解釋:
關聯關係的本質是將關聯關係映射到數據庫中。關聯關係在對象模型中體現爲內存中的一個或多個引用。 一對多關係: 一對多關係 分爲 “ 單向一對多/多對一關係 ” 和 “ 雙向多對一 ” 關係。
“ 單向一對多/多對一關係 ” 只需在 “ 一 ” / “ 多 ” 方進行配置,
“ 雙向多對一關係 ” 需要在關聯雙方均加以配置。 雙向多對一關聯就是在多方和一方都進行配置,並在 “ 一 ” 方通過屬性inverse="true" 設置控制關係反轉
注:單向多對一關聯是最常見的單向關聯關係。
注:雙向多對一關聯是最常見的雙向關聯關係。雙向多對一關聯實際上是 “ 多對
一 ” 與 “ 一對多 ” 關聯的組合。
多對一及一對多關聯映射的區別(單向):
不管多對一還是一對多,都是在" 多" 的一端添加一個外鍵指向" 一" 的一端,只不過是多對一是在多的一端爲其自己添外鍵,而一對多則是在一的一端爲多的一端添加外主鍵。
模型:一個用戶組(group )對應多個用戶(user )。
多對一關聯映射:是在" 多" 的一端添加一個外鍵指向" 一" 的一端,它維護的關係爲多到一的關係,如:當載入一個用戶(user )時將會同時載入組(group )的信息。它的關係映射將寫在多的一端(user ):
< many-to-one name="group" column = "relatedGroup" cascade = "all" /> 此時它在多的一端( user )添加了一個外鍵 “ relateGroup ” 指向一的一端。在多的一端通過 group 維護一的一端。
一對多關聯映射:是在" 多" 的一端添加一個外鍵指向" 一" 的一端,它維護的關係爲一到多的關係,如:當載入一個組(group )時將會同時載入此組用戶(user )的信息。它的關係映射將寫在一的一端(group ):
< set name="users" order-by = "name" >
< key column = "relatedGroup" />
< one-to-many class = "com.dvnchina.hibernate.User" />
</ set >
此時通過 < key column = "relatedGroup" /> 在多的一端( user )添加了一個外鍵 “ relateGroup ” 指向一的一端。在一的一端通過 users 維護多的一端。
總之,一對多和多對一的映射策略是一致的,都是通過在" 多" 的一端添加一個外鍵指向" 一" 的一端,只是站的角度不同。
多對多就是2個 一對多的疊加設置
*********************************************************************
一,
HQL 語言基本用法
1.實體查詢
String hql = " from TUser";
執行這條語句會返回TUser以及TUser子類的紀錄。
hql = "from java.lang.Object"
會返回數據庫中所有庫表的紀錄。
where 語句
hql = "from TUser as user where user.name='yyy'";
其中,as可以省略也一樣
hql = "from TUser user where user.name='yyy'";
where子句中,我們可以通過比較運算符設定條件,如:
=, <>, >, <, >=, <=, between, not between, in, not in, is, like等。
2.屬性查詢
List list = session.createQuery("select user.name, user.age from TUser as user").list();
還可以在HQL中動態構造對象實例的方法,將數據封裝。
List list = session.createQuery("select new TUser(user.name, user.age) from TUser as user").list();
Iterator it = list.iterator();
while(it.hasNext() ) {
TUser user = (TUser)it.next();
System.out.println(user.getName());
}
但是要注意這裏的TUser對象只是對name和age屬性的封裝,其他狀態均未賦值,所以不能用它來進行更新操作。
也可以在HQL的Select子句中使用統計函數
"select count(*) ,min(user.age) from TUser as user"
也可以使用distinct關鍵字來刪除重複紀錄。
select distinct user.name from TUser as user;
3.實體的更新與刪除
hibernate 2中需要先查詢出實體,設置屬性後再保存。
hibernate 3中,提供了更靈活的方式(bulk delete/update)
更新:
Query query = session.createQuery("update TUser set age=18 where id=1");
query.executeUpdate();
刪除:
session.createQuery("delete TUser where age>=18");
query.executeUpdate();
4.分組與排序
Order by子句:
from TUser user order by user.name, user.age desc
Group by子句和Having子句
"select count(user), user.age from TUser user group by user.age having count(user)>10"
5.參數邦定
通過順序佔位符?來填充參數:
1)hibernate 2 中通過session.find方法來填充
session.find("from TUser user where user.name=?", "Erica", Hibernate.STRING);
多個參數的情況:
Object[] args = new Object[] {"Erica", new Integer(20)};
Type[] types = new Type{Hibernate.STRING, Hibernate.INTEGER};
session.find("from TUser user where user.name=? and user.age=?", args, types);
2)通過Query接口進行參數填充:
Query query = session.createQuery("from TUser user where user.name=? and user.age>?");
query.setString(0,"Erica");
query.setInteger(1, 20);
通過引用佔位符來填充參數:
String hql = "from TUser where name=:name";
Query query = session.createQuery(hql);
query.setParameter("name","Erica");
query.setString("name","Erica");//?區別
甚至可以將查詢條件封裝爲一個JavaBean
class UserQuery {
private String name;
private Integer age;
//getter and setter
}
String hql = "from TUser where name=:name and age=:age";
Query query = session.createQuery(hql);
UserQuery uq = new UserQuery();
uq.setName("Erica");
uq.setAge(new Integer(20));
query.setProperties(uq); //會調用裏面的getter?
query.iterate();
6.聯合查詢
也可以使用 inner join,left outer join, right out join, full join
排列組合:form TUser, TAddress
************************************************************
二,
1. 一.對象關係映射基礎
2. 1.hibernate對象屬性映射
3. 映射文件中,<property>元素的access屬性用於指定Hibernate訪問持久化類的屬性的方式。有以下兩種可選值:
4. property:這是默認值,表示是通過屬性相應的get,set方法來訪問屬性。
5. field:表面是運用Java反射機制直接訪問類的屬性,此屬性可以沒有get,set方法。
6. 例如:<property name="name" access="field"/>
7. 這樣設置是直接通過屬性值取得,可以沒有get,set方法。
8. 反之name則是根據set,get後的Name對應,配置文件的name名可以不跟實體bean裏的屬性名直接對應。
9. 我們常用的hql語句例如:List customers = session.find("from Customer as c where c.name = 'Tom'");
10. 這裏的c.name中的name名字是根據配置文件裏的name所對應,並不是和實體bean裏的屬性名對應。
11.
12. 2.在持久化類的訪問方法中加入程序邏輯
13. (1)hibernate在執行find方法時候會調用bean中的set方法把查詢出的內容放入屬性中,提供給我們獲取其中的值。
14. 在執行save,update等方法時候會調用bean中的get方法獲得我們提交的數據進行持久化操作。
15. 所以,可以通過在set,get方法中加入相應我們需要的邏輯,如果不需要讓hibernate在執行查詢或保存操作時候隱式的調用get,set方法,
16. 可以在映射文件中設置field參數
17. (2)利用<property>元素的formula屬性。
18. 如果數據表裏沒有totalPrice價格總和字段,而我們需要在獲得用戶對象的同時,需要知道它對應多個訂單的價格總和,
19. 我們可以在CUSTOMER實體bean裏定義totalPrice屬性,生成get,set方法,再利用以下代碼可以實現
20. 例如:<property name="totalPrice" formula="(select sum(o.PRICE) rrom ORDERS o where o.CUSTOMER_ID=ID)"/>
21. (3)控制insert和update語句
22. 例如:<property name="price" update="false" insert="false" column="PRICE"/>
23. 設置成false代表在保存或修改此類時候,price不被操作。一般多用於多對一關係映射中,避免級聯操作。
1. 二.關聯映射
2. 1.主鍵自動生成映射
3. <id name="id" type="java.lang.String" column="ID" length="32">
4. <generator class="uuid" />
5. </id>
6. 2.一對一主鍵關聯
7. <one-to-one name="order" class="mypack.Order" cascade="all"/>
8. constrained屬性爲true,表明ORDERS表的ID主鍵同時作爲外鍵參照CUSTOMERS表。
9. 當前xml文件中,必須爲OID使用foreign標識符生成策略:
10. <one-to-one name="customer" class="mypack.Customer" constrained="true"/>
11. <id name="id" type="java.lang.String" column="ID">
12. <generator class="foreign">
13. <param name="property">customer</param>
14. </generator>
15. </id>
16. 注:當兩個關聯類之間有兩個一對一關聯時,可以使用一對一外鍵關聯。
17. 一對一外鍵關聯與多對一關聯的區別僅僅是在many-to-one標籤上需要配置屬性unique="true"。
18. 3.多對一關聯
19. <many-to-one name="customer" class="mypack.Customer" column="CUSTOMER_ID"/>
20. 4.一對多關聯
21. <set name="orders" cascade="save-update" inverse="true" lazy="extra">
22. <key column="CUSTOMER_ID" />
23. <one-to-many class="mypack.Order"/>
24. </set>
25. <list name="orders" cascade="save-update" lazy="extra" fetch="join">
26. <key column="CUSTOMER_ID"></key>
27. <list-index column="SHOW_INDEX"></list-index> <!--orders表中索引位置字段SHOW_INDEX-->
28. <one-to-many class="mypack.Order" />
29. </list>
30. 5.多對多關聯
31. <set name="orders" table="CUSTOMERS_TO_ORDERS" cascade="save-update" lazy="extra">
32. <key column="CUSTOMER_ID"/> <!--雙向多對多關聯必須把其中一端的inverse屬性設爲true-->
33. <many-to-many class="mypack.Order" column="ORDER_ID"/>
34. </set>
35. 注:當中間表需要有更多的屬性字段時,可把多對多分解成兩個一對多關聯。
36. 6.在數據庫中對集合排序
37. set,map映射支持sort屬性(內存排序),order-by屬性(數據庫排序)。
38. 例如:
39. <set name="orders" cascade="save-update" inverse="true" lazy="extra" order-by="ID asc">
40. <key column="CUSTOMER_ID" /> <!--當加載Customer對象的orders集合的時,會進行排序操作-->
41. <one-to-many class="mypack.Order"/>
42. </set>
43. 7.附加過濾條件
44. <set name="orders" cascade="save-update" inverse="true" lazy="extra" where="STATUS='1'">
45. <key column="CUSTOMER_ID" /> <!--當加載Customer對象的orders集合的時,只會查出STATUS爲1的order對象-->
46. <one-to-many class="mypack.Order"/>
47. </set>
1. 三.檢索方式
2. 1.分頁查詢
3. List result = this.getSession().createQuery("from Customer c order by c.name asc")
4. .setFirstResult(0).setMaxResults(10).list();
5. 2.條件查詢
6. //如果hql語句中含有參數可以使用以下方法提高安全性:
7. Object[] args = {name,order}; //傳入的查詢條件的變量名字
8. Type[] types = {Hibernate.STRING,Hibernate.entity(Order.class)}; //變量類型
9. //Type[]是hibernate提供的變量類型,order爲自定義對象類型。
10. //根據參數說在位置的索引值,傳入的變量名字,變量類型來進行查詢。
11. List list = this.query("from Customer c where c.name=? and c.order=?",args,types)
12. public List query(String hql, Object[] args, Type[] types){
13. Query query = this.getSession().createQuery(hql);
14. query.setParameters(args, types);
15. return query.list();
16. }
17. 3.批量延遲檢索
18. 映射文件中<set>元素有一個batch-size屬性,用於爲延遲檢索或立即檢索策略設定批量檢索的數量。
19. 注:批量檢索的屬性值範圍不宜過大,如果過大就失去了延遲加載的意義,如果太小也會失去批量檢索的意義。
20. 一般設置爲:3-10。合理的運用批量檢索可以提高檢索性能。具體請查閱說明文檔。
21. 4.檢索單個對象
22. Customer customer = (Customer)this.getSession().createQuery("from Customer c order by c.name asc")
23. .setMaxResults(1).uniqueResult();
24. 5.隱式內連接
25. //標準的HQL內連接查詢語句:
26. "from Customer c inner join c.orders";
27. //如果Customer類中沒有orders集合屬性,可以採用SQL風格的隱式內連接查詢語句:
28. "from Customer c,Order o where c.in=o.customer_id";
29. 6.分組查詢
30. //例如以下查詢語句僅統計具有一條以上訂單的客戶的所有訂單的總價:
31. "select c.id,c.name,sum(o.price) from Customer c join c.orders o group by c.id having (count(o)>1)";
32. 7.HQL查詢的select子句
33. "select new map(c.name as personName) from Customer c"
34. //HQL語句返回的結果是集合,其中集合元素是Map對象,以personName作爲Map的key。
************************************************************
三,
一級緩存
實驗:
1.在同一個session中2次load
load會從緩存中讀取數據
2.在同一個session中2次get
get會從緩存中中讀取數據
3.在同一個session中2次迭代 .iterate().next();
hql勢必會執行 但是迭代會從緩存中讀取數據 第一次迭代出現n+1問題(會發出查詢id的sql 不會發現查詢實體的sql)
4.在同一個session中2次查詢屬性
緩存只緩存實體對象
5.在不同的session中發出load查詢
一級緩存和session綁定
6.在同一session中先save 後load
save也是會將實體放入session中
7.在同一session中先load, 再session.clear()或session.evict(),最後再load()
總結:
支持一級緩存的方法:
get load iterate
管理一級緩存
clear evict
二級緩存
也成爲SessionFactory級緩存,二級緩存可以被所有Session共享
SessionFactory可以管理二級緩存 二級緩存與SessionFactory的生命週期一致
將echcache.xml 拷貝至src下
<property name="hibernate.cache.user_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<class-cache class="com.illu.pojo.Xxx" usage="read-only"/>
readonly策略 + idle失效 保證數據庫與內存中的一致性
************************************************************
四,
二級緩存配置:
http://blog.163.com/chenhongbin007/blog/static/3406992120094661950804/
1、首先要打開二級緩存,在hibernate.cfg.xml中添加如下配置:
<property name="hibernate.cache.use_second_level_cache">true</property>
2、Hibernate的二級緩存使用第三方的緩存工具來實現,所以我們需要指定Hibernate使用哪個
緩存工具。如下配置指定Hibernate使用EhCache緩存工具。
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
3、Hibernate在默認情況下並不會對所有實體對象進行緩存,所以,我們需要指定緩存哪些對象,
在實體對象的映射文件中(相應的<class>標籤內部),添加如下配置:
<cache usage="read-only"/>
usage="read-only"是“只讀”緩存策略。
注意,這個<cache>標籤只能放在<class>標籤的內部,而且必須處在<id>標籤的前面!!!
這個<cache>標籤放在哪些<class>標籤下面,就說明會對這些類的對象進行緩存
4、對於第3步,有一個可選的方案是在hibernate.cfg.xml文件中指定哪些類的對象需要緩存,
而不需要使用<cache>標籤來指定。如:
在hibernate.cfg.xml中添加如下配置:
<class-cache class="com.bjsxt.hibernate.Classes" usage="read-only" />
注意,這個<class-cache>標籤必須放在<mapping>標籤的後面!!
1、首先設置EhCache,建立配置文件ehcache.xml,默認的位置在class-path,可以放到你的src目錄下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000" <!-- 緩存最大數目 -->
eternal="false" <!-- 緩存是否持久 -->
overflowToDisk="true" <!-- 是否保存到磁盤,當系統當機時-->
timeToIdleSeconds="300" <!-- 當緩存閒置n秒後銷燬 -->
timeToLiveSeconds="180" <!-- 當緩存存活n秒後銷燬-->
diskPersistent="false"
diskExpiryThreadIntervalSeconds= "120"/>
</ehcache>
2、在Hibernate配置文件中設置:
<!-- 設置Hibernate的緩存接口類,這個類在Hibernate包中 -->
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<!-- 是否使用查詢緩存 -->
<property name="hibernate.cache.use_query_cache">true</property>
如果使用spring調用Hibernate的sessionFactory的話,這樣設置:
<!--HibernateSession工廠管理 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="datasource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
</props>
</property>
<property name="mappingDirectoryLocations">
<list>
<value>/WEB-INF/classes/cn/rmic/manager/hibernate/</value>
</list>
</property>
</bean>
說明一下:如果不設置“查詢緩存”,那麼hibernate只會緩存使用load()方法獲得的單個持久化對象,如果想緩存使用 findall()、list()、Iterator()、createCriteria()、createQuery()等方法獲得的數據結果集的話,就需要設置
hibernate.cache.use_query_cache true 纔行
3、在Hbm文件中添加<cache usage="read-only"/>
4、如果需要“查詢緩存”,還需要在使用Query或Criteria()時設置其setCacheable(true);屬性
5、實踐出真知,給一段測試程序,如果成功的話第二次查詢時不會讀取數據庫
package cn.rmic.hibernatesample;
import java.util.List;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import cn.rmic.hibernatesample.hibernate.HibernateSessionFactory;
import cn.rmic.manager.po.Resources;
public class testCacheSelectList ...{
/** *//**
* @param args
*/
public static void main(String[] args) ...{
// TODO Auto-generated method stub
Session s=HibernateSessionFactory.getSession();
Criteria c=s.createCriteria(Resources.class);
c.setCacheable(true);
List l=c.list();
// Query q=s.createQuery("From Resources r")
// .setCacheable(true)
// .setCacheRegion("frontpages") ;
// List l=q.list();
Resources resources=(Resources)l.get(0);
System.out.println("-1-"+resources.getName());
HibernateSessionFactory.closeSession();
try ...{
Thread.sleep(5000);
} catch (InterruptedException e) ...{
// TODO Auto-generated catch block
e.printStackTrace();
}
s=HibernateSessionFactory.getSession();
c=s.createCriteria(Resources.class);
c.setCacheable(true);
l=c.list();
// q=s.createQuery("From Resources r").setCacheable(true)
// .setCacheRegion("frontpages");
// l=q.list();
resources=(Resources)l.get(0);
System.out.println("-2-"+resources.getName());
HibernateSessionFactory.closeSession();
}
}
************************************************************
五
1:1共享主鍵關係
對象模型代碼
public class Person implements java.io.Serializable {
private Long id;
private String name;
private Address address;
public class Address implements java.io.Serializable {
private Long id;
private Person person;
private String detail;
四、映射代碼
<? xml version ="1.0" encoding ="utf-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping >
< class name ="entity.Person" table ="person" >
< id name ="id" type ="java.lang.Long" >
< column name ="id" />
< generator class ="identity" />
</ id >
< property name ="name" type ="java.lang.String" >
< column name ="name" length ="24" not-null ="true" >
< comment > 姓名</ comment >
</ column >
</ property >
<!-- cascade="all":在保存person對象的時候,級聯保存person對象關聯的address對象 -->
< one-to-one name ="address" cascade ="all" />
</ class >
</ hibernate-mapping >
<? xml version ="1.0" encoding ="utf-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping >
< class name ="entity.Address" table ="address" catalog ="mydb" >
< id name ="id" type ="java.lang.Long" >
< column name ="id" />
<!-- class="foreign": 一對一主鍵映射中,使用另外一個相關聯的對象的標識符 -->
< generator class ="foreign" >
< param name ="property" > person</ param >
</ generator >
</ id >
< property name ="detail" type ="java.lang.String" >
< column name ="detail" length ="120" not-null ="true" >
< comment > 詳細地址</ comment >
</ column >
</ property >
<!-- 表示在address表存在一個外鍵約束,外鍵參考相關聯的表person -->
< one-to-one name ="person" constrained ="true" />
</ class >
</ hibernate-mapping >
五、Hibernate配置
<? xml version ='1.0' encoding ='UTF-8' ?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
< hibernate-configuration >
< session-factory >
< property name ="connection.username" > root</ property >
< property name ="connection.url" >
jdbc:mysql://localhost:3306/mydb
</ property >
< property name ="dialect" >
org.hibernate.dialect.MySQLDialect
</ property >
< property name ="connection.password" > xiaohui</ property >
< property name ="connection.driver_class" >
com.mysql.jdbc.Driver
</ property >
< property name ="show_sql" > true</ property >
< property name ="format_sql" > true</ property >
< mapping resource ="entity/Person.hbm.xml" />
< mapping resource ="entity/Address.hbm.xml" />
</ session-factory >
</ hibernate-configuration >
*********************************************************************************************************
session的方法
get方法:
返回的對象是persistent對象
調用時發出查詢sql
查不到返回null
實例:
User user = (User)session.get(User.class, 1);
第二個參數爲主鍵,如果是主鍵生成策略是uuid,則應傳一個字符串,上面是native生成的,所以用傳的是int
load方法
返回的對象是persistent對象
調用延遲加載,原理是用CGLIB生成代理類,採用繼承,生成子類
真正使用對象的時候才發出查詢sql語句
如果查找不到會產生異常
實例:
User user = (User)session.load(User.class, 1);
delete方法
使用Session.delete()會把對象的狀態從數據庫中移除。 當然,你的應用程序可能仍然持有一個指向已刪除對象的引用。所以,最好這樣理解:delete()的用途是把一個持久實例變成瞬時(transient)實例。
實例:
session.delete(user);
Query簡單使用
//創建query,參數爲HQL語句
Query query = session.createQuery("from User");
//分頁
query.setFirstResult(0);
query.setMaxResults(2);
//獲取選出的記錄
List list = query.list();
以下是Hibernate文檔裏對對象三種狀態的描述
瞬時(Transient) - 由new操作符創建,且尚未與Hibernate Session 關聯的對象被認定爲瞬時(Transient)的。瞬時(Transient)對象不會被持久化到數據庫中,也不會被賦予持久化標識 (identifier)。 如果瞬時(Transient)對象在程序中沒有被引用,它會被垃圾回收器(garbage collector)銷燬。使用Hibernate Session可以將其變爲持久(Persistent)狀態。(Hibernate會自動執行必要的SQL語句)
持久(Persistent) - 持久(Persistent)的實例在數據庫中有對應的記錄,並擁有一個持久化標識(identifier)。 持久(Persistent)的實例可能是剛被保存的,或剛被加載的,無論哪一種,按定義,它存在於相關聯的Session作用範圍內。 Hibernate會檢測到處於持久(Persistent)狀態的對象的任何改動,在當前操作單元(unit of work)執行完畢時將對象數據(state)與數據庫同步(synchronize)。 開發者不需要手動執行UPDATE。將對象從持久(Persistent)狀態變成瞬時(Transient)狀態同樣也不需要手動執行DELETE語句。
脫管(Detached) - 與持久(Persistent)對象關聯的Session被關閉後,對象就變爲脫管(Detached)的。 對脫管(Detached)對象的引用依然有效,對象可繼續被修改。脫管(Detached)對象如果重新關聯到某個新的Session上,會再次轉變爲持久(Persistent)的(在Detached其間的改動將被持久化到數據庫)。這個功能使得一種編程模型,即中間會給用戶思考時間(user think-time)的長時間運行的操作單元(unit of work)的編程模型成爲可能。 我們稱之爲應用程序事務,即從用戶觀點看是一個操作單元(unit of work)。
三種狀態的特徵:
transient狀態的特徵
在數據庫中沒有與之匹配的數據
沒有納入session的管理
persistent狀態的特徵
在數據庫中有與之匹配的數據
納入了session的管理
在清理緩存(髒數據檢查)的時候,自動與數據庫同步
detached狀態的特徵
在數據庫中有與之對應的數據
沒有納入session的管理
可以手動構造detached狀態的對象
把ID設置爲數據庫中存在的即可
***************************************
悲觀鎖
悲觀鎖的實現,通常依賴於數據庫機制,在整個過程中都將數據鎖定,其他任何用戶都不能讀取或者修改,因此併發性不好,適合於短事務。使用悲觀鎖會是Lazy失效。
session.get/load(class,id,LockMode.UPGRADE);
===========================
樂觀鎖
樂觀鎖不是鎖,是衝突檢測機制,一個用戶改的時候,別人可以隨便改。
版本號:
假設初始版本號爲0,有用戶改一次,版本號+1
大多數是基於數據庫版本記錄機制,一般是在數據庫表中加入一個version字段。
讀取數據時將版本號一同讀出,之後更新數據時,版本號加一,如果提交數據時,版本號小於或等於數據庫表中的版本號,則認爲數據是過期的,不能更新,出現更新丟失(Lost Update)。否則給予更新。
舊數據不能更新新數據,如果當前版本低於數據庫中的版本,則不能進行更新。即一個用戶拿到數據之後,在更改之前已經被別的用戶更改過,則此時不能對其更新。
配置
在實體中加一個version(int),由Hibernate進行維護:
class標籤的optimistic-lock="version",這是默認配置,可不顯示聲明
映射文件中配置屬性(位置在普通屬性配置之前):
<version name="version"/>
加載時不需要設置鎖模式
缺點:
數據量比較多,因爲別人的更改而不能更新,大量的數據輸入無效。
*************************************************
Hibernate關係映射的說明
Hibernate開源框架對數據層的映射包括實體的映射和關係的映射,其中關係的映射是最複雜的。如果你掌握不好關係映射,你乾脆就不要用,否則會嚴重地影響性能。
Hibernate中的實體關係分爲四種,基本上與我們在數據庫建模中瞭解到的實體關係是一樣的,即一對一關係、一對多關係、多對一關係、多對多關係。我們下面分別給出說明:
一、一對一關係、多對一關係
一對一、多對一關係在代碼上的體現是在JavaBean中包含一個實體類屬性。比如,夫妻關係是一對一的關係,那麼丈夫的屬性中就應該有一個屬性是妻子,妻子的屬性中也應該有一個屬性是丈夫。同樣,多對一的關係中,在代碼上的體現也是在Java中包含一個實體類屬性。比如,孩子與媽媽的關係就是多對一的關係,每一個孩子都應該有一個屬性是媽媽。我們發現,無論是一對一,還是多對一,它們在代碼是都是一樣的,就是屬性中包含一個實體類屬性。而事實上,一對一關係是多對一關係的一種特例而已。所以,在映射時,由外鍵實現的一對一關係或多對一關係時,無論是哪一種,外鍵所在一方關係屬性都是通過many- to-one映射的,而不是用one-to-one。
二、一對多關係、多對多關係
這兩種關係也有些共性,就是它們在代碼上的體現都是在JavaBean中包含一個集合屬性。比如在上面說的媽媽與孩子的關係,媽媽應該包含一個集體屬性,在這個集合中包含了她所有的小孩。這種一對多關係使用one-to-many來映射,在數據庫中的體現是在一的一方是不含有外鍵的。而多對多的關係雖然也是在屬性中包含一個集合屬性,但在映射時使用的卻是many-to-many。這主要是因爲多對多關係需要一個關係表,必須告訴Hibernate這個關係表是誰。
以上只是簡單的概括了一下Hibernate中一些關係映射的特點,下面來說說Hibernate關係映射中的難點問題。
如果不是真正使用Hibernate開發過項目,恐怕很難理解爲什麼說如果掌握不好關係最好不要使用關係。這裏面有這樣幾個問題:
1、關係的級聯操作問題。
我們知道,在數據庫中,建立外鍵的同時還要建立約束關係。這就存在一個問題,即當外鍵所指向表的行被刪除時,當前行應該如何操作。比如,丈夫表含有一個外鍵指向妻子,如果妻子被刪除了,那麼丈夫所在行的外鍵的值應該如何操作?如果保持原有值不變,但他所指向的妻子卻已經不在了,這樣的記錄就沒有意義了,所以必須要採取一些行爲。在數據庫中一般會根據用戶的設置採取三種行爲,一是給出提示,告訴你因爲有外鍵指向這個行,所以不能刪,如果非要刪除,必須先將指向它的外鍵卻除;二是在刪除當前行後,自動將指向它的外鍵置爲空;三是刪除當前行後,將所有指向它的行也同時刪除。
一般數據庫默認情況下都採取第一種行爲,這樣如果不做任何設置,當我們對一個實體進行刪除時,就會報錯,告訴你有外鍵指向不能刪除。
這種問題如何解決呢?你可以事先做好數據庫設計,讓數據庫幫你採取一種更合適的行爲。兩種可選的確定行爲,即置空或級聯刪除。究竟採用哪一種更合適,要視你的應用而定,限於篇幅,我這裏就不做過多的講解了。再者,可以使用Hibernate的cascade屬性,它的delete代表刪除時置空外鍵,而 delete-orphan則代表刪除時同時刪除指向它的行。
2、關係的方向性問題
“一個巴掌拍不響”,一個關係也一定有兩個實體。這就存在了另外一個問題,當一個實體發生變化時,關係是不是也一定要跟着變化?這種問題很難說清,因爲它跟具體的應用關聯。有時,我們只要更新實體,而不想更新關係;而有時我們又想更新關係而不更新實體;還有些情況下,我們是實體和關係同時都要更新。在默認情況下,Hibernate對於實體和關係是同時更新的,即使你根本沒有更改過關係。這對於性能的影響比較大。我們可以給關係設置一個inverse屬性,告訴它在任何變化下,都不要更新關係。當然還有其它的辦法,讀者可以參考其文檔。總之,這是一個非常複雜的問題,要視你的應用而定。
3、N+1查詢問題
關於什麼是 N+1查詢,我不想解釋,讀者可以看一下我前面的文章或者到網上去查詢。總的來說N+1查詢的問題就是性能太低,在有些情況下甚至會導致系統崩潰。但有些時候它又是有益的。因爲N+1查詢實際上是延遲加載了,它節省了空間。Hibernate有一個fetch屬性,用於說明抓取數據的策略,如果選擇了 join則不會使用N+1查詢,但加載上來了所有的數據並不一定都是你想要的,也可能會浪費存儲空間。
4、延遲加載
延遲加載就是並不是在讀取的時候就把數據加載進來,而是等到使用時再加載。那麼Hibernate是怎麼知識用戶在什麼時候使用數據了呢?又是如何加載數據呢?其實很簡單,它使用了代理機制。返回給用戶的並不是實體本身,而是實體對象的代理。代理對象在用戶調用getter方法時就會去數據庫加載數據。但加載數據就需要數據庫連接。而當我們把會話關閉時,數據庫連接就同時關閉了。這種情況就叫做未初始化的關係。
延遲加載的好處就是節省了存儲空間。因爲我們並不是在所有情況下都需要關係數據。比如,媽媽和孩子。如果你只想修改媽媽的數據,而Hibernate將她10幾個孩子也同時給你加載進來了,這顯然是無意義的。所以你可以使用Hibernate.initialize()方法主動地去決定是否初始化關係。當然也可以在配置文件中通過lazy屬性,但這樣一來就固定了,要麼延遲,要麼不延遲。
Hibernate的關係還有很多問題,這裏限於篇幅先講這麼多。還是開頭的那句話,如果你沒有掌握好Hibernate中關係的映射,那你乾脆就不要用了,否則嚴重地影響性能。
************************************************************,