Hibernate基礎知識

Hibernate

Hibernate的做用:java

一、         Hibernate解決ORM(對象關係映射)的問題,大大減小了持久層的代碼量python

二、         hql方言,解決了可移植性問題mysql

三、         效率問題,頻繁的鏈接和關閉,自動的封裝JDBC,自動使用鏈接池。Sessionweb

四、         具備緩存功能,節省查詢時間算法

五、         經過設定自動給數據加鎖,事務的隔離級別sql

Hibernate工做在持久層。數據庫

相似Hibernate的框架 apache的OJB,sun公司的JDO,Oracle的Toplink, apache 和 Google的Ibatis。apache

POJO:領域模型也叫實體類。包括表單bean和結果bean(不繼承任何類)windows

<struts1.0的Form 不屬於POJO,由於繼承了ActionForm類>數組

1、爲何要用Hibernate?

Java中的對象,數據庫中的關係。Hibernate主要是實現對象模型和關係模型直接的轉換。輕量級的封裝。面向對象的思想。

由於Hibernate有映射文件,因此才能對數據save,query等操做。

映射文件:***.hbm.xml文件,也叫源文件,把對象模型的繼承、關聯映射成數據庫中的主外鍵關係。

Hibernate自動的保持數據庫在的記錄和對象屬性的值同步(保證一致)。

Hibernate的session是持久化管理器,工做在持久層。

 

數據庫中的表的主鍵生成策略:

Native:Hibernate根據不一樣的數據庫生成不一樣的主鍵,自動增加

Uuid:UUID.randomUUID().toString(),32位字符串

Asigned:用戶本身生成的

Foreign:外鍵維護叫作參照完整性,維護關係。來自於對象裏關聯屬性的值

GUID:increment,線程不安全,建議不要使用

Identity:使用數據庫本身的自動增加策略,oracle數據庫不支持

 

對象與對象的關係:關聯(has a )和繼承(is a )

屬性映射:主鍵通常不從對象中拿,要麼從數據庫中生成,要麼hibernate給。外鍵從對象的關聯屬性上拿。(關聯屬性:屬性類型爲自定義類型必定是關聯屬性,若是屬性爲集合類型,該屬性有多是關聯屬性)

 

Hibernate3.2 的環境搭建:(測試環境—> 真實環境)

測試環境:(不佔內存的,導入的包不在項目裏面,只是作關聯)

一、         建立java Project

二、         建立公共的jar包, windowsàjavaàUser Libraryà包名

三、         在建立的包下導入jar包,(主jar包和lib下的全部jar包+mysql的jar包)

四、         導入項目庫,右鍵—>propertiesàjava buildPathà選擇本身建立的存放jar包的包名

五、         建立source folder (src文件夾)

六、         把ext文件夾下的hibernate.cfg.xml.,log4j.properties文件放在src下

七、         在hibernate.cfg.xml文件中配置持久化數據庫信息,配置方言(不一樣的數據庫有不一樣的方言)<property>

八、         導入映射文件<mapping resource=」com/hibernate/User.htm.xml」/>:將User對象和數據庫中的表進行ORM(對象關係映射)

 

真實環境:將jar包直接導入WEB-INF/lib下,配置信息與虛擬環境相同

 

Hibernate小案例

一、         建立實體類:POJO(表單bean、結果bean)User

二、         建立和實體類對應的元文件  User.hbm.xml(可能多個實體類對應一個元文件)

<hibernate-mapping>

      <class name=」類的全路徑名 cn.bean.User」>//根據類名建立表 user

      <id name=」id」>//id映射表示該屬性爲數據庫中的表的主鍵

<generator class=」uuid」>//generator 表示主鍵的生成策略。設置主鍵爲uuid,Hibernate自動生成

</id>

<property  name=」name」/>//普通屬性用property映射,在表中的字段也叫name

      </class>

</hibernate-mapping>

數據庫中主鍵映射:

若是在對象中爲String id,generator通常爲uuid,32位碼,Hibernate自動生成

若是在對象中爲int id,generator通常爲native,數據庫中自動生成,自增。

 

Hibernate會自動根據表單bean建立和表單bean類名相同的表。

會根據表單bean中的屬性自動建立表的字段,數據類型會自動解析。Int->int,Stringàvarchar,util Date à datetime 類型相對應。長度取默認值。

 

DDL:數據庫定義語言、DCL數據庫控制語言、DML:數據庫操縱語言

DDL(data definition language):
DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定義或改變表(TABLE)的結構,數據類型,表之間的連接和約束等初始化工做上,他們大多在創建表時使用

DML(data manipulation language):
SELECT、UPDATE、INSERT、DELETE,這4條命令是用來對數據庫裏的數據進行操做的語言
DCL(Data Control Language):
是數據庫控制功能。是用來設置或更改數據庫用戶或角色權限的語句,包括(grant,deny,revoke等)語句。在默認狀態下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人員纔有權力執行DCL

 

Hbm2ddl工具類:

Configuration cfg=new Configuration().configure();//將src下的hibernate.hbm.xml文件讀到cfg中

Configuration cfg=new Configuration();//將src下的hibernate.properties文件讀到cfg中

SchemaExport  export=new SchemaExport(cfg);

export.create(true,true);//按照User.hbm.xml文件中配置在數據庫中生成對應的表

 

Hiberante的配置信息:

<hibernate-configuration>

<session-factory> 

<!—配置數據庫的鏈接信息-- >

<property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate_fist</property>

<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="hibernate.connection.username">root</property>

<property name="hibernate.connection.password">mysql</property>

<!—配置mysql數據的方言 -- >

<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<!—配置顯示sql語句,建表,查詢之類的,爲了方便跟蹤sql執行 -- >

<property name="hibernate.show_sql">true</property>

<!—配置表的自動生成策略,若是表不存在就建立,存在就再也不建立-- >

<property name="hibernate.hbm2ddl.auto">update</property>

<!—配置映射文件信息,用於數據庫中的表的生成-- >

<mapping resource="com/hibernate/User.hbm.xml"/>

</session-factory>

</hibernate-configuration>

 

JDK動態代理:對目標類實現同一個接口的代理(經過反射實現)

Cglib代理:代理類是目標類的子類               

 

Hibernate使用的是鏈接池,dbcp鏈接池(內建鏈接池)

autocommit(),自動提交事務默認爲false,須要手動提交事務

 

測試類:把對象變成表中的記錄

一、         Configuration cfg=new Configuration().configure();//讀取配置文件,xml

二、         SessionFactory factory=cfg.buildSessionFactory();//建立工廠,session工廠是重量級的,線程安全,很是耗時,原則上只建立一次。最好是單例+同步。

SessionFactory的做用:生成Session對象,二級緩存,而且能夠配置緩存策略

三、         Session session=null;

session=factory.openSession();//經過工廠拿到Session對象,session是持久化管理器,session是線程不安全的。能夠拿到多個session。

四、         session.beginTransaction();//開啓事務(從數據庫的鏈接池中拿到鏈接),session開啓的事務是session級別的事務,本地事務,局部事務。(經過SessionFactory建立的事務是全局事務,能夠被多個SessionFactory對象使用)

什麼是事務?事務是對數據庫的操做(一致性,原子性,持久性,隔離性)

五、         建立User對象,給User對象賦值

當配置中的<id><generator class =」uuid」/></id>時,本身設定的主鍵是沒有用的。賦值後的user對象是瞬時態

六、         session.save(user);//session會爲user分配主標識,uuid,j將user對象歸入session的緩存,臨時集合insertron中。 保存數據

七、         session.getTransaction().commit();//session 拿到事務後提交事務。在這裏生成sql語句,將user對象中的數據存入數據庫中對應的字段裏。(操縱數據庫

八、         session.getTransaction().rollback();// 發生異常就catch而且回滾

                                                             

Hibernate的優勢:

代碼量少,提升生產力

更加面向對象,對象化

可移植性好(改方言,改配置就能夠啦)

支持透明持久化:輕量級,沒有侵入性

 

事務:

事務分爲總事務和子事務,子事務預提交,總事務發現其中一個子事務出錯就回滾。

公共請求事務對象代理:不是真正的操做只是代理對象。-- > 中間件(代理完成事務的提交)

事務的邊界:session.beginTransaction();

JDNI:java的名稱目錄接口,對鏈接池進行管理。

Hibernate能夠處理多種事務(本地事務,跨資源事務,局部事務),提供統一的事務邊界,(事務邊界由session獲取)

SessionFactory能夠生成全局事務,供多個session使用,而且具備二級緩存功能。

 

Configuration:封裝和管理hibernate的主配置文件。

一、         封裝和管理 :數據庫的底層信息<property>

二、         封裝和管理:映射文件元信息(元數據)<mapping-resources>

獲取方式:

一、         new Configuration();//拿到hibernate.properties文件

二、         new Configuration().configure();//拿到hibernate.hbm.xml的配置信息

 

SessionFactory:(session工廠也叫會話工廠)

SessionFactory經過Configuration建立,建立特色:cfg.buildSessionFactory();

SessionFactory功能:

一、         取得持久化對象session(factory.openSession())

二、         生成全局性事務(Tansaction) 事物共享控制安全性

三、         緩存(緩存元數據和不常修改的數據) 緩存後,通常不釋放。

緩存定義:第一次讀取後,第二次訪問時已經存入內存,直接讀取效率會高。

緩存目的:提升對數據庫的訪問效率

SessionFactory性能:

一、         SessionFactory屬於重量級,耗時,通常只在初始化的時候建立一次PlugIn

二、         原則上一個Hibernate能夠管理多個數據庫,每一個數據庫都配置同一個SessionFactory(可是這樣系統效率低,通常一個sessionFactory只管理一個數據庫)

三、         sessionFactory是線程安全的(支持多線程併發訪問操做)

 

Session:持久化管理器

做用:完成對數據庫的操做 ORM(對象關係映射)

Session的功能:

一、         Session一級緩存,具備緩存功能(緩存後才能對對象進行持久化操做)

二、         保證所管理的對象中的屬性和數據庫中的記錄同步(自動update)

三、         Session能夠取得事務(對session中緩存的數據完成實際的存取)

四、         Session做爲HQL查詢à生成的Query對象,執行hql語句(session.createQuery())

Query query = session.createQuery(「from User」); 其中User是實體類。該語句將把數據庫中t_user表中的所有查詢出來放到query對象中。

五、         Session能夠從鏈接池中拿到鏈接(關閉session,鏈接釋放)

六、        Session的主要做用是對數據庫進行操做

Session的性能:

一、         線程不安全(操做完數據庫就關閉session)

        

Lifecycle,Validatable:能夠對User請求作監聽,不使用的緣由是由於須要實現接口,太過於重量級了。

UserType(轉換器),Interceptor(攔截器):都屬於輕量級接口,對對象自動攔截,不多用,不須要實現Hibernate的任何接口和類

 

實際測試環境的搭建:

測試類和被測試類在同一個包下 src

測試類的路徑和被測試類相同

 

使用Junit測試原則:

一、         測試類繼承TestCase(Junit包下的一個類)

二、         測試類的類名 suffix=*Test(eg:UserTest)

三、         測試類的方法名:test開頭,後面是正常的方法名(eg:testLogin())

四、         測試類的方法不能有參數和任何返回值

 

調試時在debug—> Junit

Log4j.propeties:是爲了測試的時候打印日誌信息。

 

持久化對象的生命週期:

對象的三種狀態:瞬時、持久、離線

瞬時態(transient):new User(),剛new 出類的對象處於瞬時態,沒有歸入session的管理,數據庫中沒有對象的記錄,事務提交後這種對象沒法提交到數據庫(和數據庫無關聯)

持久態(presist):session. save(user),這時候user對象處於持久態,session管理,具備主標識,事務對持久態的對象作操做(和數據庫同步)

離線態(Detached):session關閉後對象狀態從持久態變爲離線態。不歸入session管理中,對象中的數據在數據庫中有對應的記錄,對象發生變化,數據庫中的值不會發生變化。(和數據庫不一樣步)

 

Junit測試和Debug調試

測試類的代碼和hibernate的代碼在同一個包下

 

Session緩存的特色:

Session session=factory.openSession();//經過session工廠拿到session對象

Variable à 查看actionQueue: session隊列中有三個臨時集合(數組)

一、insertions:elementData session.save();存放要插入的對象

二、updates :elementData session.update();存放要修改的對象

三、deletions:elementData,存放要刪除的對象

Session事務一提交,先到insertion數組中查看是否有對象,若是有對象,就發送有關插入數據的sql語句:insert  into()

再從deleteions數組中查看是否有對象,若是有對象就發送關於刪除的sql語句:delete()

再從updates數組中查看是否有對象,若是有對象就發送關於更新的sql語句:update()

這三個集合都是臨時集合,發送完數據就會清空。

 

PersistanceContext:持久化容器

存放真正緩存中的數據,實體化類對象必定要和數據庫中記錄的狀態一致。

數據結構爲entityEntries 是一個Map

裏面有個Map集合,進入 entries 裏面有個table ,真正歸入session的中的數據存在table裏面。

 

局部事務:

一、         session.openTransaction();開啓事務,事務一提交,就去找session的actionQueue中的數組,發現哪一個數組中有對象就執行其對應的語句,

二、         User user=new User();建立user對象,user.setName(「zhangsan」);,這時候的user對象沒有id,(若是主鍵的生成策略是uuid,手動設定的id是無效的)這時候還的user對象處於瞬時狀態,沒有歸入session管理

三、         session.save(user);執行這條語句,通常不會發sql語句,只是將user對象歸入session管理,語句執行完後:session中的臨時數組:insertions數組中會有數據。User會分爲兩份存儲:

一份是instance :實例

一份是 state:狀態,session按照state狀態發送數據(執行insert語句)

Insert以後,若是發現實例和狀態的數據不同,會立刻執行update語句

四、         session.save(user)後,查看persistanceContext à entityEntries à map à table(table是HashMap的元素) value有值就繼續進去, 查看如下幾個狀態值:

existsInDatabase的狀態爲false,表示數據庫中沒有數據

這時候id已經被賦值(uuid)

loaderSate:存儲狀態,數據都存在這裏了(這裏的數據必須和數據庫中的數據同步)這就是session緩存的數據。

目前的user對象處於持久態

處於持久態對象的特色:必定有ID(不是全部持久態的數據會存放在臨時數組中,可是持久態的數據必定會在map集合中)

五、         user.setName(「tom」);歸入session管理以後,變爲持久態以後,修改持久態對象的值,

六、         session.commit();修改後的數據提交,

actionQueue下的insertions數組中:  instance實例中的值爲:tom;

state狀態的值爲:zhangsan

persisstanceContext底下的map:數值仍是zhangsan

會按照insertions數組的state(狀態)的值發送insert into (zhagnsan)到數據庫中

insert語句執行完後,發現instance  和state 中的值不同時,就會再發一條update語句,保證一致。發完sql語句後,臨時集合中的數據會所有清空。

七、         session.commit()狀態:persistanceContext à entityEntries à map àtableà[0]àvalues

existsInDatabase的狀態爲true,表示數據庫中已經有了相應的記錄。這時候map中的name會改成tom(由於update了)

八、         session.close(),關閉session,把session中存放的實體對象所有清除,這時候user對象處於離線狀態,user對象還有主鍵,並且這個主鍵和數據庫中的主鍵同樣。離線態的對象在數據庫中有對應的記錄

瞬時態:沒有歸入session的管理,數據庫中沒有對應的記錄

離線態:沒有歸入session的管理,數據庫中 有對應的記錄。(只要主標識和數據庫中的主鍵相同,主標識的class爲asigned,就爲離線態(new出來的對象也能夠是離線態)),離線態的對象不能直接save()進入持久態。

九、        將離線態變爲持久態:update(),saveOrUpdate(),lock(),從新開啓session,拿到事務,session.update(user); <若是new 出來的對象,本身設定的id和數據庫中主鍵相同,id標識爲assigned,那麼就只能用update方法,讓該對象變爲持久態>

十、      對象從離線態變爲持久態,臨時集合(insertions/updates/deletions)中 都 沒有數據。persistanceContext--àMap集合有數據,可是map中只有一個id記錄,數據庫中也會有相應的數據。

十一、      session.commit();就算沒有修改user對象的屬性值,hiberante也會發送update語句。Update以後,user也被歸入session管理

十二、      session.commit()後,再進入persistanceContext à entityEntries à map à table à[0]àvalues

loadedSate會有相應的user實體對象中的屬性的記錄了

瞬時態—》持久態:插入(save)

離線態—》持久態:更新( update )

 

加載:(查詢)將數據庫中的記錄加載到對象中

一、         拿到session(經過靜態方法 自定義的getSession())

二、         User user=(User)Session.get(User.class,」這裏寫數據庫中的主鍵值」);//把關係模型轉換爲對象模型。經過User類中找到對應的表。(這裏是由於session經過sessionFactory建立,sessionFactory經過Configuration建立,Configuration能夠讀取整個hibernate.hbm.xml文件中的配置信息,因此session能夠找到對應的user.hbm.xml文件)找到User.hbm.xml文件。Session經過hbm.xml元數據找到對應的表。經過主鍵找到表中對應的那條記錄,自動生成對象,而且自動將數據庫中記錄的字段設定到user對象中對應的屬性中。

這裏發送了一條sql語句:select * from user where id=」上面的id參數值」;

這就叫作加載(load())。這時候的user對象處於持久態。(和數據庫同步)

三、         user對象已經進入持久態,歸入session管理。這時候緩存域(三個集合 insertions/deletions/updates)中是沒有數據的,persistanceContextà底下的map 的 value中會有數據。

四、         user.setName(「lisi」);//加載後,修改user對象中值。這時候session緩存的臨時集合中沒有數據,persistanceContext中的map中的數據也沒有變化,user對象中的數據被修改了。 Session發現user對象中的值和map中的值不同時,會自動發update語句,保證數據同步

五、        session.commit();//修改後提交數據。Update以後,數據會變爲和對象中的值同樣。Session緩存中的數據也會變成和user對象中的值同樣。

持久態對象特色:一提交就發update.肯定保證對象和數據庫中數據同步。

處於持久態的對象在數據庫中不必定有對應的記錄。

session.commit()後:數據庫中記錄必定要和persistanaceContextàlocalState中的數據一致

使用get()方法加載數據的缺點:

一、若是從數據庫中找到了指定ID對應的記錄?

若是不須要使用User對象,會浪費內存

二、沒有從數據庫中找到ID對應的記錄?

Hiberante就不會建立對應的User對象,會返回null。

再調用該對象的方法時,會出現NullPointerException

 

根據以上問題,修改方案:使用Load();

User user=(User)Session.load(User.class,」這裏寫數據庫中的主鍵值」);//從這裏引入懶加載。執行load方法後,不會生成sql語句,意味着尚未訪問數據庫。可是會生成Cglib 代理的User對象,這個User對象是目標User對象的一個子類對象。因此這裏的User類不能定義爲final類型。

Cglib代理的原理:建立目標類的子類對象。

這時候代理User對象的target 值爲null;

user.getName();//調用代理對象的getName()方法,若是不爲null,代理對象會調用父類的getName()方法。而後à發送sql語句à建立User對象。

 

本週做業:

理解hiberante:juint,debug,生命週期,緩存,

理解物料管理項目

 

Get方法查詢數據:session.get(User.class,」對應的數據庫中的表的ID」);

一、經過類找表

二、經過主鍵找記錄

三、記錄映射

無論數據存不存在都會發送select語句,若是找到了就會生成user對象,將對應的記錄映射到對象的屬性中,若是沒有對應記錄就返回null。(不支持懶加載)

使用get查詢,會立刻發select語句,select * from user where id=」」;

若是後面再也不修改User對象的值,就不會再發update語句了

若是修改user對象的值就會發送update語句。

這裏主要比較user對象裏面的值和loadedState裏面的值,若是相同就不會發送update語句,若是不一樣就會發送update語句。

 

懶加載:(最大的問題就是會產生懶加載異常)

使用時再訪問數據庫,不使用數據的時候就不會訪問數據庫

懶加載有類上的懶加載,集合上的懶加載,單端上的懶加載

懶加載不支持多態查詢

悲觀鎖不支持懶加載

 

Load方法查詢數據:session.load(User.class,」對應的數據庫中的表的ID」);

不會立刻就發select語句,使用的時候纔會發送select語句,若是不使用就不發送

一、         建立目標對象的cglib代理對象(目標對象的子類對象)

目標類User必需要有明確的無參構造方法,(爲目標類建立代理對象,建立子類對象要先調用父類的無參構造方法),目標類不能是final類型

二、         對應的cglib代理對象下,有個target標識,標明目標對象是否生成,沒有生成目標對象的時候 target=null;

三、         執行代理對象的user.getName()時,;先看目標對象產生了沒有(判斷target的值),若是產生了就執行目標對象的getName()方法,若是沒有就從數據庫中查出主鍵所標識的記錄,根據記錄生成目標User對象,再調用該User對象的getName()方法。(生成目標對象後,target=user(目標對象的地址))

Load()方法在數據庫中找不到主鍵所標識的記錄時,會產生異常 :ObjectNotFoundException,而不是NullPointerException。這個異常在load()的時候產生這個異常,可是使用目標對象的時候就會拋出這個異常。

Get()方法在數據庫中找不到主鍵所標識的記錄時,會返回null,再在使用目標對象時,會拋出NullPointerException.

 

Get和load:

共同點:經過ID(主鍵)去數據庫中找相同的記錄,生成對應的User 對象

不一樣點:

一、         Get:不支持懶加載,立刻發送select語句

Load:支持懶加載,使用的時候才發送select語句

二、         若是使用get,發現主鍵在數據庫中找不到對應的記錄,不會生成user對象,也不會拋出異常,會返回null.(使用對象是拋出NullPointerException.)

若是使用load,發現主鍵在數據庫中找不到對應的記錄時, 使用時會拋出ObjectNotFoundException。

通常使用load,不多使用get.

 

刪除:(刪除的內容:數據庫中的數據

先查 à 後刪

User user=(User)session.load(User.class,」ID」);//對象處於持久態

session.delete(user);//在session的緩存persistanceContext中的map集合中有該對象,existInDatabase=true,loadedState有該對象;

臨時集合中的deletions中有該對象,

User代理對象中也有數據。target=user;

 

session.getTransaction().commit();//這裏發送delete語句(進入瞬時態)

臨時集合deletions中數據被清空

Session裏面的persistanceContextàmap中沒有數據

數據庫中的數據也沒有

User對象依舊存在,target=user.

刪除了User對象對應的數據庫中的表的記錄,刪除後對象進入瞬時態。(不歸入session管理,數據庫中的記錄也沒有了)

(課堂做業:完成hiberante的簡單的增刪改查)

 

查看錶中的全部記錄:(Query

Query query=session.createQuery(「from User」);//hql語句,from後面跟的是類名。經過session建立Query對象,實參爲hql語句。從User類所映射的user表中拿到每一條記錄,封裝到Query對象中。

Query對象的做用是:執行hql語句。

List list=query.list();//Query中的list方法,把Query封裝的每條記錄放到List集合中。

 

Hibernate的基本映射

若是主鍵爲String類型,通常配置爲uuid<generator class=」uuid」/>

關聯映射:

<hiberante-mapping  package=」com.hibernate」>//多個實體類在同一個包下,這裏配置了包名,使用時直接寫具體類名就能夠了

<class name=」User」 table=」t_user」>//根據類名爲User的實體類生成表,table:指定數據庫中的表名爲t_user

<id name=」id」 column=」user_id」 length=」33」>//定義主鍵名在數據庫的表中的字段名爲 user_id,長度位33,(最好不要修改主鍵字段長度,使用默認長度255

<generator class=」uuid」/>//標識主鍵生成策略爲uuid

</id>

 

/* Name:標識在User類的屬性

Unique:標識該字段在數據庫的表中必須惟一(不能重複)

Not-null:標識該字段在數據庫的表中不能爲null

爲空驗證:user.setName(「」);這裏不會產生異常,空串不爲空。

若是直接不給name賦值,String類型默認值爲null,就會產生NulPointerException

Length:指定該字段的最大長度

Column:指定該屬性映射在數據庫中的表中的字段名爲 t_name

*/

<property name=」name」 unique=」true」 not-null=」true」 length=」232」 column=」t_name」>

 

</class>

<hibernate-mapping>

 

主鍵生成策略:(做用/目的:一旦實體類歸入session的管理,session根據實體類的主鍵生成策略生成主標識,這個主標識就是數據庫中的記錄的主鍵

主標識:

業務主鍵和業務標識:(有意義的惟一標識)--學號

邏輯主鍵和邏輯標識:(沒有意義的惟一標識),由數據庫或者是Hibernate生成uuid

 

根據實體類的ID類型肯定主鍵生成策略:

若是是字符串,hibernate生成 。uuid

若是是int/long,由數據庫生成。native

用<id>標籤來標識主鍵,後面必需要跟<generator class=」 主鍵生成策略

」>

 

1、由數據庫生成:

native:根據不一樣的數據庫自動使用不一樣的主鍵生成策略。(不是真正的生成策略,只是選擇對應的生成策略)

identity:(mysql,sqlserver)

sequence:(DB2,Oracle)

2、由程序生成:

increment(遞增):多臺應用服務器,可能會主鍵相沖,不多使用

uuid.hex:Hibernate採用這種方式生成主鍵,多態服務器同時生成主鍵,不會重複;

一、         IP地址

二、         JVM的啓動時間(1/4秒)

三、         系統時間

四、         隨機數(和指令計數器有關)

以上四種數據進行隨機組合,最後生成32 uuid 碼

3、用戶本身維護主鍵:

assigned:用戶本身輸入

4、外部引用

foreign:依賴於另外一個實體類的主鍵。

 

主鍵生成策略爲uuid: (主屬性類型爲String類型)

1三、      session.openTransaction();開啓事務,事務一提交,就去找session的actionQueue中的數組,發現哪一個數組中有對象就執行其對應的語句,

1四、      User user=new User();建立user對象,user.setName(「zhangsan」);,這時候的user對象沒有id,(主鍵的生成策略是uuid,手動設定的id是無效的)這時候還的user對象處於瞬時狀態,沒有歸入session管理

1五、      session.save(user);執行這條語句,通常不會發sql語句,只是將user對象歸入session管理,語句執行完後:session中的臨時數組:insertions數組中會有數據。User會分爲兩份存儲:

一份是instance :實例

一份是 state:狀態,session按照state狀態發送數據(執行insert語句)

Insert以後,若是發現實例和狀態的數據不同,會立刻執行update語句

1六、      session.save(user)後,查看persistanceContext à entityEntries à map à table(table是HashMap的元素) value有值就繼續進去, 查看如下幾個狀態值:

existsInDatabase的狀態爲false,表示數據庫中沒有數據

這時候id已經被賦值(uuid)

loadedSate:存儲狀態,數據都存在這裏了(這裏的數據必須和數據庫中的數據同步)這就是session緩存的數據。

目前的user對象處於持久態

處於持久態對象的特色:必定有ID(不是全部持久態的數據會存放在臨時數組中,可是持久態的數據必定會在map集合中)

1七、      user.setName(「tom」);歸入session管理以後,變爲持久態以後,修改持久態對象的值,

1八、      session.commit();修改後的數據提交,

actionQueue下的insertions數組中:  instance實例中的值爲:tom;

state狀態的值爲:zhangsan

persisstanceContext底下的map:數值仍是zhangsan

會按照insertions數組的state(狀態)的值發送insert into (zhagnsan)到數據庫中

insert語句執行完後,發現instance  和state 中的值不同時,就會再發一條update語句,保證一致。發完sql語句後,臨時集合中的數據會所有清空。

session.commit()狀態:persistanceContext à entityEntries à map àtableà[0]àvalues

existsInDatabase的狀態爲true,表示數據庫中已經有了相應的記錄。這時候map中的name會改成tom(由於update了)

1九、      session.close(),關閉session,把session中存放的實體對象所有清除,這時候user對象處於離線狀態,user對象還有主鍵,並且這個主鍵和數據庫中的主鍵同樣。離線態的對象在數據庫中有對應的記錄

瞬時態:沒有歸入session的管理,數據庫中沒有對應的記錄

離線態:沒有歸入session的管理,數據庫中 有對應的記錄。(只要主標識和數據庫中的主鍵相同,主標識的class爲asigned,就爲離線態(new出來的對象也能夠是離線態)),離線態的對象不能直接save()進入持久態。

 

主鍵生成策略爲native: (主屬性類型爲 int類型)

先看使用的數據庫,而後根據數據庫選擇主鍵生成方式,若是是mysql數據庫:identityà auto_increment

數據庫自動生成主鍵:首先是主屬性爲int類型,大部分都是從1開始(基本上沒有從0開始的數據)。

session.save(user);//就發送sql語句了,insert語句中不會插入ID,ID由數據庫自動生成,人爲設定的sql語句無效。發送sql語句:insert into(),目的是生成主鍵

一旦發送sql語句了,數據庫中必定會有對應的記錄。

主鍵生成策略爲native,save後的特色:(進入持久態)

一、         session的臨時集合中沒有數據,(臨時集合是爲了發sql語句而存在的)

二、         session的緩存persistanceContext下的map下的existInDatabase=true;表示數據庫中有數據,可是是髒數據。(查不到)

髒數據:未經證實和檢測的數據

事務提交特色:先插入數據,再對數據進行驗證,若是正確就提交到數據庫中,若是有誤就回滾。沒有提交的數據叫作髒數據,mysql事務的傳播特性是可提交讀,看到的數據是提交後的數據,看不到髒數據,因此提交前在數據庫的表中查不到數據。

session.getTransaction().commit();//commit()後纔會檢查數據的正確性。(對數據庫中的髒數據進行確認,若是正確就將髒數據變爲可用數據《在數據庫中能夠查到》,若是不正確就將髒數據回滾。)

主鍵生成策略爲assigned(用戶提供)

主鍵標識 爲String類型

一、         不設置對象的ID值,直接save(),會從user對象中共取ID,發現沒有會拋出異常

二、         2.一、設置ID值

2.二、   session.save(user);從瞬時態進入持久態:不發送sql語句,user對象歸入session緩存,存放在session的臨時集合中的insertions中,session的緩存persistanceContext下的map的loadedState下也會有數據。數據庫中沒有數據,existInDatabase=false;

2.三、   session.getTransaction().commit();臨時集合中的數據清空,數據庫中有數據。existInDatabase=true;

 

經過配置讓數據庫自動生成表:

<property  name=」 hiberante.hbm2ddl.auto」>update</property>

 

 

做業:分析三種主鍵生成策略在session緩存中的異同:

主鍵生成策略爲native和uuid,assigned的異同:

native根據數據庫不一樣而自動選擇對應數據庫的主鍵生成策略。好比:mysql數據庫à主鍵生成策略爲:identity.主屬性類型爲int類型。(用戶手動設定的ID無效)

uuid將IP地址,JVM的啓動時間,系統時間以及和指令相關的隨機數相結合隨機生成32位uuid碼。主屬性類型爲String類型。(用戶手動設定的ID無效)

assigned用戶在建立對象的時候,手動設定主鍵(ID的值)。主屬性類型爲String類型,若是不設定ID 就會拋出異常。

session.getTransaction();//開啓事務

 

User user=new User();//建立User對象

user.setID(「123」);

user.setName(「zhangSan」);

這時候三種主鍵生成方式(native,uuid,assigned)都相同: user對象處於瞬時態,沒有歸入session管理(臨時集合和session緩存map中都沒有user對象的相關數據)

 

session.save(user);

相同點:將user對象歸入session管理,進入持久態。

native:(發送insert 語句)。臨時集合中沒有數據,session緩存的map下的table中有數據,loadedState中有user對象值,existInDatabase=true,user對象依舊存在,user對象的ID值變爲數據庫中自增的ID值。

uuid:臨時集合insertions中的instance和state中都有數據,ID爲hibernate自動生成的32位碼,session緩存的map下的table中有數據,loadedState中有user對象值,existInDatabase=false,user對象依舊存在(ID爲32位碼,其他屬性值不變)

assigned;臨時集合insertions中的instance和state中都有數據,ID爲用戶手動設定的值,session緩存的map下的table中有數據,loadedState中有user對象值,existInDatabase=false,user對象依舊存在(值和設定的同樣)。

 

user.setName(「Tom」);//修改進入持久態後的數據

相同點:user對象中的name屬性值變爲Tom,session 緩存的map下的table中的值不變。

native:臨時集合中依舊沒有數據。

uuid: 臨時集合insertions中的instance 指定的name屬性改變爲 Tom, state指定的name屬性依舊爲zhangSan,

assigned: 臨時集合insertions中的instance 指定的name屬性改變爲 Tom, state指定的name屬性依舊爲zhangSan。

 

session.getTransaction().commit();//提交事務

相同點:session緩存的map下的table中有數據,loadedState中user對象的name屬性值爲Tom,existInDatabase=true,

native:(發送update語句), user對象依然存在。

uuid: (發送兩條sql語句,先insert,後update)臨時集合insertions的數據清空, user對象依然存在。

assigned: (發送兩條sql語句,先insert,後update)臨時集合insertions的數據清空, user對象依然存在。

 

session.close();//關閉事務

相同點:session緩存map下的table中的數據被清空, user對象依然存在。user對象處於瞬時態。

native:臨時集合中一直沒有數據,

uuid:臨時集合中數據被清空,

assigned:臨時集合中數據被清空

 

 

Many-to-one

關聯屬性:多對一:(Many-to-one

關聯屬性的特色:

若是屬性的類型時自定義類型,那麼這個屬性就是關聯屬性

若是屬性的類型時集合或者數組,那麼這個屬性有多是關聯屬性

對象模型經過關聯屬性創建關聯關係

關係模型的關聯關係經過外鍵創建關係

誰往外鍵裏存放數據,誰就在維護關係。

 

核心技術:配置*.hbm.xml

 

關聯屬性的映射:(外鍵)

<many-to-one name=」group」  column=」groupid」 cascade=」」/>

column=」groupid」:這個值的設定參照group屬性對應的Group對象的id屬性。

這是怎麼知道引用的表是group表的主鍵?

由於User實體類中,有Group類型的group屬性。根據關聯屬性找到引用的外鍵表。

 

存儲數據:(主鍵生成策略都爲native)

Group group=new Group();//先建立Group對象,

group.setName(「zte」);// 給該對象的屬性賦值,(id自動分配)

session.save(group);//發送insert into 語句,取得id

 

將Group對象的地址賦值給User對象的group屬性中。

User user=new User();//再建立User對象,

user.setName(「zs」);// 給該對象的屬性賦值,(id自動分配)

user.setGroup(group);//經過user拿到group屬性所指的Group對象,取出該Group對象的id值,將取到的id設定到groupid字段中。(groupid是 hbm.xml文件中 many-to-one 標籤中的name指定的字段名)

session.save(user);//數據庫發送sql語句,insert into ,生成id。將user對象屬性的屬性值設定到數據庫中的user表中的對應的記錄的各個字段中。

 

cascade(級聯屬性): all,update-save,delete,none

 

加載:(將關係模型轉換爲對象模型)

User user=(User)session.load(User.class,1);

將從數據庫中查出來的記錄,將字段依次賦值給User對象的對應屬性。

Many-to-one:經過外鍵(groupid)找到對應的 Group對象對應的group表中的記錄,根據記錄再建立Group對象賦值,將Group對象設定到User對象的group屬性中。(經過外鍵找主鍵)

 

user.getName();//zs,這叫類上的懶加載,只發送一條sql語句,只查詢user表

user.getGroup().getName();//這就叫單端上的懶加載,用到兩張表的時候查詢兩張表

單端上的懶加載在外鍵設定的標籤上,<set><many-to-one>

外鍵能夠爲空,若是不爲空,必定是所關聯表的有效值(通常爲關聯表的主鍵)

TransientObjectException:持久態對象引用了瞬時態對象

若是不給Group對象的id屬性設值,那麼默認值爲0 ,

不save Group對象。Group對象爲瞬時態。id依舊爲默認值0

建立User對象,給user對象的各個屬性賦值。

session.save(user);//發送sql語句,insert into

將Group對象的id屬性值(0)設置到User對象中的group屬性所指Group對象的id ,對應數據庫中的字段爲groupid上。

Mysql數據庫的主鍵不能爲0 ,因此這就違反了參照完整性。

session.getTransaction().commit();

提交,就檢查數據,發現錯誤就回滾。(commit之前的數據都是髒數據

這裏的user對象處於持久態,user對象使用了瞬時態數據 group對象,瞬時態的對象的id是無效值。因此會出現TransientObjectExecption.

主要緣由:持久態對象引用了瞬時態對象,就會出現TransientObjectException

結論:持久態對象不能引用瞬時態對象。

解決辦法:

  1. 1.   建立group對象的時候就session.save(group);

2、添加級聯屬性。

<many-to-one name=」group」 column=」groupid」 cascade=」all」>//持久態對象引用瞬時態對象,加了cascade時,會自動先把瞬時態對象save進入持久態。在執行session.save(user)這條語句時,會先執行 insert into group(),再執行insert into user();

 

cascade:(級聯屬性)

cascade的取值:all、none、save-update,delete

cascade的做用:存儲,而不是爲加載服務的。

級聯:兩個對象中的操做聯動性,對一個對象操做後,對其指定的級聯對象也須要執行相同的操做。(對象的連鎖操做)

all:全部狀況下執行級聯操做

none:任何狀況下都不執行級聯操做

delete:在刪除的時候執行級聯操做(容易出錯)

save-update:在保存和更新的時候執行級聯操做

 

Hql語句對全部的數據庫都同樣:

Query query=session.createQuery(「from User user where user.group.name=’zte’ 」);//從數據庫中找到User類對應的t_user表,從user表中找到user對象的group屬性所指定的Group對象對應的t_group表,找出t_group表中的name=‘zte’的那條記錄。

至關於sql語句;

select * from t_user ,t_group

where t_user.groupid=t_group.id

and  t_group.name=’zte’;

 

One-to-one:(一對一)

一對一單向關聯:

對象模型:

Person: (類)

int id;

String name;

IdCard idCard;//關聯屬性

 

IdCard:(類)

int id;

String cardNo;

 

關係模型:

Person表和idCard表的主鍵相同,一 一對應

(兩個表的主鍵相同並不意味着兩個表有關係)

 

三大問題:(在*.hbm.xml文件中體現)

一、如何創建關係

二、如何存儲關係

三、如何加載關係

 

創建關係:(在數據庫中建表時)

t_person:(表)

具備關聯屬性的那個對象生成對應的表時,須要設定主鍵生成策略爲 foreign:t_person表中這樣設定,使得對象一 一對應。

<id name=」id」>

<generator class=」foreign」>

<param name=」property」>idCard</param>//引用的是idCard屬性的所指的IdCard對象的主標識 (id屬性值)

</generator>

 </id>

一旦歸入session的管理,person對象的id須要和IdCard的id相同。(經過session保證一對一關係:執行save語句的時候將IdCard對象的id賦值給person對象的id)

必須加約束: <one-to-one name=」idCard」 constrained=」true」/> 建立t_person表的時候不會建立idCard字段,這裏表示引用的外鍵是當前t_person表的主鍵,外鍵參照idCard屬性對應的IdCard對象所映射的t_idCard表。

一、        創建關係:表示當前person類所映射的表的主鍵就是外鍵,

二、        存儲關係:外鍵參照idCard屬性對應的類所映射的表生成

三、        加載關係:one-to –one ,經過主找主,爲當前映射屬性idCard賦值

若是不添加 constrained=true,在建立表的時候會缺乏 foreign key(id) references t_idCard (id)語句,就不會有外鍵。這樣t_person表和t_idCard表就沒有任何關係了。

 

t_idCard:(表)

和普通表同樣,沒有外鍵,主鍵爲id.<id name=id>

<generator class=」native」 /></id>

 

存儲關係:(在生成對象時創建存儲關係)

一、         先建立IdCard對象,

IdCard idCard=new IdCard();

idCard.setCardNo(「123」);

session.save(idCard);//發送sql語句,生成id主標識,歸入session管理

二、         建立Person對象

Person person =new Person();

person.setName(「tom」);

person.setIdCard(idCard);

session.save(person);//這裏不發送sql語句,(由於不須要數據庫爲之生成id)直接從Person對象的idCard屬性所指的IdCard對象中拿到主標識(IdCard對象中的id屬性值)設爲Person對象的主標識(id屬性)。從這裏創建了對象和對象之間的一對一關係。臨時集合中有數據(insertions),數據庫中沒有數據。

commit()就把Person的id屬性映射到t_person的id字段上。

主標識:爲對象中的id

主鍵:爲數據庫表中的id

 

三、        t_person表在數據庫中存儲Person對象的idCard(關聯)屬性的時候:把關聯屬性的值存在數據庫的表的外鍵上。 往對應的外鍵設值,經過映射發現,存儲idCard屬性的時候,外鍵就是主鍵 id。往主鍵id上設定值,t_person表的主鍵id值來自於idCard屬性所指的IdCard對象映射的t_idCard表的主鍵(id).

 

One-to-one:主鍵關聯,自動級聯。cascade=」all」

倘若在session.save(person);語句執行以前,不執行session.save(idCard)語句,

IdCard對象尚未歸入session管理,也沒有爲 IdCard對象分配主標識,IdCard對象處於瞬時態。

一、         執行session.save(person)時,發現IdCard沒有主標識,Person對象沒法拿到主標識。

二、         自動先發送IdCard的sql語句,insert into t_idCard (),先爲IdCard對象分配主標識(IdCard對象歸入session管理,進入持久態,IdCard在數據庫中有數據)

三、         將IdCard的主標識賦給Person對象的主標識,Person對象歸入session管理,進入持久態。Person在數據庫中沒有數據,由於person尚未發送sql語句,因此Person對象在臨時集合insertions中有數據。

 

加載關係: one-to-one(主找主)

Session.load(Person.class,1);

經過t_person表的主鍵找到所關聯的t_idCard表的主鍵對應的記錄,建立IdCard對象,並將該IdCard對象賦值給Person對象的idCard屬性。其他屬性直接賦值。

 

Session不容許同一個類的不一樣對象使用相同的ID

若是兩個不一樣的對象引用同一個對象的id,會拋出異常。

例如:

IdCard idCard=new IdCard();//建立Idcard對象

idCard.setCardNo(「123」);

session.save(idCard);//發送sql語句,生成id主標識,歸入session管理

 

Person person =new Person();//建立第一個Person對象

person.setName(「tom」);

person.setIdCard(idCard);

      session.save(person);//不會發送sql語句,將IdCard對象的主標識id給該Person對象

 

Person person1 =new Person();//建立第二個Person對象

person.setName(「lisa」);

person.setIdCard(idCard);//引用同一個IdCard對象

      session.save(person1);// //不會發送sql語句,將IdCard對象的主標識id給該Person對象,因此在這裏會拋出異常,由於one-to-one不一樣對象不能引用同一個對象的id.

 

一對一主鍵雙向關聯: 兩張表中的數據能夠相互查詢

IdCard.hbm.xml中:主鍵生成策略爲native

這裏的person在實體類中爲Person類型。

<one-to-one name=」person」>:單獨使用不會在建立表的時候不會指定外鍵,也不能往外鍵中設值,只會加載關係,給Person屬性賦值。

person 不是關聯屬性,

 

Person.hbm.xml中:主鍵生成策略爲foreign

這裏的idCard在實體類中爲IdCard類型。

<one-to-one name=」 idCard」 constrained=」true」>:指定外鍵就是主鍵,外鍵的生成參照idCard屬性對應的IdCard類映射的idCard表。還能夠加載。

idCard爲關聯屬性,

 

創建關係:兩張表的關係依靠Person類中的idCard屬性創建關係。

維護關係:存儲Person的idCard屬性時,外鍵就是t_person的主鍵,維護關係。

加載關係:

session.load(IdCard.class,1);

IdCard類中的person屬性經過one-to-one(主找主),到t_person表中找到對應的記錄,建立Person對象給IdCard類的person 屬性賦值。

 

不是全部的類中的自定義類型都是關聯屬性,當前IdCard類中的person屬性就不是關聯屬性,

 

 

一對一惟一外鍵單向關聯:(外鍵 unique=」true」

讓外鍵惟一,兩條記錄即是一 一對應。

Person的配置:使用 many-to-one.配置

<generator class=」native」/>

<many-to-one name=」idcard」 unique=」true」>//在映射關聯屬性建立關係的時候會在表中建立字段idCard,關係經過many-to-one :建立外鍵,外鍵參照idCard屬性的類所映射的t_idcard表。 設定unique=」true」,使得外鍵惟一。

<many-to-one name=」idcard」>:必定要先save所引用的對象,它不會自動級聯,在數據庫的表中會生成idcard字段。

<one-to-one name=」idcard」>指定主鍵就是外鍵,能夠自動級聯,在數據庫的表中不會生成idcard字段。

 

創建關係:

<one-to-one name=」idcard」>至關於:<many-to-one unique=」true」 name=」idcard」 cascade=」all」> 由many-to-one創建關係,指定idcard爲外鍵。

 

加載關係:

Many-to-one:找到idCard在t_idcard表中對應的記錄,生成IdCard對象,設定到Person對象中的idcard屬性上。

 

存儲關係:

Person存儲關係:存儲Person的idcard屬性值,往數據庫的idcard字段設值是存儲,由於idcard字段是外鍵。(若是往不一樣的對象的idcard屬性中存同一個的idcard對象,因爲設定了unique=」true」,就會拋出異常,外鍵重複。)

在這時候 持久態對象引用瞬時態對象,因爲瞬時態對象的默認id值爲0,不影響數據庫的存儲,在save()的時候不會產生異常,commit()的時候數據庫就會檢測0爲非法數據就會拋出異常。

 

一對一惟一外鍵的雙向關聯:

Person表的映射不變:

int id;

IdCard idcard;//關聯屬性,創建關係,維護關係,加載關係(外鍵

 

<id name=」id」><generator class=」native」/><id>

<many-to-one name=」idcard」 unique=」true」>

 

 

IdCard表的映射:

int id;

String cardNo;

Person person;//不創建關係,不維護關係,只加載關係(非外鍵

 

<id name=」id」><generator class=」native」/><id>

<property name=」cardNo」/>

<one-to-one name=」person」 property-ref=」idcard」/ >

property-ref=」idcard」:將位置指針挪到t_person表中的idcard屬性上。

property-ref表示引用person屬性對應的Person對象所映射的t_person表中的idcard字段。

 

加載關係:

給IdCard的person屬性設值,one-to-one

主找主找不到對應的數據,property-ref=」idcard」,位置指針挪到idcard字段上,找到idcard字段對應的那條記錄,生成Person對象,將該person對象賦值到person屬性中,

 

MyEclipse快捷鍵:

Ctrl+shift+T:查找類

Ctrl+F:查找類中的具體方法

 

 

one-to-many

one-to-many 單向關聯:

一對多:由一的一方來創建關係,存儲關係,使用關係(加載)。

外鍵必定在多的一方。

一怎麼管理多:創建集合,管理多方(Set集合)

爲何不是List集合和map集合,由於Hibernate爲Set集合重寫了懶加載。

一個班級多個學生

Classes 類:(一方)外鍵關係建立方、維護方、使用方

int id;

String name;

Set students;

 

Student類:(多方)外鍵關係存放方

int id;

String name;

 

Set 集合能夠存聽任意類型的數據。(關聯屬性)

 

創建關係:

t_classes表在映射關聯屬性的時候,會建立外鍵,而且把外鍵設定在t_student表中,因此t_student表中有classesid字段。可是這個外鍵的關係由Classes維護。

 

Student.hbm.xml

<id name=」id」><generator class=」native」/></id>

<property name=」name」/>

 

Classes.hbm.xml

<id name=」id」><generator class=」native」/></id>

<property name=」name」/>

<set name=」students」>

       <key column=」classesid」/>

       <one-to-many class=」cn.hiberante.bean.Student」/>

</set>

一、         用set標籤映射關聯屬性,當前Classes對象中的students屬性

二、         建立Classes對象的時候在Set集合中存放 Student類型的數據

三、         建立外鍵字段 key column=」classesid」,加在<one-to-many>指定的class àStudent類所映射的t_student表中

四、         增長的字段classesid爲外鍵,經過 one-t-many:外鍵與當前表創建關聯,外鍵參照t_classes表

 

經過上面的映射:

t_student表中有三個字段, id,name,classesid;

t_classses表中有兩個字段:id,name

 

 

維護關係:(關係存儲,往外鍵裏面設值) native

1、建立多個學生對象(兩個或者兩個以上)

Student stu=new Student();

stu.setName(「tom」);

session.save(stu);//id 生成,name爲tom,外鍵classesid爲空,進入持久態,歸入session管理(外鍵能夠爲空) insert into t_student(name) values(?);

 

Student stu1=new Student();

stu.setName(「Jam」);

session.save(stu1);//id 生成,name爲Jam,外鍵classesid爲空,進入持久態,歸入session管理(外鍵能夠爲空)insert into t_student(name) values(?);

 

 

2、建立Set集合,將Student對象添加到集合中

Set students =new HashSet();

students.add(stu);

student.add(stu1);

3、建立Classes對象

Classes classes=new Classes();

classes.setName(「java1」);

classes.setStudents(students);//將建立的Set集合students設到Set集合類型屬性students

session.save(classes);//發送sql語句,id自動生成,把Classes的name屬性存在t_classes表中的name字段上。insert into t_classes(name) values(?);

關聯屬性的存儲:(外鍵設值)

一、         從students這個Set集合中拿到第一個元素stu,取得stu對象

二、         拿到stu對象主標識,(由於stu已經save過了,因此能取到該對象的主標識)

三、        經過stu對象的主標識找到數據庫中t_student表中對應的記錄

四、         存儲的時候拿到Classes對象的主標識id,(由於classes對象已經save)

五、        將Classes對象的主標識(id值)設定到stu對象對應的t_student表中的對應記錄的classesid字段上。

第二個元素stu1和第一個同樣;

 

session.beaginTransaction().commit();//提交後會發送update語句,有多少個學生對象會發送多少條update語句,update t_student set classesid=?where id=?;

 

加載關係:(將數據庫中查出來的記錄給對象賦值)

session.load(Classes.class,1);

classes.getName();//直接取出記錄中的name字段的值設置到name屬性中

classes.getStudents();

one-to-many,主找外:(一個對應多個)

一、         經過主鍵到對應的t_student表中找到外鍵,找到對應的第一條記錄

二、         建立Student對象,

三、         建立Set集合(有記錄時纔會建立)

四、         將Student對象add到Set集合中

五、         而後繼續經過主找外,依次找到全部的Student對象都分別add到Set集合中

六、         最後把Set集合設置到Classes對象的students屬性上。(students屬性爲Set類型)

One-to-many單向映射的缺點:

一、         若是t_student表中的classesid字段設置爲非空,就沒法保存數據

二、         由於不在Student這一端維護關係,全部Student不知道是哪一個班的

三、         須要發出多餘的update語句(數據庫負載大)

 

one-to-many 雙向關聯:

 

創建關係:

Student

int id;

String name;

Classes classes;

 

Student.hbm.xml文件:

<id class=」id」><generator class=」native」/></id>

<property name=」name」/>

<many-to-one name=」classes」 column=」classesid」>

讓外鍵重名,保證關係維護的一致性。

 

Classes 類:

 int id;

String name;

Set students;

 

Classes.hbm.xml文件:

<id class=」id」><generator class=」native」/></id>

<property name=」name」/>

<set name=」students」 inverse=」true」 cascade=」all」>

<key column=」classesid」/>

<one-to-many class=」cn.hiberante.bean.Student」/>

</set>

 

Key指定的字段名必須和<many-to-one name=」classes」  column=」classesid」>指定的同樣

外鍵的關係由一端維護,兩頭使用關係。

雙向關聯時,由多的一方存儲關係,維護關係。

<set name=」students」  inverse=」true」 cascade=」all」>

<key column=」classesid」/>

<one-to-many class=」cn.hiberante.bean.Student」/>

</set>

讓多的一方(Student)維護關係。添加inverse=」true」

 

維護關係:(關係存儲,往外鍵裏面設值)

1、建立多個學生對象(兩個或者兩個以上)

Student stu=new Student();

stu.setName(「tom」);

session.save(stu);//id 生成,name爲tom,外鍵classesid爲空,進入持久態,歸入session管理(外鍵能夠爲空)Student能夠維護關係可是不維護關係。Classes不設值就爲null. insert into t_student(name,classesid) values(?,?);

 

Student stu1=new Student();

stu.setName(「Jam」);

session.save(stu1);//id 生成,name爲Jam,外鍵classesid爲空,進入持久態,歸入session管理(外鍵能夠爲空)Student能夠維護關係可是不維護關係。Classes不設值就爲null. insert into t_student(name,classesid) values(?,?);

 

2、建立Set集合,將Student對象添加到集合中

Set students =new HashSet();

students.add(stu);

student.add(stu1);

3、建立Classes對象

Classes classes=new Classes();

classes.setName(「java1」);

classes.setStudents(students);//將建立的Set集合students設到Set集合類型屬性students

session.save(classes);//發送sql語句,id自動生成,把Classes的name屬性存在t_classes表中的name字段上。insert into t_classes(name) values(?);

這時候Classes想維護關係,可是看到inverse=」true」,就沒法維護關係,Student能夠維護關係可是不維護。建立的表 t_student 中的 classesid列爲null.

Inverse的做用:(值爲true/false

一、         指定誰反轉另外一方來維護關係

二、         主要使用在set集合標籤上

三、         主要用於存儲關係

 

正確流程:

先建立classes對象,只給name屬性設值;(id,set集合都不設值)

再建立Student對象,每一個值都設值(id,name,classes)

再建立Set集合將Student對象add到set集合中

session.save(classes);//insert into t_classses(name) values(?);

 

inverse=true:

設定由另外一端往外鍵中設值。(對方來維護關係)

用在Set標籤上

用在存儲上(加載用不着inverse)

一般inverse後面會加 cascade 級聯屬性

cascade=all

session.save(classes)當Classes對象準備維護關係的時候,inverse屬性爲true,讓Student屬性維護關係.因此就自動發送session.save(stu);就建立Student對象的 id主標識,往外鍵設值(Student對象的classid屬性)à拿到classes所指的Classes對象的主標識設到classesid上。

優勢:沒有發送多餘的sql語句(update

 

Inverse和cascade

一、         都是進行存儲的時候使用

二、         inverse à翻轉,指定由對方來存儲關係

三、         cascade在存儲數據之前,若是須要存儲另外一種數據,就會自動的發送save語句,先存儲另外一種數據再save當前對象。

四、         inverse:是關聯關係的控制方向,用在set標籤上

五、         cascade操做上的連鎖反應,用在 one-to-one,many-to-one,one-to-many標籤上

 

 

週末任務:

一、         整理出Hibernate案例,把筆記分析都添加到對應的案例上

二、         Many-to-one *

三、         One-to-one *

四、         One-to-many *

五、         Many-to-many

六、         單繼承映射關係

七、         看Oracle視頻,學會Oracle數據庫的基本操做 建表以及增刪改查

八、         複習一下struts1.0,儘可能完善本身的struts框架

九、         找到Hibernate源碼,看看源碼,加強本身對映射關係的理解

 

Many-to-many:(多對多)

多對多---單向關聯:

示例:一我的能夠有多個角色,一個角色能夠由多我的擔當

從關係模型中體現多對多關係:建立中間表

中間表的特色:

至少有兩個外鍵,外鍵分別參照兩張表的主鍵

這兩個外鍵共同生成主鍵à聯合主鍵

兩個外鍵的值都由一個對象的關聯屬性設定,關聯屬性爲Set集合類型。

 

創建關係:

Role:(類)

int id;

String  name;

Role.hbm.xml

<id name=」id」>

<generator class=」native」/></id>

<property name=」name」>

 

User (類)

int id;

String  name;

Set roles

 

Roles屬性映射指定:

一、         Set裏面放什麼類型的數據

二、         建立第三張表 t_user_role

三、         往第三張表中添加兩個字段(兩個外鍵分別參照兩張表)

 

User.hbm.xml

<id name=」id」>

<generator class=」native」/></id>

<property name=」name」>

<set name=」roles」 table=」t_user_role」>

<key column=」userid」/>

<many-to-many class=」cn.hibernate.Role」 column=」roleid」>

</set>

一、         指定roles的Set集合中存放cn.hibernate.Role類型的數據

二、         Many-to-many, 須要建立中間表,映射時roles屬性時,建立t_user_role表

三、        <key column=」userid」/>指定外鍵字段userid,把這個字段添加到t_user_role表中

四、         userid這個外鍵字段 參照當前對象所對應的表t_user表

五、         在many-to-many 後面的column=」roleid」 指定外鍵字段 roleid, 把這個字段添加到t_user_role表中

六、         roleid這個外鍵字段參照class指定的Role所映射的t_role表

七、         userid和roleid爲 t_user_role表的聯合主鍵,分別參照t_user和t_role表

 

存儲關係:

先建立Role對象:

Role role1=new Role();

role1.setName(「zs」);

session.save(role1);//insert   into t_tole(name) values (?);

Role role2=new Role();

role2.setName(「ls」);

session.save(role2); //insert   into t_tole(name) values (?);

 

再建立User對象

User  user=new User();

user.setName(「wife」);

 

建立Set集合,將Role對象添加到set集合中

Set set=new HashSet();

set.add(role1);

set.add(role2);

 

user.setRoles(set);//把存儲Role對象的Set集合設定到roles屬性中

session.save(user);//發送三條sql語句,在t_user_role表中建立兩條記錄

//insert   into t_user (name) values (?);

//insert   into t_user_role(userid,roleid) values (?,?);

//insert   into t_user_role(userid,roleid) values (?,?);

 

在當前對象Set集合中有幾個元素,就會在t_user_role表中建立幾條記錄

 

id自動生成,name字段直接映射

存儲User對象的關聯屬性roles:

從roles的Set集合中拿出第一個元素role1,拿到role1對象的主標識,

將role1對象的(主標識)id,設到t_user_role的roleid字段上

拿到當前User對象的id, 設到t_user_role的userid字段上

 

從roles的Set集合中拿出第二個元素role2,拿到role2對象的主標識,

將role2對象的id主標識,設到t_user_role的roleid字段上

拿到當前User對象的id, 設到t_user_role的userid字段上

 

加載關係:

關聯屬性的加載,給關聯屬性roles賦值:

一、         many-to-many:分紅一對多(one-to-many),和多對一(many-to-one);

二、         one-to-many(主找外),經過t_user表中 id 找找t_user_role表中對應的userid

三、         經過userid自動找到 roleid字段

四、         many-to-one(外找主),經過roleid字段找到t_tole表中的對應的id指定的行生成記錄

五、         根據記錄生成Role對象,設到Set集合中

六、         繼續重複2-5(直到找完t_user表中指定id對應的t_user_role表中的全部記錄)

七、         將賦完值後的Set集合設定到roles屬性中

 

 

多對多---雙向關聯:

 

創建關係:

Role:(類)

int id;

String  name;

Set users

Role.hbm.xml

<id name=」id」>

<generator class=」native」/></id>

<property name=」name」>

<set name=」users」 table=」t_user_role」>

<key column=」roleid」/>

<many-to-many class=」cn.hibernate.User」 column=」userid」>

</set>

 

User (類)

int id;

String  name;

Set roles

 

Roles屬性映射指定:

四、         Set裏面放什麼類型的數據

五、         建立第三張表 t_user_role

六、         往第三張表中添加兩個字段(兩個外鍵分別參照兩張表)

 

User.hbm.xml

<id name=」id」>

<generator class=」native」/></id>

<property name=」name」>

<set name=」roles」 table=」t_user_role」>

<key column=」userid」/>

<many-to-many class=」cn.hibernate.Role」 column=」roleid」>

</set>

 

<set name=」」 table=」t_user_role」>//這裏table指定的表必須同樣

兩張表均可以維護關係,

User維護關係,set集合中存放Role類型的數據,

key column=」userid」,參照 t_user表

<many-to-many class=」cn.hibernate.Role」 column=」roleid」>

roleid參照 class指定的類Role對應的t_role表

 

Role維護關係,set集合中存放User類型的數據,

key column=」roleid」,參照 t_role表

<many-to-many class=」cn.hibernate.User」 column=」userid」>

user參照 class指定的類User對應的t_user表

 

建立關係能夠雙方建立,

維護關係只能一方維護

使用關係能夠雙方使用

 

存儲關係:

存儲的時候,只能由一方維護,只讓一方給關聯屬性設值,設值的一方就是在維護關係。

 

加載關係:

和單向關聯相似

關聯屬性的加載,給關聯屬性roles賦值:

一、         many-to-many:分紅一對多(one-to-many),和多對一(many-to-one);

二、 one-to-many(主找外),經過t_user表中 id 找找t_user_role表中對應的userid

三、         經過userid自動找到 roleid字段

四、         many-to-one(外找主),經過roleid字段找到t_tole表中的對應的id指定的行生成記錄

五、         根據記錄生成Role對象,設到Set集合中

六、         繼續重複2-5(直到找完t_user表中指定id對應的t_user_role表中的全部記錄)

七、         將賦值後的Set集合設定到roles屬性中

 

 

單表繼承映射

創建關係:

一個父類,兩個子類

一個hbm.xml文件

Hibernate默認 lazy=」true」

Extends.hbm.xml文件的配置:

<!— Animal爲父類,具備id,name,sex三個基本屬性-->

<class name=」Animal」 table=」t_animal」 lazy=」true」>

<id name=」id」><generator class=」native」/></id>

 

<!--注意元素的順序:discriminator要在property的上面-->

<!—discriminator :爲鑑別器,指定type爲鑑別器字段(not-null=true)。string是Hibernate定義的String類型。在t_animal中增長type字段。類型爲string,在數據庫中會轉換爲varchar類型。自動把 discriminator-value的值自動設定到該字段上。

-->

<discriminator column=」type」  type=」string」/>

<property name=」name」/>

<property name=」sex」/>

 

<!—Pig繼承了Animal,爲子類,Pig子類有新增的weight屬性

discriminator-value:指定鑑別值,設到鑑別器字段中

-->

<subclass name=」Pig」 discriminator-value=」P」>

<property name=」weight」/>

</subclass>

 

<!—Bird繼承了Animal,爲子類,Bird子類有新增的height屬性

discriminator-value:指定鑑別值,設到鑑別器字段中

-->

<subclass name=」Bird」 discriminator-value=」B」>

<property name=」height」/>

</subclass>

</class>

 

Discriminator(鑑別器)的做用:加載時,自動識別記錄生成對應的對象類型。

 

存儲關係:(識別子類類型,自動往鑑別器字段加鑑別值)

Pig pig=new Pig();

pig.setName(「xiaohua」);

pig.setSex(true);

pig.setWeight(100);

session.save(pig);//id自動生成

一、經過Pig這個類,找到映射文件中的子類subclass name=」Pig」

二、經過subclass子類找到父類class=」Animal」,

三、根據class=」Animal」找到對應的表 t_animal,

四、往Pig對象設定到t_animal表中id指定的記錄上的的各個字段。沒有height屬性的就設爲null,

五、type字段的設值:看到是subclass,就往type鑑別器字段自動設 discriminator-value指定的值。(這個值Hibernate根據配置信息自動設定)

 

 

加載關係:

 

加載子類對象:

session.load(Pig.class,1);// Sql:select * form  t_animal where id=1 and type=’P’.

經過*.hbm.xml文件中找,經過Pig找到t_animal表

到表中找到id=1對應的記錄

在查找的時候會自動添加查找條件 type=’P’.

生成的記錄對應的對象爲Pig類型

 

加載父類對象:

Animal animal =(Animal)session.get(Animal.class,1);

if(animal instanceof Pig) ==true

到t_animal表中查出id=1的記錄,拿到全部字段,

發現type=」P」,知道應該建立Pig類型的對象。

 

load方法查詢不支持多態查詢,get方法查詢支持多態查詢。

Load支持懶加載,不支持多態查詢:由於load拿到的是cglib代理對象,代理類不是真正的實例 沒法用instenceof判斷類的歸屬

Query支持多態查詢,

session.createQuery(form Animal);

拿出全部的記錄而且生成對應子類對象。(指定類型)

 

多態查詢:單表查出數據,Hibernate能夠自動鑑別類的數據類型(指定生成對象類型)。

單表繼承惟一缺點是:多了冗餘字段。

若是關閉懶加載,load就支持多態查詢。(直接發送sql語句)lazy=false,默認狀況下lazy爲true

 

多表繼承映射:

繼承關係:每個具體的類映射成一張表

Animal爲父類,Pig爲子類。

創建關係:(創建三張表,t_animal,父類對應的表中有三個字段,t_pig,t_bird子類對應的表中只有兩個字段,主類的主鍵也爲外鍵,參照父類的主鍵。對應子類和父類主鍵保持一致)

映射文件:

<class name=」Animal」 table=」t-animal」>

<id name=」id」>

<generator class=」native」/>

</id>

<property name=」name」/>

<property name=」sex」/>

 

<joined-subclass name=」Pig」 table=」t_pig」>

<!—pid是當前表t_pig的主鍵,也是外鍵參照父類的t_animal表。-- >

<key column=」pid」/>

<property name=」weight」/>

</joined-subclass>

</class>

 

存儲關係:

Pig pig=new Pig();

pig.setName(「hua」);

pig.setSex(「male」);

pig.setWeight(100);

session.save(pig);

一、經過子類Pig找到父類Animal,再找到Animal對應的表t_animal

二、先往父類的表中存數據,t_animal(id,name,sex)

三、再往子類的表中存數據,將t_animal的id設到t_pig表中的pid字段。t_pig(weight,pid)

<從t_animal中拿到主鍵做爲當前t_pig表的主鍵(由於pid即便主鍵也是外鍵)>

 

加載關係:

Pig pig=(Pig)session.load(Pig.class,1);

一、先從t_pig中找對應記錄,拿到該記錄的主鍵(先找子表),找到weight字段,設到weight屬性中。

二、再經過外找主,找到t_animal中對應的記錄,將查到的記錄設到pig對象中。Sex,name.

 

優勢:結構清晰,一個類一張表

缺點:查找效率低,(須要多個表關聯查詢)

 

繼承映射:每一個子類一張表,父類沒有對應的表

缺點:主鍵生成策略不能爲native,只能爲assigned

Animal爲父類,Pig爲子類。

創建關係:

<!-- abstract=」true」:表示父類不生成表,映射的屬性字段被子類繼承,讓子類應用-- >

<class name=」Animal」 abstract=」true」>

<id name=」id」>

<!—這裏只能爲assigned-- >

<generator class=」assigned」/>

</id>

<property name=」name」/>

<property name=」sex」/>

 

<union-subclass name=」Pig」 table=」t_pig」>

<property name=」weight」/>

</union-subclass>

</class>

只有t_pig表,子類的映射的表不只有本身的屬性對應的字段,還會有父類的屬性對應的字段。(weight+name,sex)

 

存儲關係:

Pig pig=new Pig();

pig.setId(2);

pig.setName(「hua」);

pig.setSex(「male」);

pig.setWeight(100);

session.save(pig);

/*這裏不會發送sql語句,由於主鍵生成策略爲assigned,發sql語句是爲了拿到主鍵。這裏的主鍵已經本身手動指定,全部不須要發送sql語句。

Pig對象已經歸入session管理,id爲2.。*/

若是再建立一個Bird,

Bird bird=new Bird();

bird.setId(2);

session.save(bird);//這裏就會拋出異常,由於id已經在session中存在。

同一個類的不一樣對象,一旦歸入session管理,id必須不一樣。

同一個類的不一樣子類對象,id也必須不一樣。不然會出異常。

 

 

關於主鍵生成策略:

父類的屬性會被子類繼承,映射在子類的字段中,

父類的主鍵生成策略會被子類繼承。

若是爲native或者是uuid,有數據庫或者hibernate自動生成,只生成一次。

session.save()的時候,會爲父類對象生成主標識

同一個id會被父類的全部子類都繼承做爲子類的主標識id,就會形成多個子類對象引用相同的主標識,就會拋出異常。同一個類的不一樣子類對象,id必須不一樣。

 

單表繼承:

優勢:效率高

缺點是數據冗餘

 

一個類映射一張表:

優勢:結構清晰,一個類一張表

缺點:查找效率低,(須要多個表關聯查詢)

 

一個子類映射一張表:

優勢:結構清晰,效率高

缺點:主鍵不能自動生成,必須制定爲assigned(不符合hibernate

建議使用第一種,單表繼承.

 

物料項目:

持久層:繼承映射和關聯映射同時使用

用戶需求: 主鍵生成策略爲assigned

鑑別器字段 discriminator column=」category」 type=」string」

 

存儲關係:(繼承關係的表中的數據的存儲)

經過子類對象找到子類所在的映射文件,拿到鑑別器值 discriminator-value=pig」

在映射文件中,經過子類找到父類,經過父類找到對應的表 t_data_dict

往父類的表(t_data_dict)中設值。

 

加載關係:(繼承關係的表中的數據的加載)

session.createQuery(from ItemCategory);

至關於sql語句:select *from t_data_dict where category=itemCategory;

由於查詢的是子類對象,多態查詢,會自動加上鑑別器的值category=itemCategory。

將查詢出來的記錄封裝到Query對象中,再經過.list()方法,將Query對象中的記錄存到list集合中。

 

 

關聯映射:(關於關聯屬性的映射- Item對象)

Item:

Private ItemCategory category;

Category爲關聯屬性,經過配置hbm.xml文件

<many-to-one name=」category」>

代表category爲外鍵,參照的是category屬性所屬的類ItemCategory所映射的表,可是因爲ItemCategory爲子類而且在單表繼承映射中,全部參照的是ItemCategory的父類所映射的表t_data_dict。

 

存儲關係:(關聯映射--存Item對象)

在Action中:

一、         從表單中拿到表單中的參數(普通屬性以及關聯屬性的值)

二、         建立Item對象,將普通屬性設到Item對象中(Item處於瞬時態)

三、         建立ItemCategory對象,ItemCategory category=new ItemCategory();(從數據庫中取出來的數據是離線態,在數據庫中有對應的記錄)

四、         category.setId (categoryId);// categoryId爲從表單中取出來的數據

五、         item.setItemCategory(category);//給關聯屬性賦值

六、         建立ItemUnit對象,ItemUnit unit=new ItemUnit();//離線態

七、         unit.setId(unitId);//unitId爲從表單中取出來的數據

八、         item.setUnitItem(unit);// //給關聯屬性設置

 

持久態對象能夠引入離線態對象,不會有異常,由於離線態對象在數據庫中有對應的id.

 

加載(關聯映射—給關聯屬性賦值):

session.load(Item.class,itemNo);

一、         找到hbm文件

二、         找到對應的表 t_items

三、         根據itemNo找到對應的記錄

四、         將對應的記錄設到Item對象中

五、         給關聯屬性ItemCategory賦值,many-to-one,外找主,找到t_data_dict中對應的記錄

六、         根據記錄生成對象,將對象賦值給Item對象中的category屬性

 

懶加載(Lazy)

懶加載概念:只有真正使用該對象時,纔會建立對象;對於Hibernate而言,真正使用對象的時候纔會發送sql語句查詢數據庫。

懶加載--lazy主要用於加載數據。

lazy使用的標籤:

<class>:

<peroperty>:

<set><list>:

<one-to-one><one-to-many>:

懶加載的實現原理:產生cglib代理

一、         生成目標類的子類(cglib代理類)

二、         一旦須要使用對象的方法時,判斷target是否等於null(target爲代理對象中的一個狀態標識,默認爲null,對象建立後就會等於對象的類型 target=Group)

三、         若是target不等於null就直接使用

四、         若是target等於null 就發sql語句查詢數據庫根據記錄生成對象再使用。

懶加載的分類:

一、類上的懶加載<class>::true/false,默認爲true,開啓懶加載

二、集合上的懶加載<set>:: true/false/extra 默認爲true

三、單端關聯懶加載<one-to-one><one-to-many>::

:false/proxy/noproxy,默認爲proxy

 

類上的懶加載:(默認開啓lazy=」true」

<class name=」Group」 table=」t_group」 lazy=」true」>

Group gourp=session.load(Group.class,1);//執行這條語句時不會立刻發送sql語句

建立的Group對象是cglib代理對象,target=null;

group.getId();//取得id的時候也不會發送sql語句,由於上面查詢條件中已經給出id的值。group依舊是cglib代理對象,target=null

group.getName();//這時候group仍是cglib代理對象。而後發送sql語句。

cglib代理對象是目標對象的子類,會繼承目標對象的方法,會先判斷目標對象是否存在(target==null)。若是存在就會調用目標對象的方法;若是不存在就會發送sql語句,從數據庫中查詢記錄,根據記錄建立目標對象,執行目標對象的getName()方法。target=Group。

再執行一次 group.getName();這裏仍是代理對象,目標對象是由cglib代理對象調用。會判斷target==null,發現不等於null,就直接調用目標對象的getName()方法。

 

加載能夠不須要事務,事務只是用在存儲。

Hibernate支持三種事務:局部事務,全局事務,代理事務

 

懶加載異常:

懶加載開啓,建立的是代理對象,先不使用代理對象(就不會發送sql語句建立目標對象),執行commit()方法後,執行session.close()方法把session關閉。而後再使用代理對象,須要發送sql語句建立目標對象時,發現鏈接已經關閉,就會拋出異常LazyInitializationException:懶加載初始化異常。

Session是線程不安全的,因此用完了要立刻關閉。

 

解決方案:

一、         關閉懶加載:可是效率會大大下降

二、         使用ThreadLocal

 

懶加載的實際應用:物料項目

一、load()方法在lazy=true的狀況下查詢出來的對象都是代理對象,在持久層是不會使用對象的

二、將代理對象下傳到業務層(struts的Action類中)

三、在Action類中經過request.setAttribute(「item」,item);傳到jsp頁面

四、在jsp頁面經過EL表達式輸出。經過item屬性名拿到對應的屬性值 item代理對象。

五、發現要使用的時候就會發送sql語句建立目標對象,可是session已經關閉因此會拋出異常。

 

正確處理方法:

在持久層不關閉session,使用Filter來管理session的建立和關閉

Public class HibernateFileter implements Filter{}

ThreadLocal hibernateHolder=new ThreadLocal();

一、加載類建立工廠類型的靜態變量(全局)

二、Tomcat一啓動就建立過濾器對象,一旦建立過濾器對象就會執行該對象的init()方法

三、init()方法中,讀取Hibernate.hbm.xml文件,建立SessionFactory對象。

四、把工廠對象賦值給工廠類型的靜態全局變量

五、在HibernateFilter類中,建立getSession靜態方法,在此方法中獲得session

public static Session getSession(){

Session session=(Session)hibernateHolder.get();//取得當前線程綁定的session

if(session==null){

session=factory.openSession();

hibernateHolder.set(session);//把session和當前線程綁定

}

return session.

}

 

六、持久層經過HibernateFilte類中的的靜態方法 getSession()拿到session。

七、在實現Filter接口的類中,doFilter方法中:chain.doFilter()執行後對結果進行攔截,關閉session。先判斷當前線程是否已經綁定session,

Session session=(Session)hibernateHolder.get();//取得當前線程綁定的session

若是綁定了,取出的session不爲null,就執行判斷session.isOpen(),若是爲true就繼續執行session.close()方法關閉session,而後再將session從map集合中移除(解除當前線程與session的綁定關係)

try {

chain.doFilter(servletRequest, servletResponse);

} finally {

Session session = (Session)hibernateHolder.get();//從Map中取出session

if (session != null) {

if (session.isOpen()) {

session.close();//關閉session

}

hibernateHolder.remove();//移除session

}

}

 

ThreadLocal類的分析:

原理:在ThreadLocal類中有一個線程安全的Map,用於存儲每個線程的變量的副本。

public class ThreadLocal{

private Map values = Collections.synchronizedMap(new HashMap());;//線程安全

}

ThreadLocal已經支持泛型,該類的類名已經變爲ThreadLocal<T>

Thread thraed=Thread.currentThread();//獲得當前線程對象

ThreadLocal:是線程作併發用的技術,主要方法get(),set(T),initialValue(),remove()

 

1set(T):--設置到當前線程的局部變量

將當前線程和給session作綁定,線程做爲key,T(session)做爲value存入一個加鎖的線程安全的map集合中。values.put(thread,session);

public void set(Object newValue) {

values.put(Thread.currentThread(), newValue);

}

2T get():返回當前線程的局部變量

從線程安全的map中集合中取出線程對應的session,若是有就直接用,若是沒有就建立session,再綁定到當前線程上  values.put(thread,session);

public Object get() {

Thread curThread = Thread.currentThread();

Object  obj=values.get(currentThread);//先從map集合中取

if (obj == null && !values.containsKey(curThread)){//發現map集合中沒有當前線程做爲key對應的value,就建立再添加到map

 obj = initialValue();

values.put(curThread, obj);

}

return obj;

}

 

3T initialValue():---返回當前線程的局部變量的初始值

protected Object initialValue(){

 return null;

}

4remove():--移除當前線程的局部變量

map.remove(key);//根據map集合的移除元素的原則,根據key移除,

public void remove() {

values.remove(Thread.currentThread());

}

 

爲何必定要用線程安全的map?

多個線程併發的拿到session併發的訪問數據庫會形成數據混亂。線程安全的map,使得一次只能有一個線程訪問數據庫。並行變串行

資源共享:

對於多線程資源共享的問題,同步機制採用了以時間換空間」的方式,而ThreadLocal採用了「以空間換時間」的方式。前者僅提供一份變量,讓不一樣的線程排隊訪問,而ThreadLocal爲每個線程都提供了一份變量,所以能夠同時訪問而互不影響。

Hibernate和struts的集成,使用攔截器(Filter)繼承。

 

OpenSessionInview模式:一直開啓session,直到數據輸出完再關閉。

 

集合上的懶加載:(extra,true,false)

在<set>標籤上使用,使用set標籤的主要有<one-to-many><many-to-many>

<class>標籤上的懶加載,不影響<set>上的懶加載。

普通屬性的加載是使用類上的懶加載

關聯屬性是使用集合上的懶加載

1、集合上的懶加載開啓lazy=true,類上的懶加載是開啓的 lazy=true

session.load(Classes.class,1);//生成的是代理對象,target=null,不會發送sql語句

classes.getName();//只發一條sql語句,訪問t_classes,只給普通屬性設值

Set students=classes.getStudents();//不會發送sql語句訪問t_student表,說明當前students是PersistantSet(相似cglib)代理對象,不是真正存放Student對象的Set集合。

Iterator it=students.iterator();

While(it.hasNext()){

Student stu=it.next();

stu.getName();//真正使用到Set集合中的元素時纔會發送sql語句訪問t_student

}

 

2、集合上的懶加載關閉lazy=false,類上的懶加載是開啓的 lazy=true

加載Set集合類的students屬性和加載普通屬性同樣。

session.load(Classes.class,1);//生成的是代理對象,target=null,不會發送sql語句

classes.getName();//發送兩條sql語句,訪問t_classes,t_student表(後面都不發送sql語句了)

 

3、集合上的懶加載關閉lazy=false,類上的懶加載關閉lazy=false

session.load(Classes.class,1);//生成的是目標對象,會發送兩條sql語句,分別訪問t_classes,t_student表

 

4、集合上的懶加載開啓lazy=true,類上的懶加載關閉lazy=false

session.load(Classes.class,1);//生成的是目標對象,會發送一條sql語句,訪問t_classes表

Set students=classes.getStudents();//不會發送sql語句訪問t_student表,說明當前students是PersistantSet(相似cglib)代理對象,不是真正存放Student對象的Set集合。

Iterator it=students.iterator();

While(it.hasNext()){

Student stu=it.next();

stu.getName();//真正使用到Set集合中的元素時纔會發送sql語句訪問t_student

}

 

5、集合上的懶加載lazy=extra,類上的懶加載關閉lazy=true

session.load(Classes.class,1);//生成的是代理對象,不會發送sql語句,

classes.getName();//會發一條sql語句,訪問t_classes表

Set students=classes.getStudents();//不會發送sql語句訪問t_student表,說明當前students是PersistantSet(相似cglib)代理對象,不是真正存放Student對象的Set集合。

students.size();//這時候會使用函數從數據庫中查詢數據,select count(*)form t_student where classesid=1;,若是集合上的lazy爲true,就會先發送select *from student where classesid=1語句,查詢數據庫中的全部記錄,再計算出記錄的條數。

Iterator it=students.iterator();

While(it.hasNext()){

Student stu=it.next();

stu.getName();//真正使用到Set集合中的元素時纔會發送sql語句訪問t_student

}

 

lazy=true和lazy=extra的功能差很少,可是extra更加智能,它會根據用戶的的條件發送比較智能的sql語句。

 

單端上的懶加載:(proxy,false,no-proxy

般用在<many-to-one><one-to-one>標籤上面

默認爲lazy=」proxy」,開啓狀態

<many-to-one name=」group」  lazy=」proxy」>

主要是在加載group(關聯)屬性的時候有用,

1、單端上的懶加載開啓lazy=proxy,類上的懶加載是開啓的 lazy=true

session.load(User.class,1);//生成的是代理對象,target=null,不會發送sql語句

user.getName();//發送sql語句,訪問t_user表

Group group=user.getGroup();//拿到的而是Group的代理對象

Group.getName();發送sql語句訪問t_group.

 

2、單端上的懶加載關閉lazy=false,類上的懶加載關閉 lazy=false

session.load(User.class,1);//發送兩條sql語句,訪問t_user和t_group

user.getName();

Group group=user.getGroup();

Group.getName();

 

3、單端上的懶加載開啓lazy=proxy,類上的懶加載關閉lazy=false

session.load(User.class,1);//發送一條sql語句,只訪問t_user

user.getName();

Group group=user.getGroup();//不會發送sql語句,group爲代理對象

Group.getName();//發送sql語句訪問t_group

 

4、單端上的懶加載開啓lazy=no-proxy,類上的懶加載關閉lazy=false

session.load(User.class,1);//發送一條sql語句,只訪問t_user

user.getName();

Group group=user.getGroup();//不會發送sql語句,group爲代理對象

Group.getName();//發送sql語句訪問t_group

 

Lazy的取值爲proxy和no-proxy功能和性能上都沒有很大的區別:

proxy:作懶加載是使用cglib代理,no-proxy使用的是加強的字節碼工具,在字節碼生成的時候增長了懶加載策略。

 

今天的任務:

將老師講的案例本身寫一遍(懶加載) *

晚上投簡歷 *

作>=1道算法題 *

查看hibernate源碼

 

Session_Flush

Session_flush的做用:

一、         清理緩存(清理session臨時集合中的數據)

二、         生成sql語句(發送sql)

三、         將session的persistanceContext中的map->existsInDatabase的標記置爲true。

Flush:用在存儲上。

 

主鍵生成策略爲uuid(session.flush()的特色)

session.save(user)前—>> 對象處於瞬時態

執行save(user)方法後,通常不會發送sql語句,由於主鍵是由hibernate自動生成。

session.save(user)後-->> 對象處於持久態。existsInDatabase=false

session.flush();//主要作了如下三件事:

一、         清理緩存(清理session臨時集合中的數據)

二、         生成sql語句(發送sql)

三、         將session的persistanceContext中的map->existsInDatabase的標記置爲true

數據庫中會有相應記錄,可是會看不到。(由於是 髒數據:沒有提交的數據)

session.commit();

一、         執行commit()方法的時候會執行flush()方法,因此能夠不顯示的寫出flush()方法。

二、        就算是顯示寫了flush()方法,commit()的時候仍是會再執行一次flush()

三、        執行flush()方法的步驟:

3.一、   先判斷臨時集合中有沒有數據

3.二、   若是沒有的話就直接commit().

3.三、   若是臨時集合中有數據的話就發送sql語句再清空臨時集合中的數據,而且將existsInDatabase標記置爲true,而後再執行commit()

 

主鍵生成策略爲native(session.flush()的特色)

session.save(user)前—>> 對象處於瞬時態

執行save(user)方法後,通常會發送sql語句,由於主鍵是由數據庫自動生成。

session.save(user)後-->> 對象處於持久態。existsInDatabase=true

session.flush();//這時候flush()什麼都不作,由於臨時集合中沒有數據,sql語句已經發送,existsInDatabase=true

session.commit();//會再次執行flush()方法,依舊什麼都不作。commit()方法對提交到數據庫的數據作驗證,成功就添加到數據庫,不成功就回滾。

 

主鍵生成策略爲uuid(session.evict()的特色)

session.save(user)前—>> 對象處於瞬時態

執行save(user)方法後,一 般不會發送sql語句,由於主鍵是由hibernate自動生成。

session.save(user)後-->> 對象處於持久態臨時集合中有數據existsInDatabase=false

session.evict(user);//將user對象從session-- >> persistanceContext下的map中逐出。臨時集合中依舊有數據.

session.commit();//拋出異常,(persistanceContext下的map)EntityEntries屬性中的table中的數據不存在,找不到數據,沒法提交數據。(緩存使用不當)

產生異常的緣由:

主鍵生成策略爲uuid

session.save(user);//在臨時集合中有數據,session的map中有數據,existsInDatabase=true,loadedState中存放了user對象

session.evict(user);// 把user從map中逐出(session緩存),爲了管理緩存

而後 臨時集合中有數據,可是map中沒有數據

 

執行commit()方法前會先調用session.flush(),作如下三步操做:

  1. 從臨時集合中拿到數據,清空臨時數據
  2. 發送sql語句
  3. 3.   將map下的existsInDatabase的標記置爲true,可是user的map已經被清除,找不到map也找不到map裏面的existsInDatabase標記,就會拋出異常。

 

解決辦法:(調用session.evict(user)之前先顯示調用session.flush())

session.save(user);

顯示的寫flush方法

session.flush();//根據臨時集合中的數據發送sql語句,而後清空臨時集合,到session緩存中將existsInDatabase標記置爲true

session.evict(user);//將user對象從session緩存的map集合中移除

最後session.commit();//這時候依舊會執行session.flush()方法,可是不會拋出異常,由於flush()方法,是先查看臨時集合中有沒有數據,發現臨時集合是空的沒有數據就不會發送sql語句,也不用再次清空臨時集合,也不須要將existsInDatabase標記置爲true;直接執行commit()方法。

 

主鍵生成策略爲native(session.evict()方法的特色)

session.save(user)前—>> 對象處於瞬時態。

執行save(user)方法後,通常會發送sql語句,由於主鍵是由數據庫自動生成。

session.save(user)後-->> 對象處於持久態。臨時集合中沒有數據。existsInDatabase=true

session.evict(user);//自動把數據從session緩存的map中逐出

session.commit();//執行flush()方法,可是因爲主鍵生成策略爲native,臨時集合中已經沒有數據了,就不要用發送sql語句也不用將existsInDatabase標記置爲true了。直接執行commit()方法。 commit()方法對提交到數據庫的數據作驗證,成功就添加到數據庫,不成功就回滾。這裏不會拋出異常,由於這裏的flush()方法什麼都不用作。

 

主鍵生成策略爲assigned(session.evict()的特色)

session.save(user)前—>> 對象處於瞬時態。

執行save(user)方法後,通常不會發送sql語句,由於主鍵是由用戶手動生成。

session.save(user)後-->> 對象處於持久態。existsInDatabase=false,臨時集合中有數據。

session.evict(user);//自動把數據從緩存中逐出

session.commit();//無論assinged知道的主標識類型是int仍是String,執行session.evict(user)後執行session.commit()必定會拋出異常

解決辦法,在調用sessionn.evict(user)方法前顯示的調用session.flush()

 

Sql語句在數據庫中的執行順序:

發送sql語句的順序:

建立兩個對象 user,user1;

session.save(user);

user.setName(「ls」);

session.save(user1);

session.commit();

先發送insert into  user () 存儲的對象是user

再發送 insert into user() 存儲的對象是user1

再發送 update user set name=ls 更新的對象是 user

 

可使用flush()方法調整語句的發送順序

建立兩個對象 user,user1;

session.save(user);

user.setName(「ls」);

session.flush();

session.save(user1);

session.commit();

先發送insert into  user ()存儲的對象是 user

再發送 update user set name=ls user(由於flush()方法)

再發送 insert into user()存儲的對象是 user1

 

悲觀鎖和樂觀鎖:

一、         爲何要加鎖?

在數據庫中不加鎖會丟失更新

緣由:用戶同時訪問數據庫,查看的數據相同,一方對數據作了修改可是另外一方不知道,因此就在原來的數據上修改。就數據不同。

 

不能併發的訪問數據庫中的數據,否則數據庫會崩潰的哦

保證數據庫的安全,並行變串行

 

悲觀鎖:(悲觀鎖不支持懶加載)

測試示例:

兩個方法串行執行:(load1(),load2())money=1000

一、load1();---- session.load(Test.class,1);

二、Debugà 查完數據後修改數據庫中的值 money-200可是 不執行commit()方法

三、執行另外一個查詢方法load2()-----ssession.load(Test.class,1);

四、修改數據 money-200而且一次執行完.

五、再接着執行第一個方法,commit()。更新後數據庫中的數據就是錯誤的。最後結果爲money=800。更新丟失

 

session.load(Test.class,1,LockMode.UPGRADE);//悲觀鎖,使用數據庫自身的鎖

LockMode.UPGRADE//利用數據庫的for  update 子句加鎖—---記錄鎖(會出現幻讀)

鎖的分類:記錄鎖,表鎖,庫鎖

記錄鎖:只給當前查詢出來的記錄加鎖,其餘記錄依舊沒有變化

 

加悲觀鎖後的示例:(悲觀鎖中懶加載自動失效,load方法執行後就會發送sql語句)

兩個方法串行執行:(load1(),load2())money=1000

一、load1();---- session.load(Test.class,1,LockMode.UPGRADE);//立刻發sql語句查詢

二、使用debug調試à 查看記錄money=1000,而後修改數據庫中該記錄的的money字段, ,money=money-200可是 不執行commit()方法

三、執行另外一個查詢方法load2()-----ssession.load(Test.class,1,LockMode.UPGRADE); //發送sql語句查詢的時候發現查不出來數據。id=1的記錄已經被上了鎖。

四、修改數據 money值,money=money-200,執行commit()方法,會停留在記錄外面,查詢不出來記錄,由於前面已經上了鎖,要等前面那個鎖釋放才能夠訪問那條記錄。

五、再接着執行第一個方法,commit()。更新後數據庫中的數據money=800;

六、這時候load2()方法執行,查詢出來的數據money=800,以後才能對記錄作修改。

悲觀鎖中的記錄鎖的特色:避免了不可重複讀,可是存在幻讀。(效率低)

 

樂觀鎖:(樂觀鎖支持懶加載)

表中必須添加一個版本控制字段version,version默認值爲0,用戶讀到的記錄是同樣的 version和money。version在User類中定義爲 private int version;

version在映射文件中使用<version>標籤配置,version是版本控制字段,由Hibernate自動管理。

<version name=」version」/>:標識對樂觀鎖的控制

Load1()方法(debug調試,執行commit()方法之前的代碼)

User user=(User)session.load(User.class,1);//不會發送sql語句,生成代理對象

int money= user.getMoney();//使用到對象方法時發送sql語句select  * from user where id=1 and version=0),

user.setMoney(money-200);

 user.setVersion(1); //查詢出數據後,修改表中記錄的值money = money-200;version=1。

session.update(user);//發送update語句,update user set money=money-200 and version=1 where id=1 and version=0;

暫不執行session.commit()這條語句。

 

Load2()方法(一次性執行完)

User user1=(User)session.load(User.class,1);//不會發送sql語句,生成代理對象

int money= user.getMoney();//使用到對象方法時發送sql語句select  * from user where id=1 and version=0),

user.setMoney(money-200);

 user.setVersion(1); //查詢出數據後,修改表中記錄的值money = money-200;version=1。

session.update(user);//發送update語句,update user set money=money-200 and version=1 where id=1 and version=0;

執行session.commit()方法。修改數據庫中id=1對應的記錄

 

再繼續執行load1()方法中的session.commit()語句,發送sql語句update user set money=money-200 and version=1 where id=1 and version=0;

可是版本號version的值已經改爲1,因此commit()的時候就會拋出異常。

 

 

事物的隔離級別:

一、         未提交讀:沒有提交就能在數據庫中讀到數據

二、         可提交讀:提交後才能在數據庫中讀到數據,解決不可重複讀的方法是加悲觀鎖。(mysql和oracle的默認處理級別)

三、         可重複讀:加了悲觀鎖(記錄鎖)

四、         序列化讀:不會出現幻讀,加表鎖(不會出現幻讀,不會出現髒讀,也不會出現不可重複讀)

五、         髒讀:讀取未提交到數據庫中數據。

六、         不可重複讀:讀取數據時,若是有人修改了數據就發現不是原來的數據,會出錯(加悲觀鎖解決問題)

七、         幻讀:每次讀取數據的時候讀取到的記錄數一直在變化(解決方法應該加表鎖)

安全性強的使用使用悲觀鎖

併發性強的時候使用樂觀鎖

 

緩存

緩存的分類:

一級緩存:session級別,生存週期和session同樣

session.save(obj);將數據歸入session管理。load()get(),iterator()方法都使用一級緩存。若是iterator()方法使用不當就會形成n+1問題。

Query 的list()方法不使用一級緩存,可是往一級緩存中放數據。(可能有n+1問題)

 

二級緩存:sessionFactory級別,工廠不會輕易銷燬,通常只建立一次。放到二級緩存中的數據特色:查詢次數頻繁,可是修改次數少

 

二級緩存:一級緩存能夠控制對二級緩存的使用get(),save(),load() 既要往一級緩存中存數據,也要往二級緩存中存數據。

查詢緩存:爲了提升Query list的查找效率

 

一級緩存和二級緩存主要是緩存實體對象,不會緩存屬性

查詢緩存只緩存實體屬性

 

一級緩存:(load,get,list,iterator都支持一級緩存)

直接查詢load()或者get()

Session級別的緩存,通常不須要配置也不須要專門的管理,直接使用就能夠了。

Student stu=(Student)session.load(Student.class,1);

stu.getName();//先從一級緩存session中查看有沒有id=1的Student對象,有的話就直接使用,沒有的話就發sql語句訪問數據庫根據記錄生成對象再使用。<load支持一級緩存>

stu.getSex();//再次使用Student對象的時候,先從一級緩存session中查有沒有該對象,發現有就直接使用,再也不建立了。

Student stu1=(Student)session.get (Student.class,1);//這裏也不會發送sql語句,由於一級緩存中有id=1的Student對象,因此直接使用。<get()也支持一級緩存>

 

先save後查詢get()或者load(),查到的是save()過的數據

主鍵生成策略爲:native

Student stu=new Student();

stu.setName(「ls」);

stu.setSex(「male」);

Serializable id=session.save(stu);//save()方法的返回值是Integer類型的數據。獲得的id是對象的主標識。這裏save()後Student對象歸入session管理,進入緩存。

Serializable:序列化接口,String ,Integer都支持序列化協議。

Student stu1=(Student)session.get(Student.class,1);//這裏不會發送sql語句,由於先從緩存中查找數據,發現有id=1的Student對象,就直接使用。

缺點: 可能會讀到髒數據。

 

Query的iterate()方法

Student stu1=(Student)session.createQuery(「from Student s where s.id=1」).iterate().//執行到iterator()方法纔會發送sql語句

一、        經過id先從數據庫中查對象的id(select id from t_stu where id=1)

二、        根據id到session中查對象

三、        若是沒有就再發sql語句訪問數據庫 (select* from t_stu where id=1)

Student stu2=(Student)session.createQuery(「from Student s where s.id=1」).iterate().//再執行一次,仍是會發查詢id的sql語句(select id from t_stu where id=1)

而後發現session中有對象,就不發sql語句(select* from t_stu where id=1)了,直接從session中拿到對象使用。

 

N+1問題:(iterator()緩存使用不當)

session.createQuery(「from Student ).iterate();

一、        先發一條sql語句取出全部的id(select id from t_stu

二、        查詢session中有沒有id對應的對象

三、        根據id依次發送sql語句訪問數據庫(select * from t_stu),數據庫中有多少條記錄就會發送多少條sql語句,若是數據庫中的記錄特別的多,這裏就會出現數據庫訪問擁塞,效率降低,形成n+1問題。

session.createQuery(「from Student ).iterate();//再查一次,這裏會先發sql語句查id,而後發現以及緩存session中有id對應的對象,就直接使用。不會再發送sql語句訪問數據庫了。

 

list()方法:只會往一級緩存中放數據,不會從一級緩存中取數據

List stus=session.createQuery(「from Student」).list();//直接發sql語句訪問數據庫,不會先查id,一次性直接把數據庫中的全部記錄放到一級緩存session

Stus= session.createQuery(「from Student」).list();//list()只放不拿,仍是會再次發送sql語句訪問數據庫,查出全部記錄放到session中。

 

N=1問題的解決方法:

第一次使用list(),第二次使用iterator()方法

一、        list()先把從數據庫中查詢出全部數據放到一級緩存session

二、         再使用iterator()方法使用一級緩存session中的數據

三、        iterator()會先發送sql語句查全部的id,而後去一級緩存session中查找id分別對應的對象

四、         發現對象已經存在一級緩存session中就直接使用。不會再發送多條sql語句訪問數據庫了。這樣就只發送了兩條sql語句,解決了N+1問題。

 

一級緩存的管理:(evict(),clear(),close()

使用flush()清除session中的緩存

session.flush();//清除臨時集合中的數據

session.clear();//清除persistanceContext下的map中的數據

必定要先flush(),而後再clear(),否則可能會產生異常

session.evict(user);//清除一條

session.clear();//清除多條數據

clear()和evict()的區別:clear()方法清除多條,evict()只能清除一條。

 

一級緩存只緩存對象,不緩存對象的屬性。

若是sql語句查詢的結果是記錄(一條或者多條),就會根據記錄生成對象(一個或多個)放到一級緩存session中。

若是sql語句查詢的結果是記錄的字段,就不會把字段映射成屬性放入一級緩存session中了。

 

一級緩存裏面的數據不能共享:session關閉後全部數據都銷燬。

Hibernate的缺點:Hibernate不會保證批量數據的同步,只能保證單條數據的同步。

 

二級緩存:(緩存的內容是對象,load,get,save都支持一級緩存)

二級緩存只有一個,生成周期很長,和SessionFactory的生命週期同樣。

緩存策略的做用是: 提供對緩存的方式

Hibernate通常使用Hcache這種緩存策略。

 

 

二級緩存的配置:

1、準備緩存策略的配置文件,在配置文件中寫緩存策略

在ehcache.xml中配置緩存策略:(配置在src下)

默認的緩存策略:

<defaultCache

maxElementsInMemeory=」10000」//緩存的最大空間

eternal=」false」//設置沒有任何對象常駐二級緩存中

timeToldleSeconds=」120」//若是120秒不使用就移除緩存

timeToLiveSeconds=」120」//最多能在二級緩存中呆120秒

overflowToDisk=」true」//若是緩衝存的數據超過10000,就將數據存到硬盤中

/>

 

配置指定的緩存策略:

<cache name=」cache1」

maxElementsInMemeory=」10000」//緩存的最大空間

eternal=」true」//設置全部對象都常駐二級緩存中

timeToldleSeconds=」 0」//無論多長時間沒有使用都不會移除緩存

timeToLiveSeconds=」 0」 //無論使用多長時間都不會移除緩存

overflowToDisk=」false」//溢出的時候不支持將數據存到硬盤上

/>

 

二級緩存經過緩存策略設定緩存中能存放多少個對象,存放多久,有多長的生存時間。

 

2、在Hibernate.cfg.xml文件中配置哪一個類的對象來管理二級緩存

<!-- 指定緩存產品提供類-->

<property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider</property>

org.hibernate.cache. EhCacheProvider:建立sessionFactory時,讀取Hibernate.cfg.xml文件,建立EhCacheProvider對象。經過插件執行對象的相應的方法--init(),讀取ehcache.xml配置文件。經過讀取ehcache.xml配置文件對緩存進行管理。

3、配置哪些對象使用二級緩存

<class-cache class=」cn.Student」 usage=」read-only」/>

4、指定使用二級緩存使用對象的使用方式

也能夠在實體類的hbm.xml文件中配置 <cache usage=」read-only」/>

緩存策略的集中方式:read-only(經常使用)read-write , nonstrict-read-write

五、        開啓二級緩存:

<!-- 開啓二級緩存 -->

<property name="hibernate.cache.use_second_level_cache">true</property>

 

二級緩存的使用:(只存放配置了的對象,get,load,save

load()方法的二級緩存解析:

Student stu=(Student)session.load(Student.class,1);

Stu.getName();//發送sql語句從數據庫中拿到id=1的記錄生成對象,存入二級緩存也會存入一級緩存中。

Session.close();//關閉session,一級緩存中的數據被清空

Student stu=(Student)session.load(Student.class,1);

Stu.getName();再次訪問時,先從一級緩存session中查看有沒有對象,沒有再從二級緩存中查,發現有就直接使用。

 

get()方法的二級緩存解析:

Student stu=(Student)session.get(Student.class,1); //發送sql語句從數據庫中拿到id=1的記錄生成對象,存入二級緩存也會存入一級緩存中

stu.getName();

session.close();//關閉session,一級緩存中的數據被清空

Student stu=(Student)session.get(Student.class,1); 再次訪問時,先從一級緩存session中查看有沒有對象,沒有再從二級緩存中查,發現有就直接使用。

stu.getName();

結論:在二級緩存開啓的狀態下,load()和get()不只使用一級緩存,還會使用二級緩存。

 

二級緩存的管理:(沒有臨時集合,基本上都是map

Student stu=(Student)session.get(Student.class,1); //發送sql語句從數據庫中拿到id=1的記錄生成對象,存入二級緩存也會存入一級緩存中

stu.getName()

session.close();//關閉session

factory =HibernateUtils.getSessionFactory();//拿到sessionFactory對象

factory.evict(Student.class);//清除二級緩存中的全部Student對象

factory.evict(Student.class,1);//只把二級緩存中id=1的Student對象

Student stu=(Student)session.get(Student.class,1); //再次發送sql語句從數據庫中拿到id=1的記錄生成對象,存入二級緩存也會存入一級緩存中(由於一級緩存和二級緩存中的數據都被清空了)

SessionFactory的做用:

一、         建立Session對象

二、         管理二級緩存

 

一級緩存和二級緩存的交互:

一級緩存能夠控制如何使用二級緩存

1、設定二級緩存只能讀不能寫

session.setCacheMode(CacheMode.GET);//設定僅從二級緩存中讀數據,而不向二級緩存中寫數據

Student stu=(Student)session.get(Student.class,1); //發送sql語句從數據庫中拿到id=1的記錄生成對象,只將數據存入一級緩存中。

stu.getName()

session.close();//關閉一級緩存,這時候一級緩存和二級緩存中都沒有數據,下一次使用對象時,會發送sql語句。

2、設定對二級緩存不作任何約束

Student stu=(Student)session.get(Student.class,1); //發送sql語句從數據庫中拿到id=1的記錄生成對象,將數據存入一級緩存和二級緩存中。

stu.getName()

session.close();//關閉一級緩存,這時候一級緩存沒有數據,二級緩存中有數據

3、設定二級緩存只能寫不能讀

session.setCacheMode(CacheMode.PUT); //設定只往二級緩存中寫數據,可是不能讀取二級緩存中的數據。

Student stu=(Student)session.get(Student.class,1); //雖然二級緩存中有數據,可是由於二級緩存中的數據不能讀取,因此要發送sql語句從數據庫中拿到id=1的記錄生成對象,將數據存入一級緩存和二級緩存中。

Stu.getName();

Session.close();/關閉session,一級緩存中的數據被清空。二級緩存中的數據還在。

4、設定對二級緩存不作任何約束

Student stu=(Student)session.get(Student.class,1); //不會發送sql語句,由於二級緩存中有數據,能夠讀取。

Stu.getName();

Session.close();/關閉session,一級緩存始終都沒有數據,二級緩存中的數據依舊在。

 

查詢緩存:(只能緩存屬性,不能緩存對象,只有list可使用查詢緩存)

list經過查詢緩存使用二級緩存

查詢緩存的配置:(不須要配置緩存策略)

一、         在Hibernate.cfg.xml中配置開啓查詢緩存:(二級和查詢緩存都開啓 true)

二、         在使用查詢緩存前要啓動查詢緩存

query.setCacheable(true);

 

一、        開啓查詢緩存(true),關閉二級緩存(false) 使用list()方法

Query query=session.createQuery(「select s.name form Student s);//查出全部的name屬性對應的字段

query.setCacheable(true);//啓動查詢緩存

List name=query.list();//發送sql語句,將查到的name屬性放到查詢緩存中

 

Query query=session.createQuery(「select s.name form Student s);//查出全部的name屬性對應的字段

query.setCacheable(true);//啓動查詢緩存,查詢緩存配置後也是默認開啓的

List name=query.list();//不會再發送sql語句,由於list()方法使用了查詢緩存,查詢緩存中已經有數據

結論:查詢緩存,值緩存屬性,不緩存對象;只有list()方法能使用查詢緩存

 

二、        查詢緩存不受一級緩存的影響

 

三、        開啓查詢緩存(true),關閉二級緩存(false) 使用iterate()方法

Query query=session.createQuery(「select s.name form Student s);//查出全部的name屬性對應的字段

query.setCacheable(true);//啓動查詢緩存

List name=query.iterate();//發送sql語句,由於是iterate()方法,不能使用查詢緩存,因此沒法將查到的name屬性放到查詢緩存中

 

Query query=session.createQuery(「select s.name form Student s);//查出全部的name屬性對應的字段

query.setCacheable(true);//啓動查詢緩存

List name=query.iterator();//仍是再次發送sql語句,由於iterator()方法不能使用查詢緩存,查詢緩存中沒有數據

結論:iterator()不使用查詢緩存。

四、        關閉查詢緩存(false),關閉二級緩存(false) 使用list()方法

Query query=session.createQuery(「select s form Student s);//查出全部的Student對象

查詢兩次,查詢完後關閉session,第二次查詢依舊會發送sql語句

 

五、        開啓查詢緩存(true),關閉二級緩存(false) 使用list()方法

Query query=session.createQuery(「select s form Student s);//查出全部的Student對象

query.setCacheable(true);//啓動查詢緩存

List stus=query.list();//查出的全部對象都存入一級緩存中,查出全部對象的id放入查詢緩存中。

Session.close();//關閉session,一級緩存中的數據被清空

 

Query query=session.createQuery(「select s form Student s);//查出全部的Student對象

query.setCacheable(true);//啓動查詢緩存

List stus=query.list();//先到查詢緩存中查看存放的全部對象的id,再到二級緩存中找id分別對應的對象,可是二級緩存已經關閉,全部查詢緩存會發送sql語句:slelect * from t_stu where id= id;有多個id就會發送多少條語句。這樣就會形成數據庫訪問擁塞,N+1問題。

 

6開啓查詢緩存(true),開啓二級緩存(true) 使用list()方法

就不會出現N+1問題,由於開啓二級緩存,二級緩存中就會有數據,直接使用就能夠了,不用再次發sql語句訪問數據庫拿到記錄生成對象。

 

N+1問題:

iterator()是對一級緩存使用不當,形成N+1問題

list()是對二級緩存的使用不當,形成N+1問題。

 

結論:Query的list方法不能使用一級緩存,可使用二級緩存,可是要開啓查詢緩存纔有用。

Iterate()也會使用二級緩存。

 

 

注意:

list()方法:直接取出全部對象

iterate()方法:先取出全部的記錄的id,再根據id到一級緩存中查看有沒有對應的對象,沒有的話就發送sql語句訪問數據庫拿到記錄生成對象。查詢出了對少個id就發送多少條sql語句。

 

 

今天的任務:

將緩存示例寫一遍(尤爲是二級緩存和查詢緩存)

寫>=1道算法題

看python文檔

複習一遍Hibernate

Lunix的指令

數據結構,算法

 

Hibernate查詢語句:hql

hibernate的查詢語言種類:

一、         標準化對象查詢(Criteria Query:徹底面向對象可是不夠成熟,不支持投影也不支持統計函數。

二、         原生的sql查詢語句(Native SQL Queries) :直接使用標準SQL語言和特定的數據庫相關聯的sql進行查詢。<和數據庫耦合性太強>

三、        Hibernate查詢語言(Hibernate Query Language HQL

使用sql的語法,以面向對象的思想完成對數據庫的查詢,獨立於任何數據庫

以類和屬性來代替表和數據列

支持多態,支持各類各樣關聯

減小了SQL的冗餘, hql:不區分大小寫

 

Hql支持的數據庫操做:

鏈接(joins),笛卡爾積(cartesian products),投影(projection),聚合(max,avg),排序(ordering),子查詢(subquering),sql函數,分頁

 

HQL導航查詢示例:

Query query=session.createQuery(「from User user where user.group.name=’zte’ 」);

//查找User類對象的屬性group,group屬性所屬的類型Group對象的name

至關於sql語句:

select  * from t_user ,t_group where t_group.name=’zte’ and t_group.id =t_user.groupid;

 

randomDate(「2017-1-1」,」2017-2-9」);//隨機生成一個「2017-1-1」,」2017-2-9」之間的日期

 

兩個或者兩個以上的普通屬性查詢

使用Object[]數組接收查詢出來的屬性

List students=session.createQuery(「select id ,name from Student」).list();

Iterator it=students.iterator();

While(it.hasNext()){

      Object[] obj=(Object) it.next();

      System.out.println(obj[0]+」---」+obj[1]);

}

先建立一個Object類型的數組對象Object[]

Object[]數組的長度和查詢的屬性的個數相同,一 一對應,obj[0]存放從數據庫中取出來的id值,obj[1]存放從數據庫中取出的name值。

Object[]數組元素的類型和Student實體類的屬性類型一 一對應。

List集合students中存放的元素是Object[]類型的數據,查詢出來多少條記錄就有多少個元素,有多少個Object[]數組。

 

使用Object[]數組接收查詢出來的屬性(添加別名,更加清晰)

List students=session.createQuery(「select s.id ,s.name from Student (as) s」).list(); //as能夠省略也能夠顯示的寫出來

Iterator it=students.iterator();

While(it.hasNext()){

      Object[] obj=(Object) it.next();

      System.out.println(obj[0]+」---」+obj[1]);

}

 

把從數據庫查到的記錄生成對象,再查出id和name屬性(不建議使用)

List students=session.createQuery(「select new Student( id ,name) from Student」).list();//自動調用Student類的有參的構造方法,建立Student對象

Iterator it=students.iterator();

While(it.hasNext()){

      Student stu=(Student) it.next();

      System.out.println(stu.getId()+」---」+stu.getName());

}

 

實體對象的查詢:(Select 查找對象 不能使用*)error: unexpected token *

List students=session.createQuery(「from Student」).list();//正確

List students1=session.createQuery(「from Student s」).list();//正確

List students2=session.createQuery(「select * from Student」).list();//這是錯誤的

List students3=session.createQuery(「select s from Student  as s」).list();//這纔是正確的,as能夠省略

Iterator it=students.iterator();

While(it.hasNext()){

      Student stu=(Student) it.next();

      System.out.println(stu.getId()+」---」+stu.getName());

}

 

條件查詢:

一、寫定查詢條件

List students=session.createQuery(「select s.id,s.name from Student s where s.name like ‘%1%’ 」).list();//查詢出全部名字包含1的學生id和name

二、使用填充符,動態查詢條件

List students=session.createQuery(「select s from Student s where s.name like 」).setParameter(0,」%1%」).list();

三、使用參數,把條件設到參數中

List students=session.createQuery(「select s from Student s where s.name like :myname 」).setParameter(「myname」,」%1%」).list();

 

四、使用多個參數,把多個條件分別設到參數中

List students=session.createQuery(「select s from Student s where s.name like :myname and s.id= : myid 」).setParameter(「myname」,」%1%」). setParameter(「myid」,1).list();

參數順序不同沒有關係。只和參數名有關。

 

五、使用一個參數,把多個條件設到一個參數中(setParameterList

List students=session.createQuery(「select s from Student s where s.id in (:myids )」).setParameterList(「myids」,new Object[]{1,2,3}).list();

使用參數數組對象,查詢出id 爲1 ,2,或者3的學生對象

 

六、日期類型數據做爲條件的查詢(使用數據庫中的date_format()函數)

List students=session.createQuery(「select s from Student s where date_format(s.createTime,’%Y-%m’)=?」).setParameter (0,」2017-2」).list();

先把字符串轉換爲日期類型

再把日期和數據庫中日期作比較

 

六、日期類型數據做爲條件的查詢(使用自定義的日期轉換函數 sdf.parse())

SimpleDateFormat sdf=new SimpleDateFormat(「yyyy-MM-dd HH:mm:ss」);

List students=session.createQuery(「select s from Student s where sdf.parse (s.createTime,’%Y-%m’)=?」).setParameter (0,」2017-2-12 11:22:11」).list();

先把字符串轉換爲日期類型

再把日期和數據庫中日期作比較

 

若是非要使用select *語句 就要使用原生的sql語句

List students=session.createSQLQuery(「select * from Student).list();

 

分頁查詢:

List students=session.createQuery(「select s from Student s )

.setFirstResult(5)//查詢從第五條記錄開始的數據 ((pageNo-1)*pageSize)

.setMaxResult(2)//每頁最多顯示兩條(pageSize)

.list();

 

對象導航查詢:

List students=session.createQuery(「select s.name  from Student s where s.classes.name like ‘%1%’ 「).list();

 

鏈接查詢:

內鏈接

List students=session.createQuery(「select s.name ,c.name  from Student s join s.classes c」).list();//經過學生類的關聯屬性把學生類和班級類作內鏈接。(找出既有學生又有班級的記錄)

外鏈接:

左鏈接:

List students=session.createQuery(「select s.name ,c.name  from Classes c left join c.student s」).list();拿到有學生的班以及沒有學生的班級(班級所有顯示,學生只顯示有班級的)

右鏈接:

List students=session.createQuery(「select s.name ,c.name  from Classes c right join c.students s」).list();拿到有學生的班以及沒有班級的學生(學生所有顯示,班級只顯示有學生的)

 

分組查詢:

List students=session.createQuery(「select s.name ,count(*)  from Classes c left join c.Student s group by s.name order by s.name」).list();

 

查詢過濾器:

使用場合:要求數據錄入人員只能看到本身錄入的數據時 ---使用查詢過濾器

使用條件:

在Student.hbm.xml映射文件中定義過濾器參數:

<filter-def name=」filtertest」>//查詢過濾器的名字

<filter-param name=」myid」 type=」integer」/>//參數名 參數類型爲integer

</filter-def>

 

使用步驟:(先在Student.hbm.xml文件中)

<filter name=」filtertest」  condition=」id &lt; :myid」/>//condition配置條件,id< myid(參數)

在查詢方法中:session.enableFilter(「filtertest」) .setParameter(「myid」,10)//啓用查詢過濾器,而且設定參數。 表示查詢id<10的數據記錄。

 

外置命名查詢:

在Student.hbm.xml映射文件中採用<query>標籤來定義hql:

<query name=」searchStudents」>

<![CDATA[SELECT s FROM Student s where s.id<?]]>

</query>

List students=session.getNameQuery(「searchStudents」).setParameter(0,5).list();//安全性比較差,顯示寫出sql語句在配置文件中,容易被黑。

 

DML風格:(批量更新,批量操做)和緩存不一樣步

session.createQuery(「update Student s set s.name=? where s.id<?」)

.setParameter(0,」小仙女」)//更新name都爲小仙女

.setParameter(1,5)//id<5的記錄都被更新

.executeUpdate();

Hibernate的缺點:不支持批量更新,不是說不能批量更新,而是批量更新的操做不能保證緩存中的數據和數據庫中的數據同步。

 

Struts1Hibernate的集成:

一、         建立web項目

二、         導入兩個框架的jar包以及jstl的jar包—--WEB-INF/lib

三、         在src下存放國際化資源文件

四、         在web.xml中配置struts1.0  <ActionServlet>,配置編碼過濾器

五、         配置struts-config.xml(一般用DispatchAction),放在WEB-INF下

六、         在src下配置hibernate.cfg.xml,log4j.properties

七、         在web.xml文件中配置HibernateFilter,一啓動就建立SessionFactory

八、         也能夠在struts-config.xml文件中使用插件配置Hibernate的啓動,可是建議在Filter中配置,由於filter還要處理openSessionInview問題。

九、         模型層:包括實體類和類對應的*hbm.xml文件.

相關文章
相關標籤/搜索