Hibernate經典總結

jopen 11年前發布 | 120K 次閱讀 Hibernate 持久層框架

1.Hibernate框架作用,優點

(1)、什么是Hibernate

       Hibernate是一個數據訪問框架(持久層框架),在項目中利用Hibernate框

       架可以實現對數據庫的增刪改查操作,為業務層構建一個持久層。

(2)、Hibernate框架主要用于對數據庫的操作。

    使用該框架可以簡化數據操作代碼,程序員可以將更多地精力放在業務

       編寫上。Hibernate經典總結

     Hibernate本質上是對JDBC技術的封裝。

    Hibernate和JDBC的關系類似于JQuery和JavaScript的關系。

   (3)、原有JDBC方式訪問數據庫,有以下幾點不足:

   a.需要編寫大量復雜的SQL語句

   b.需要做大量的對象和記錄的轉換

       c.數據庫移植時,需要修改SQL語句。(非標準SQL語句:如分頁語句,

使用數據庫函數的語句)

使用Hibernate框架后,可以解決上述問題

   (4)、Hibernate框架的優點:

a.無需編寫大量復雜的SQL語句

b.程序中的實體對象和數據庫中的數據實現自動映射轉換

c.方便數據庫的移植

2.Hibernate設計原理

   Hibernate框架是一款ORM工具。基于ORM設計思想開發出來的。

   ORMObject--Relation--Mapping對象關系映射

         對象指的就是Java的實體對象;

         關系指的是關系型數據庫。(Oracle、DB2、MySql、SqlServer)

         ORM的主要思想就是將程序中的對象和數據庫中的數據實現自動映射

         轉換。利用ORM工具,在查詢時,可以自動將記錄封裝成Java對象返

         回。在更新、插入操作時,可以將對象自動寫入數據表。對于中間的

         SQL+JDBC操作細節,完全封裝在工具底層

  基于ORM思想設計的框架有很多,例如Hibernate,iBATIS,JPA等.

*3.Hibernate框架主要結構

    Hibernate主要由以下幾部分構成:

   a.Java實體類(1-n個)

     與數據表對應,用于封裝數據表的一行記錄。

   b.hibernate.cfg.xml(1個)

      Hibernate主配置文件,里面主要定義連接數據庫的參數、框架參數等。

<hibernate-configuration>

<session-factory>

<!-- 方言設置,hibernate根據方言類型自動生成相應數據庫的sql語句 

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

<property name="connection.url">jdbc:oracle:thin:@localhost:1522:xe

        </property>

<property name="connection.username">system</property>

<property name="connection.password">zhuxun</property>

<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver

</property>

<!-- 將sql語句從控制臺打印出來 -->

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

<property name="format_sql">true</property><!-- 格式化sql語句 -->

<!-- 加載映射描述文件 -->

<mapping resource="entity/Cost.hbm.xml" />

</session-factory>

</hibernate-configuration>

   c. 文件名.hbm.xml1-n個)

      Hibernate映射描述文件,里面定義了實體類和數據庫之間的對應關

      系。例如定義了java實體類和數據表,屬性和表字段之間的對應關系。

<hibernate-mapping>

<!-- Hibernate映射描述文件定義了實體類和數據庫之間的對應關 系。例

         如定義了java類和數據表, 屬性和表字段之間的對應關系。

 -->

<class name="entity.Cost" table="Cost">

<!-- 定義主鍵映射 -->Hibernate經典總結

<id name="id" column="ID" type="integer">

            <!-- 也可寫成type="java.lang.Integer"-->

<!-- 指定主鍵的生成方式為序列方式 -->

<generator class="sequence">

<param name="sequence">cost_seq</param>

</generator>

</id>

<!-- 非主鍵映射 -->

<property name="name" column="NAME" type="string">

        </property>

<property name="baseDuration" column="BASE_DURATION" 

           type="integer"></property>

<property name="baseCost" column="BASE_COST" 

           type="double"></property>

<property name="unitCost" column="UNIT_COST" 

           type="double"></property>

</class>

</hibernate-mapping>

   d.Hibernate API

   在使用時,需要使用Hibernate提供的API, 它們將SQL+JDBC操作細節

        封裝起來了。

*4.Hibernate主要API

 Configuration:用于加載主配置文件(hibernte.cfg.xml)和映射文件。

 SessionFactory:用于創建Session對象。

 Session:將原Connection對象進行了封裝,代表Hibernate與數據庫之

       間的一次連接,負責執行增刪改查操作。

       save(),update(),delete(),load(),get()方法.

 Transaction:用于進行事務管理。

             注意:由于關閉了JDBC中的自動提交功能

                   (setAutoCommit(false)),所以使用時,必須顯示地

                   執行commit 操作。

Query:負責執行各種查詢。

 

注:此處的Session區別于JavaWeb中的Session。前者表示Hibernate與數據庫之間的

    連接,后者表示客戶端與服務器端的一次會話。

*5.Hibernate使用步驟

   a.創建工程,引入hibernate和驅動開發包

   b.在src下追加hibernate.cfg.xml主配置

   c.根據數據表創建Entity實體類

   d.編寫實體類和數據表的映射文件xxx.hbm.xml

   e.利用Hibernate API實現增刪改查操作

6.Hibernate中的增改刪查操作:

/**

 * 測試添加操作

 */

public static void testSave() {

Cost cost = new Cost();

// cost.setId();//由hbm.xml定義的序列負責

cost.setName("400元套餐");

cost.setBaseDuration(200);

cost.setBaseCost(400.0);

cost.setUnitCost(2.0);

cost.setCostType("1");

cost.setDescr("400特惠大套餐");

cost.setCreateTime(new Date(System.currentTimeMillis()));

cost.setStatus("0");

Session session = HibernateUtil.getSession();

// 開啟事務

Transaction tx = session.beginTransaction();

session.save(cost);// 保存

tx.commit();// 提交事務

session.close();// 是否session

}

 Hibernate經典總結

/**

 * 測試更新操作

 */

public static void testUpdate() {

// 開啟資費處理,更新status=1,startTime時間

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

// 按ID=189主鍵做條件查詢

Cost cost = (Cost) session.load(Cost.class, 189);

cost.setStatus("1");// 設置為暫停狀態

cost.setStartTime(new Date(System.currentTimeMillis()));// 設置為開

                    啟時間

session.update(cost);

tx.commit();

session.close();

}

 

/**

 * 測試刪除操作

 */

public static void testDelete() {

Cost cost = new Cost();

cost.setId(189);

// Cost cost = (Cost)session.get(Cost.class, 189);

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

session.delete(cost);// 執行一個delete語句,按ID做條件刪除

tx.commit();

session.close();

}

 

/**

 * 測試查詢操作

 */

public static void testFindAll() {

Session session = HibernateUtil.getSession();

// 利用Query執行一個HQL查詢語句

Query query = session.createQuery("from Cost");

List<Cost> list = query.list();

for (Cost c : list) {

System.out.println(c.getId() + "  " + c.getName() + "  " + 

              c.getBaseDuration());

}

session.close();

}

 

7、Hibernate封裝JDBC+SQL語句過程:

Java對象—>Cost—>SQL+JDBC—>DB

insert into cost (NAME, BASE_DURATION, BASE_COST, UNIT_COST, STATUS.) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

//java反射技術

Class costClass = Cost.class;

控制臺輸出的SQL語句:

Hibernate: 

    select cost_seq.nextval from dual

Hibernate: 

    insert into Cost

        (NAME, BASE_DURATION, BASE_COST, UNIT_COST, STATUS........) 

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

 

8、注意:

  (1).Hibernate在底層對JDBC語句進行了封裝,用rs.getObject(“some”)方式

獲取數據。

eg:"BASE_DURATION"為數據庫中表的字段名,類型為Number(5)。如果在表中該字段有null值,那么對于如下兩種獲取方式將出現不同的結果:

對于rs.getObject("BASE_DURATION")方式,得到的值為null

對于rs.getInt("BASE_DURATION")方式,得到的值為0

所以在Hibernate中,根據數據表創建的實體類中不能有基本數據類型,如果為基本類型,rs.getObject(“some”)得到的null值賦給基本類型會發生異常,所以要定義成相應的包裝類。

  (2).創建Session對象耗費時間比較多,session對象里面綁定了大量的預編譯

     的SQL語句。所以不能每次使用都新建一個對象。

9、Hibernate數據映射類型:

  在hbm.xml中,描述屬性和字段之間映射的時候,可以使用type屬性指定數據 

  映射類型。

  type屬性可以指定Java類型和Hibernate類型。主要作用是指定屬性值和字段

  值之間轉換時(即在底層執行rs.getXXX(),stmt.setXXX()時XXX的類型),采用的

  轉換類型。(建議采用Hibernate映射類型)

  Hibernate數據映射類型主要有以下幾種:

  a.整數類型

     byte,short,integer,long

  b.浮點數類型

     float,double

  c.字符串類型

     string

  d.日期時間類型

     date,time,timestamp

其中:

date只取年月日,

time只取小時分鐘秒,

timestamp取完整的時間年月日小時分鐘秒

  e.boolean類型

     該類型可以實現boolean屬性和字符之間的轉換。

     yes_no:將true|false轉換成Y|N

    true_false:將true|false轉換成T|F

    底層實現方法:

stmt.setChar(cost.getBoolean().toString())  

cost.setBoolean(Boolean.parseBoolean(rs.getChar("***")))

  f.其他big_decimal,big_integer,clob,blob

    

-----------案例-----------------

create table t_foo(

  t_id number primary key,

  t_name varchar2(50),

  t_salary number(8,2),

  t_marry char(1),

  t_hiredate date,

  t_last_login_time date

)

-------------mysql----------------

create table t_foo(

  t_id int auto_increment primary key,

  t_name varchar(50),

  t_salary double,

  t_marry char(1),

  t_hiredate date,

  t_last_login_time timestamp

) default charset=utf8;

10.Hibernate主鍵生成方式

  在hbm.xml中,可以為主鍵指定生成方式。具體如下:

  *a. sequence :采用指定序列生成,適用于Oracle數據庫。使用格式

<generator class="sequence">

    <param name="sequence">foo_seq</param>

</generator>

  

 *b.identity : 采用數據庫自增長機制生成。適用于MySQL,SQLServer,DB2數

              據庫。

   <generator class="identity">

   </generator>

   

 *c.native : 由hibernate決定,hibernate會根據配置文件hibernate.cfg.xml中

             方言<property name="dialect">決定,如果方言是Mysql,相當

             于identity,如果方言是Oracle,相當于是sequence

 

   <generator class="native">

   </generator>

  

 *d.increment : 首先獲取最大主鍵值,然后加1,再執行插入操作。適用于各

                 種數據庫。

 先執行select max(id) from t_foo;

 再執行insert into...

    <generator class="increment">

   </generator>

  

 e.assigned : Hibernate忽略主鍵生成,不負責管理。需要程序員在程序中指定

             主鍵值,不常用

   <generator class="assigned">

   </generator>

 

  f.其他

 uuid:采用UUID算法生成一個字符串主鍵值

 hilo:采用高地位算法生成一個數值主鍵值

   

 

 

11.Hibernate框架基本特性

   1)一級緩存(Session級別緩存,默認開啟)

    a.每次創建一個Session對象,會為這個Session對象提供一個緩存區,用于

      緩存該Session查詢出來的單個對象。當使用該Session再次查詢同一個對

      象時,從緩存取出,避免對數據庫的查詢。

   *b.一級緩存區管理的方法:

    session.evict(obj):將obj從一級緩存移除

    session.clear():清除一級緩存所有對象

    c.一級緩存好處

     當利用同一個Session對象多次查詢同一個數據對象時,僅第一次從數據庫

     查詢,后續幾次從緩存獲取。

-------------------------------------

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

for(int i=1;i<100000;i++){ 

     User user = new User();

     //......

     session.save(user);

     //20條調用一次flush

     if(i%20==0){

        session.flush();//同步到數據庫

        session.clear();//清空緩存

      }

}

tx.commit;

HibernateUtil.close();

對于上面列子,如果不及時清理緩存,將會發生內存溢出異常。

------------------------------------

每個session只能訪問自己的緩存區,在session1中緩存了一個對象,

   在session2中查詢相同的對象,仍然要從數據庫中重寫查找

/**

 * 使用兩個不同的Session查詢同一個對象 結果:去數據庫查詢2兩次

 */

public static void test3() {

Session session = HibernateUtil.getSession();

// 第一次查詢

Foo foo = (Foo) session.get(Foo.class, 1);

System.out.println(foo.getName() + " " + foo.getSalary());

session.close();

session = HibernateUtil.getSession();

// 第二次查詢

Foo foo1 = (Foo) session.get(Foo.class, 1);

System.out.println(foo1.getHireDate() + " " + foo1.getLastLoginTime());

session.close();

}

/**

 * 同一個session兩次查詢不同對象 結果:去DB查兩次

 */

public static void test2() {

Session session = HibernateUtil.getSession();

// 第一次查詢

Foo foo = (Foo) session.get(Foo.class, 1);

System.out.println(foo.getName() + " " + foo.getSalary());

// 第二次查詢

Foo foo1 = (Foo) session.get(Foo.class, 22);

System.out.println(foo1.getHireDate() + " " + foo1.getLastLoginTime());

session.close();

}

/**

 * 同一個session兩次查詢相同對象 結果:去DB查一次

 */

public static void test1() {

Session session = HibernateUtil.getSession();

// 第一次查詢

Foo foo = (Foo) session.get(Foo.class, 1);

System.out.println(foo.getName() + " " + foo.getSalary());

// session.evict(foo);//從一級緩存移除foo對象

session.clear();

// 第二次查詢

Foo foo1 = (Foo) session.get(Foo.class, 1);

System.out.println(foo1.getHireDate() + " " + foo1.getLastLoginTime());

session.close();

}

 

   2)對象持久性

     Hibernate框架用于實現對數據庫的操作,為應用程序構建一個持久層。

    (由持久對象組成)

     Hibernate使用中,實體對象可以具有以下3種狀態。

 

     a.臨時狀態(暫時態,Transient

        采用new方式構建的對象的狀態是暫時的,如果沒有跟數據庫表相關聯

        的行為,只要應用程序不再引用這些對象,他們的狀態將會丟失,并由

        垃圾回收機制回收。

     *b.持久狀態Persistent

如果內存中的對象和數據庫的記錄有對應關系,即和session對象相關,

則此對象處于Persistent狀態,在當事務提交時它們的狀態和數據庫進行

同步。

采用Session對象查詢出來的,受Session對象管理的對象。例如調

load,get,save,update方法后的對象。

        處于持久性的對象特點如下:

      --當事務提交(commit())時,或執行session.flush()方法時,對象的數

           據狀態可以更新到數據庫。

           commit()前會自動調用session.flush(),

 commit()=session.flush()+commit()

         --對象不能被垃圾回收器回收

        --對象存儲在一級緩存中,由Session負責管理

     c.游離/脫管狀態Detached

Session關閉(session.close())之后,持久化對象就變為detached對象。表

示這個對象不能再與數據庫保持同步,它們不再受Hibernate(確切地說

是Session)管理。另外,調用了session.evict(),session.clear()方法后,

一級緩存中的對象就變成了游離狀態。

session.close()前會自動調用session.clear(),close()=clear()+close()。

 

public static void test1() {

Foo foo = new Foo();

foo.setName("scott");

foo.setSalary(2000);

foo.setMarry(true);

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

session.save(foo);// foo變為持久對象

foo.setName("tom");// 修改持久對象name屬性

foo.setSalary(3000);// 修改持久對象salary屬性

// session.flush();//將緩存中對象數據與數據庫同步

tx.commit();// 默認調用session.flush,然后提交事務

session.close();

      }

  *3)延遲加載(默認啟用)

     Hibernate提供一些方法,利用這些方法返回的對象,并沒有立刻加載數據

    庫的數據。而是在調用對象的getXxx()方法時才觸發數據庫查詢,加載數據

    記錄。

其一,如果通過session查詢某對象,session將先到緩存中查找是否有查詢

的對象,找到則直接取出,否則才查詢數據庫。

其二,session需要負責實時維護在緩存中的數據,保護緩存中的數據與數據

庫中數據的一致性,一旦用戶對緩存中的數據做了修改,session負責將數據

更新到數據庫中(前提是調用了commit()或flush()方法)。

*a)延遲加載機制的基本原理:

  當訪問實體對象時,并不是立即到數據庫中查找。而是在真正要使用實體

 對象的時候,才去數據庫查詢數據。有部分方法具備這種功能,比如

 session.load(),query.iterator()。

注意:這些方法返回的對象,只有id屬性有值,其他屬性數據在使用

時候(調用getXxx()方法時)才去獲取。

b)延遲加載優點:

使用延遲加載,可以降低用戶并發量,減少服務器資源占用。

*c)哪些方法具有延遲加載

     session.load()

     query.iterator()

     關聯屬性

   *d)使用延遲加載方法時,避免出現下面異常LazyInitializationException: 

       could not initialize proxy - no Session

     原因:session關閉過早

  session.close()要放在查詢之后,否則出現異常

   *e)get()和load()區別

      相同點:按照主鍵ID做條件查詢某個對象

      不同點如下:

       --load采用了延遲加載機制,get為立刻加載。

       --load如果沒有符合記錄,會拋出ObjectNotFoundException; 而get方法

         返回的是null,不會出現異常。

         session.load(Cost.class,11235)如果id不存在,則拋出異常

         session.get(Cost.class,11235)為null

       --load返回的是一個動態生成一個類型,get方法返回的是實體類型。

     f)延遲加載實現原理(動態代理技術)

      --采用延遲加載方法后,Hibernate會在底層動態的創建一個新的實體類,

        動態編譯成class。比如load方法返回的是動態生成的一個類型(如Cost

        的子類),在這個類里面重寫了Cost類的getter方法

  public class Cost$$CGLIB... extends Cost{

      //重寫Cost中屬性的getter方法

      public String getName(){

           //判斷是否有name值,沒有查詢DB

           return name;

       }

   }

   --Hibernate采用了cglib.jar和asm.jar兩個開發包。實現了動態生成新類和編

    譯操作。

public static void test3() {

Session session = HibernateUtil.getSession();

Foo foo = (Foo) session.load(Foo.class, 29);

System.out.println(foo.getClass().getName());

 

session.close();

}

 Hibernate經典總結

public static void test2() {

Session session = HibernateUtil.getSession();

// 延遲加載查詢

Foo foo = (Foo) session.load(Foo.class, 29);

session.close();// session關閉過早導致異常

System.out.println("-------------");

System.out.println(foo.getName());// 觸發查詢

System.out.println(foo.getSalary());

}

 

public static void test1() {

Session session = HibernateUtil.getSession();

// 延遲加載查詢

Foo foo = (Foo) session.load(Foo.class, 1);

System.out.println("-------------");

System.out.println(foo.getName());// 觸發查詢

System.out.println(foo.getSalary());

session.close();

}

12、OpenSessionInView與ThreadLocal:

(1)、OpenSessionInView

如果通過Hibernate查詢到了一條記錄(此時并未執行getter方法,由于

延遲加載原因,此時的記錄除了主鍵并不包含任何數據,即沒有真正執行查

詢),該記錄須在Jsp頁面顯示(本質上是間接執行了getter方法),那么此時

就需要一種技術實現把Session的關閉延遲到View組件運行完之后。

OpenSessionInView技術可以把Session的關閉延遲到View組件運行完之

  后。

 如果用延遲加載必須使用OpenSessionInView技術,否則在取數據時,

session已經關閉了。

實現OpenSessionInView可以采用的技術如下:

Servlet------過濾器

Struts2------攔截器

Spring--------AOP

(2)、ThreadLocal:使用OpenSessionInView必須滿足Session的線程單例。

一個線程分配一個Session,在該線程的方法中可以獲得該Session,具體使用ThreadLoad,ThreadLocal是一個Map結構,線程ID作為key,拿要存的對象當做value,這樣就可以將要存儲的數據和線程綁定。

(3)、如何實現OpenSessionInView

方案一:利用ThreadLocal機制手動實現Session與當前線程綁定

  public class HibernateUtil1 {

private static SessionFactory sf;

private static ThreadLocal<Session> sessionLocal = new 

ThreadLocal<Session>();

static {

// 創建配置對象

Configuration conf = new Configuration();

// 加載指定的hibernate.cfg.xml配置

conf.configure("/hibernate.cfg.xml");

// 獲取SessionFactory

sf = conf.buildSessionFactory();

}

public static Session getSession() {

Session session = sessionLocal.get();

if (session == null) {

session = sf.openSession();

sessionLocal.set(session);

}

return session;

}

public static void closeSession() {

Session session = sessionLocal.get();

sessionLocal.set(null);

if (session != null && session.isOpen()) {

session.close();

}

}

   }

public class OpenSessionInViewInterceptor extends AbstractInterceptor {

@Override

public String intercept(ActionInvocation in) throws Exception {

// 開啟事務

Session session = HibernateUtil1.getSession();

Transaction tx = session.beginTransaction();

try {

System.out.println("----開啟事務---");

in.invoke();// 執行Action,DAO和Result-->JSP

// 提交事務

tx.commit();

System.out.println("----提交事務---");

return null;

catch (Exception ex) {

// 回滾事務

tx.rollback();

System.out.println("----回滾事務---");

throw ex;

finally {

// 關閉session

 HibernateUtil1.closeSession();

System.out.println("----關閉事務---");

}

}

  }

 方案二:利用Hibernate框架封裝的機制實現Session與當前線程綁定

public class HibernateUtil2 {

private static SessionFactory sf;

static {

//創建配置對象

Configuration conf = new Configuration();

//加載指定的hibernate.cfg.xml配置

conf.configure("/hibernate.cfg.xml");

//獲取SessionFactory

sf = conf.buildSessionFactory();

}

public static Session getSession(){

//返回當前線程綁定的session,需要在hibernate.cfg.xml增加配置

//如果沒有的新建一個,然后和線程綁定

//該Session對象在事務結束自動關閉

//該Session對象必須在一個事務中使用

return sf.getCurrentSession();

//創建一個新的Session對象,

//必須手動關閉

//return sf.openSession();

}

}

public class OpenSessionInViewInterceptor extends AbstractInterceptor {

@Override

public String intercept(ActionInvocation in) throws Exception {

// 開啟事務

Session session = HibernateUtil1.getSession();

Transaction tx = session.beginTransaction();

try {

System.out.println("----開啟事務---");

in.invoke();// 執行Action,DAO和Result-->JSP

// 提交事務

tx.commit();

System.out.println("----提交事務---");

return null;

catch (Exception ex) {

// 回滾事務

tx.rollback();

System.out.println("----回滾事務---");

throw ex;

finally {

// Hibernate會自動關閉session

System.out.println("----關閉事務---");

}

}

}

另外還需要在hibernate.cfg.xml中加入如下配置:

<!-- 支持sf.getCurrentSession()方法,將session與線程綁定 -->

<property name="current_session_context_class">thread</property>

 

:對于sf.openSession()和sf.getCurrentSession(),sf.getCurrentSession()

獲得的session對象實現了和ThreaLocal的自動綁定以及在事務結束后的自

動關閉功能。而sf.openSession()則要顯示地綁定和關閉,該

sf.getCurrentSession()對象必須在一個事務中使用。

 

=========案例練習==========

1.重構資費列表顯示示例

為了支持延遲加載方法,需要在項目中采用OpenSessionInView思想。可以利用Filter和Interceptor技術實現。

*.action-->攔截器前期(無)

-->Action-->DAO-->Result

-->JSP(通過標簽和EL獲取對象數據)

-->攔截器后期(關閉Session)

-->將HTML結果響應

 

13.Hibernate關系映射的作用

   利用關聯映射,Hibernate可以幫助查詢加載對象相關的數據,可以簡化查詢

   代碼。

   此外還可以基于關系進行增刪改操作。

   Hibernate關系映射分類: 一對一、一對多、多對一、多對多、繼承

注:Hibernate關系映射區別于Hibernate數據映射。

14.一對多關系映射

   a.確定兩個對象哪個是1方和n方

   b.需求:由1方對象查詢n方記錄,可以采用一對多關系映射

   c.首先在1方實體類中添加集合屬性Set

   d.然后再1方的hbm.xml中定義一對多關系映射的描述

    <set name="關系屬性">

        <key column="關聯條件的外鍵字段"/>

        <one-to-many class="關聯的另一方類型,即n方類型"/>

    </set>

 

   e.使用時,通過1方對象.關系屬性獲取n方數據

    eg:測試一對多關系:account--->service

public class TestOneToMany {

/**

 * 測試一對多

 */

public static void main(String[] args) {

test2();

test2();

}

/**

 * 顯示帳務帳號信息(關聯操作)

 */

public static void test2() {

int id = 1011;

Session session = HibernateUtil.getSession();

Account account = (Account) session.load(Account.class, id);

System.out.println("--------顯示帳務帳號基本信息--------");

System.out.println("登錄名:" + account.getLoginName());

System.out.println("身份證號:" + account.getIdcardNo());

System.out.println("狀態:" + account.getStatus());

System.out.println("創建日期" + account.getCreateDate());

System.out.println("------包含的業務帳號信息-------");

Set<Service> list = account.getServices();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getUnixHost() + " "

+ s.getOsUsername() + "" + s.getStatus());

}

HibernateUtil.closeSession();

}

 

/**

 * 顯示帳務帳號信息(單表操作)

        如果用單表操作的話,要手動的編寫兩次查詢語句,而關聯操作只需一

         次

 */

public static void test1() {

int id = 1011;

Session session = HibernateUtil.getSession();

Account account = (Account) session.load(Account.class, id);

System.out.println("--------顯示帳務帳號基本信息--------");

System.out.println("登錄名:" + account.getLoginName());

System.out.println("身份證號:" + account.getIdcardNo());

System.out.println("狀態:" + account.getStatus());

System.out.println("創建日期" + account.getCreateDate());

System.out.println("------包含的業務帳號信息-------");

String hql = "from Service where accountId=?";

Query query = session.createQuery(hql);

query.setInteger(0, id);

List<Service> list = query.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getUnixHost() + " "

+ s.getOsUsername() + "" + s.getStatus());

}

HibernateUtil.closeSession();

}

}

<!-- 描述services屬性,采用一對多關系加載Service記錄 -->

       <set name="services">

        <!-- 指定關聯條件,寫外鍵字段 -->

        <key column="ACCOUNT_ID"></key>

        <!-- 指定采用一對多關系, class指定關聯的另一方-->

        <one-to-many class="org.tarena.netctoss.entity.Service"/>

 </set>

test2()輸出:

--------顯示帳務帳號基本信息--------

Hibernate: 

    select

        account0_.ID as ID0_0_,

        account0_.RECOMMENDER_ID as RECOMMEN2_0_0_,

        ......................................

        account0_.LAST_LOGIN_IP as LAST20_0_0_ 

    from

        ACCOUNT account0_ 

    where

        account0_.ID=?

登錄名:dgbf70

身份證號:330902197108270429

狀態:1

創建日期2009-03-01

------包含的業務帳號信息-------

Hibernate: 

    select

        services0_.ACCOUNT_ID as ACCOUNT4_1_,

        ...........................................

        services0_.CLOSE_DATE as CLOSE10_1_0_ 

    from

        SERVICE services0_ 

    where

        services0_.ACCOUNT_ID=?

2002 192.168.0.26 huangr2

2004 192.168.0.23 huangr0

2003 192.168.0.20 huangr0

 

test1()輸出:

--------顯示帳務帳號基本信息--------

Hibernate: 

    select

        account0_.ID as ID0_0_,

        ...........................

        account0_.LAST_LOGIN_IP as LAST20_0_0_ 

    from

        ACCOUNT account0_ 

    where

        account0_.ID=?

登錄名:dgbf70

身份證號:330902197108270429

狀態:1

創建日期2009-03-01

------包含的業務帳號信息-------

Hibernate: 

    select

        service0_.ID as ID1_,

        .....................

        service0_.CLOSE_DATE as CLOSE10_1_ 

    from

        SERVICE service0_ 

    where

        accountId=?

2002 192.168.0.26 huangr2

2004 192.168.0.23 huangr0

2003 192.168.0.20 huangr0

 

15.多對一關系映射

   a.需要由n方對象查詢1方對象信息

   b.在n方實體類中添加屬性,屬性類為1方類型

   c.在n方hbm.xml文件中,添加屬性的映射描述信息

<many-to-one name="關系屬性"

  column="關聯條件的外鍵字段" class="關聯的另一方類型,即1方類型"/>

   d.清除n方實體類中外鍵字段描述信息和屬性

   e.使用時,通過n方對象.關聯屬性獲取相關的1方記錄信息

測試多對一關系:service-->account

public class TestManyToOne {

/**

 * 測試多對一:

 */

public static void main(String[] args) {

Session session = HibernateUtil.getSession();

Service service = (Service) session.load(Service.class, 2002);

System.out.println("業務帳號:" + service.getId());

System.out.println("業務服務器:" + service.getUnixHost());

System.out.println("業務用戶名" + service.getOsUsername());

System.out.println("所屬帳務帳號:" + service.getAccount().getId());

System.out.println("開通身份證:" + service.getAccount().getIdcardNo());

System.out.println("真實姓名:" + service.getAccount().getRealName());

HibernateUtil.closeSession();

}

     }

輸出:

業務帳號:2002

Hibernate: 

    select

        service0_.ID as ID1_0_,

        service0_.UNIX_HOST as UNIX2_1_0_,

        ........................

        service0_.CLOSE_DATE as CLOSE10_1_0_ 

    from

        SERVICE service0_ 

    where

        service0_.ID=?

業務服務器:192.168.0.26

業務用戶名huangr

所屬帳務帳號:1011

Hibernate: 

    select

        account0_.ID as ID0_0_,

        account0_.RECOMMENDER_ID as RECOMMEN2_0_0_,

        ........................

        account0_.LAST_LOGIN_IP as LAST20_0_0_ 

    from

        ACCOUNT account0_ 

    where

        account0_.ID=?

開通身份證:330902197108270429

  真實姓名:huangrong

16.關聯操作注意事項

 級聯操作也即關聯操作。  

 注意區分:關聯屬性和主對象的關系

 不要忘了在hbm.xml的關聯屬性定義中添加cascade屬性,具體參見下面

        的”*2)級聯添加”

 *1)關聯(級聯)查詢

     默認情況下,關聯屬性數據的加載采用了延遲加載機制,當調用屬性getter

    方法才出發查詢。在使用時,如果需要將關聯屬性和主對象一起加載,可以

    通過以下方法改變加載機制

     a.(1)在hbm.xml中為該屬性添加lazy="false" (關聯屬性數據在主對象

        加載時加載)

       為該屬性追加fetch="join"

       (2).(指定關聯屬性加載方式,可以指定為select,join,subselect值)

<many-to-one name="account" column="ACCOUNT_ID"  

lazy="false" fetch="join"  

class="org.tarena.netctoss.entity.Account">

  </many-to-one>

     b.寫一個HQL,采用join fetch關鍵字加載關聯屬性。

    (注意:a方案會影響所有Service對象操作,不推薦,如果需要一起加載關

     聯屬性,建議采用b方案)

public class TestJoinFetch {

/**

 * 用join fetch關鍵字加載關聯屬性,取消延遲加載

 */

public static void main(String[] args) {

test2();

}

/**

 * 實例化Account對象時一起實例化關聯屬性services

 */

public static void test2(){

String hql = "from Account a " +

"join fetch a.services " +

"where a.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 1011);

Account account = 

(Account)query.uniqueResult();

System.out.println("--------顯示帳務帳號基本信息--------");

System.out.println("登錄名:"+account.getLoginName());

System.out.println("身份證號:"+account.getIdcardNo());

System.out.println("狀態:"+account.getStatus());

System.out.println("創建日期"+account.getCreateDate());

System.out.println("------包含的業務帳號信息-------");

Set<Service> list = account.getServices();

for(Service s : list){

System.out.println(s.getId()+" "

+s.getUnixHost() +" "

+s.getOsUsername() +""

+ s.getStatus());

}

HibernateUtil.closeSession();

}

/**

 * 實例化Service對象時一起實例化關聯屬性account

 */

public static void test1(){

String hql = "from Service s " +

"join fetch s.account " +

"where s.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 2002);

Service service  = (Service)query.uniqueResult();

System.out.println("業務帳號:"+service.getId());

System.out.println("業務服務器:"+service.getUnixHost());

System.out.println("業務用戶名"+service.getOsUsername());

System.out.println("所屬帳務帳號:"+service.getAccount().getId());

System.out.println("開通身份

          證:"+service.getAccount().getIdcardNo());

System.out.println("真實姓名:"+service.getAccount().getRealName());

HibernateUtil.closeSession();

}

}

   2)級聯添加

     對主對象做添加操作時,關聯屬性的數據也做相應的添加操作。

     a.需要在hbm.xml的關聯屬性定義中添加cascade屬性,屬性值可以為

       none(默認),all,delete,save-update等。

       其中:none為默認,表示不進行級聯操作。

            save-update:級聯添加或者級聯更新。

            delete:級聯刪除

               all:包括級聯添加、刪除、更新

     b.在執行session.save()之前,將關聯數據對象添加到關聯屬性中。

<!-- 描述services屬性,采用一對多關系加載Service記錄 -->

       <set name="services" cascade="all" inverse="true">

/**

 * 級聯添加操作 同時向數據庫添加一個Account和兩個Service記錄

 */

public static void testAdd() {

// 創建一個Account

Account account = new Account();

account.setLoginName("tiggy41");

account.setLoginPasswd("123456");

account.setRealName("老虎");

account.setIdcardNo("33033234324393");

account.setTelephone("13234232");

// 創建兩個Service

Service service1 = new Service();

service1.setUnixHost("192.168.0.23");

service1.setOsUsername("hooqt1");

service1.setLoginPasswd("111111");

service1.setCostId(1);

service1.setAccount(account);

// --

Service service2 = new Service();

service2.setUnixHost("192.168.0.20");

service2.setOsUsername("hilot");

service2.setLoginPasswd("222222");

service2.setCostId(3);

service2.setAccount(account);

// 將service1和service2給account對象的services賦值

account.getServices().add(service1);

account.getServices().add(service2);

// 添加

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

session.save(account);

tx.commit();

HibernateUtil.closeSession();

}

   3)級聯刪除

     對主對象做刪除操作時,關聯屬性的數據也做相應的刪除操作。

      a.需要在hbm.xml中的關聯屬性開啟級聯刪除操作。

     在執行session.delete(obj)操作時,刪除的obj對象時利用session查詢出

        來的。注意不要使用new方式,因為不具有關聯數據

      (級聯刪除采用n+1個delete清除數據,因此關聯數據對象n過多時,不推

       薦使用,而是采用HQL語句進行刪除)

Hibernate在做級聯刪除操作時,比如刪除service表中相關5個

accountId=1010相關字段時,其在底層執行的sql語句并不是delete from 

service where accountId=1010 而是5條delete from service where  

id=?,所以。在做批量刪除時效率較低

 //批量清除Service關系表記錄

delete from Service where account.id=?

Query query = session.createQuery(hql);

     //...設置參數

query.executeUpdate();

 

/**

 * 級聯刪除操作 刪除account記錄的同時, 也刪除services屬性中關聯

   *servcie記錄關聯數據對象n過多時,不推薦使用

 */

public static void testDelete() {

Session session = HibernateUtil.getSession();

Transaction tx = session.beginTransaction();

Account account = (Account) session.load(Account.class, 2);

session.delete(account);

tx.commit();

HibernateUtil.closeSession();

}

   4)inverse屬性

     a.inverse可以控制關系字段值維護的操作由哪一方負責。默認情況下由具

      有關系的對象雙方負責。

    

     b.Hibernate在做增刪改操作時默認分成對(非關聯字段)的增刪改和關聯

      字段更新兩個步驟

     c.Hibernate中對關系的維護默認為有關系的多表共同維護,在一對多關系

     中,通常在多方中設置inverse="true",意思要多方放棄關系維護

      操作,當程序對1方對象做級聯操作時,不會再出現update維護關系字段

      的語句。這樣可以提高執行效率。

 inverse="true"在添加的時候可以省略Hibernate對關聯字段的更新

(account_Id)步驟,這樣就可以解除一方解除關系維護操作

eg:

inverse=true時的添加操作:

Hibernate: 

    insert into SERVICE

     (UNIX_HOST, COST_ID, ACCOUNT_ID, OS_USERNAME, LOGIN_PASSWD, STATUS, CREATE_DATE, PAUSE_DATE, CLOSE_DATE, ID) 

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

默認情況下的添加操作(inverse=false):

insert  into SERVICE

     (UNIX_HOST, COST_ID, ACCOUNT_ID, OS_USERNAME, LOGIN_PASSWD,        STATUS, CREATE_DATE, PAUSE_DATE, CLOSE_DATE, ID) 

    values

        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

   *** update SERVICE  set ACCOUNT_ID=? where ID=?

 

--------------------------案例-----------------------------------

ACCOUNT : 賬務賬號表(1方)

SERVICE :業務賬號表(n方)

案例1:查看賬務賬號及其包含的業務賬號信息

案例2:查看某一個業務賬號及其賬務賬號信息

 

 

17.如何利用MyEclipse根據數據庫生成實體類和映射描述文件

 1)在DB Browser中建立一個與數據庫的連接

 2)新建一個Web Project工程

 3)為工程添加Hibernate開發框架

   (導入包,添加主配置及其參數設置)

     選中工程-->右鍵-->MyEclipse-->Add Hibernate Capabilities...

  4)按MyEclipse向導添加Hibernate框架開發包,添加主配置文件,設置連接參

    數,創建一個HibernateUtil工具類  .    

    ----------生成實體類和hbm.xml-------------

  5)進入DB Browser,選中要操作的數據表,右鍵-->Hibernate Reverse 

     Engineering.按向導生成實體類和hbm.xml.

  6)向導界面1:選擇存放實體類和hbm文件的工程和package。

     選擇要生成文件:hbm.xml,pojo,dao

  7)向導界面2: 將Type Mapping選中為Hibernate Types

  8)向導界面3:點擊Finish完成

 

18.多對多關系映射

   多對多關系在數據庫中需要3張表表示。

  例如AdminInfo-->Admin_Role<--Role

  如果需要根據Admin查找Role,可以建立Admin到Role的多對多關系映射。具

  體過程如下:

   a.在Admin實體類中追加一個集合屬性,用于存儲相關聯的Role對象信息

   b.在Admin的hbm.xml中描述集合屬性映射

<set name="關系屬性" table="關系表">

   <key column="與當前類型聯的關系表字段">

   </key>

   <many-to-many  class="關聯的另一方類型"  

      column="與另一方類型關聯的關系表字段">

   </many-to-many>

 </set>

eg:

    <!-- 采用多對多關系加載roles信息 -->

        <set name="roles" order-by="ROLE_ID" table="ADMIN_ROLE">

         <!-- 指定ADMIN_ROLE表的ADMIN_ID與當前AdminInfo主鍵關聯 -->

         <key column="ADMIN_ID"></key>

         <!-- 指定ADMIN_ROLE表的ROLE_ID與Role主鍵關聯 -->

         <many-to-many class="org.tarena.entity.Role" column="ROLE_ID">

         </many-to-many>

    </set>

 

public class TestManyToMany {

/**

 *基于多對多關系映射的查詢、添加、刪除

 */

public static void main(String[] args) {

 testFindAdmin();

// testAdminAddRole();

// testAdminRemoveRole();

//testFindRole();

 testAddAdmin();

}

/**查詢具有某個角色的用戶 */

public static void testFindRole() {

Session session = HibernateSessionFactory.getSession();

Role role = (Role) session.get(Role.class, 1);

System.out.println(role.getName());

System.out.println("-----具有該角色的用戶----");

for (AdminInfo admin : role.getAdmins()) {

System.out.println(admin.getId() + " " + admin.getAdminCode());

}

HibernateSessionFactory.closeSession();

}

/**給管理員刪除某個角色*/

public static void testAdminRemoveRole() {

Session session = HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

// 分配角色

// 獲取管理員

AdminInfo admin = (AdminInfo) session.get(AdminInfo.class, 1);

// 獲取角色

Role role1 = (Role) session.get(Role.class, 10);

// 將角色對象在管理員對象的roles中移除

admin.getRoles().remove(role1);

// 更新admin

session.update(admin);

tx.commit();

HibernateSessionFactory.closeSession();

}

/** 利用多對多查詢管理員和角色信息*/

public static void testFindAdmin() {

Session session = HibernateSessionFactory.getSession();

AdminInfo admin = (AdminInfo) session.load(AdminInfo.class, 1);

System.out.println(admin.getAdminCode() + " " + admin.getName());

System.out.println("---具有以下角色---");

for (Role role : admin.getRoles()) {

System.out.println(role.getId() + " " + role.getName());

}

HibernateSessionFactory.closeSession();

}

/**給管理員追加角色*/

public static void testAdminAddRole() {

Session session = HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

// 分配角色

// 獲取管理員

AdminInfo admin = (AdminInfo) session.get(AdminInfo.class, 1);

// 獲取角色

Role role = (Role) session.get(Role.class, 9);

Role role1 = (Role) session.get(Role.class, 10);

// 將角色對象給管理員對象的roles賦值

admin.getRoles().add(role);

admin.getRoles().add(role1);

// 更新admin對象

session.update(admin);

tx.commit();

HibernateSessionFactory.closeSession();

}

/**給管理員添加角色*/

public static void testAddAdmin(){

Session session = HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加管理員

AdminInfo admin = new AdminInfo();

admin.setAdminCode("Scofield");

admin.setName("斯科菲爾德");

admin.setPassword("12364");

admin.setEnrolldate(new Date(System.currentTimeMillis()));

//獲取角色

Role role1 = (Role) session.get(Role.class, 2);

Role role2 = (Role) session.get(Role.class, 7);

// 將角色對象給管理員對象的roles賦值

admin.getRoles().add(role1);

admin.getRoles().add(role2);

//給管理員添加角色

session.save(admin);

tx.commit();

HibernateSessionFactory.closeSession();

}

}

 Hibernate經典總結

19.繼承關系映射

   a.父類一張表,每個子類一個表,主鍵對等

   b.可以采用<joined-subclass>進行繼承關系映射,具體如下

    --在子類追加extends 父類

    --在子類hbm.xml中定義

 <joined-subclass name="子類類型" extends="父類類型" table="子類表">

   <key column="子類哪個字段與父類關聯">

   </key>

   //子類中屬性的property映射

</joined-subclass>

     eg:參見Hibernate05_1

hbm.xml:

<hibernate-mapping>

    <joined-subclass name="org.tarena.entity.Book" table="BOOK"  

        extends="org.tarena.entity.Product" schema="SYSTEM">        

         <!-- 指定父子之間的關聯條件,寫子類的關聯字段 -->

         <key column="ID"></key>     

         <!-- 由于映射類型為繼承類型,所以把子類的主鍵映射描述去掉 -->

        <!-- 

        <id name="id" type="integer">

            <column name="ID" precision="5" scale="0" />

            <generator class="assigned" />

        </id>

         -->       

        <property name="author" type="string">

            <column name="AUTHOR" length="20" />

        </property>

        <property name="publishing" type="string">

            <column name="PUBLISHING" length="50" />

        </property>

        <property name="wordNumber" type="string">

            <column name="WORD_NUMBER" length="20" />

        </property>

        <property name="totalPage" type="string">

            <column name="TOTAL_PAGE" length="20" />

        </property>

    </joined-subclass>

</hibernate-mapping>

test.java:

public class TestExtends {

/**

 * 多對多關系映射:

 */

public static void main(String[] args) {

addBook1();

//addBook2();

//findBook();

//deleteBook();

//addCar();

//findAllBook();

}

public static void findAllBook(){

String hql = "from Book";

Session session = 

HibernateSessionFactory.getSession();

Query query = session.createQuery(hql);

List<Product> list = query.list();

for(Product p:list){

System.out.println(p.getId()+" "+p.getName()+" "+p.getPrice());

}

HibernateSessionFactory.closeSession();

}

public static void addCar(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加汽車

Car car = new Car();

car.setName("Q5");

car.setPrice(150000);

car.setProductPic("4.jpg");

car.setType("J");

car.setBrand("寶馬");

car.setColor("紅色");

car.setDisplacement("2.0排量");

session.save(car);//保存

tx.commit();

HibernateSessionFactory.closeSession();

}

public static void deleteBook(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

Book book = (Book)session.load(Book.class, 3);

session.delete(book);

tx.commit();

HibernateSessionFactory.closeSession();

}

public static void findBook(){

Session session = 

HibernateSessionFactory.getSession();

Book book = (Book)session.load(Book.class, 1);

System.out.println(book.getName()+" "+book.getPrice());

System.out.println(book.getAuthor()+" "+book.getPublishing());

HibernateSessionFactory.closeSession();

}

/** 基于繼承關系映射添加*/

public static void addBook2(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加圖書

Book book = new Book();

book.setName("Struts2框架使用");

book.setPrice(90);

book.setProductPic("2.jpg");

book.setAuthor("張三的書");

book.setPublishing("菜鳥出版社");

book.setTotalPage("100");

book.setWordNumber("100個字");

session.save(book);//添加圖書

tx.commit();

HibernateSessionFactory.closeSession();

}

/** 沒有關系映射添加圖書*/

public static void addBook1(){

Session session = 

HibernateSessionFactory.getSession();

Transaction tx = session.beginTransaction();

//添加圖書

//product

Product pro = new Product();

pro.setName("Java語言基礎");

pro.setPrice(100);

pro.setProductPic("1.jpg");

session.save(pro);//保存product信息

//book

Book book = new Book();

book.setId(pro.getId());

book.setAuthor("我寫的");

book.setPublishing("我家出版社");

book.setWordNumber("10個字");

book.setTotalPage("2");

session.save(book);//保存book信息

tx.commit();

HibernateSessionFactory.closeSession();

}

}

 

######示例表Oracle######

CREATE TABLE PRODUCT

(

ID  NUMBER(5) CONSTRAINT PRODUCT_ID_PK PRIMARY KEY,

NAME  VARCHAR2(20), 

PRICE  NUMBER(15,2), 

PRODUCT_PIC  VARCHAR2(100)

);

 

CREATE SEQUENCE product_seq;

 

CREATE TABLE BOOK

(

ID  NUMBER(5) CONSTRAINT BOOK_ID_PK PRIMARY KEY,

AUTHOR  VARCHAR2(20), 

PUBLISHING  VARCHAR2(50), 

WORD_NUMBER VARCHAR2(20), 

TOTAL_PAGE VARCHAR2(20)

);

 

CREATE TABLE CAR

(

ID  NUMBER(5) CONSTRAINT CAR_ID_PK PRIMARY KEY,

BRAND VARCHAR2(20), 

TYPE VARCHAR2(1),

COLOR  VARCHAR2(50), 

DISPLACEMENT VARCHAR2(20)

);

##############################

20、Hibernate查詢

  *a.HQL查詢

    Hibernate Query Language

    HQL與SQL語句結構相似,SQL語句是面向數據表和字段進行查詢,而HQL

    語句是面向Hibernate映射過來的對象進行查詢,因此HQL被稱為是

    一種面向對象查詢語言

    HQL和SQL共同點:

      --都支持select,from,where,order by,having,group by等子句。

      --都支持運算符表達式,例如+,-,*,/,>,<等

      --都支持in,not in,between and,like等過濾條件關鍵字

      --都支持分組函max,min,sum,avg,count

    HQL和SQL不同點:

      --HQL是大小寫敏感的,類名和屬性名嚴格區分大小寫

      --HQL不支持select * 寫法

      --HQL不支持join...on...中的on子句,因為join...on發生在多表操作,而

         Hibernate中對于有關系的多張表已將將關聯映射寫在了xxx.hbm.xml

         中,在查詢時會自動加上join..on

      --HQL不支持表名和字段名

 

 HQL案例:

--查詢所有:

from Account

/**查詢所有的Account select * from account */

public static void test1() {

 String hql = "from Account"//也可寫成:String hql = "select a from 

                                  Account a";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

                            a.getIdcardNo());

}

HibernateUtil.closeSession();

     }

--參數查詢:

from Account where status=?

    也可以寫成:from Account where status=:s

   eg1:使用默認參數:?

   /** 測試帶參數的查詢*/

public static void test3() {

// 查詢所有status=0的account記錄

// String hql = "from Account where status=?";

// 查詢status=1 并且 realnameguo

String hql = "from Account " + "where status=? and realName like ?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

// 設置?參數

query.setString(0, "1");

query.setString(1, "guo%");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

+ a.getIdcardNo());

 }

HibernateUtil.closeSession();

}

       eg2:使用Hibernate中的參數: :Xxx

/**測試帶參數的查詢*/

public static void test4() {

// 查詢status=1 并且 realnameguo

String hql = "from Account " + "where status=:s and realName like :r";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

// 設置:s和:r參數

query.setString("r""guo%");

query.setString("s""1");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

+ a.getIdcardNo());

}

HibernateUtil.closeSession();

}

--查詢部分字段:

    select id,osUsername from Service

    返回List<Object[]>

    select new Service(id,osUsername) from Service

    返回List<Service>

注意:查詢部分字段,也包括select id,osUsername,.......(Account中的

      所有字段),情況;非部分字段查詢只限于”from account ”。

    eg1:返回List<Object[]>

   /** 測試查詢部分字段值 查詢部分字段,hibernate會利用Object[]封裝一條記

       錄信息*/

public static void test5() {

String hql = "select id,osUsername,unixHost "

"from Service where account.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 1011);

// 部分字段用Object[]封裝一行結果

List<Object[]> list = query.list();

for (Object[] objs : list) {

System.out.println(objs[0] + " " + objs[1] + " " + objs[2]);

}

HibernateUtil.closeSession();

}

 

  eg2:返回List<Service>

     要先在Service實體類中添加如下構造器:

public Service(){}

public Service(int id,String osUsername,String unixHost){

this.id = id;

this.osUsername = osUsername;

this.unixHost = unixHost;

}

   /** 測試查詢部分字段值查詢部分字段,通過追加構造器使hibernate利用

 * List<Service>封裝一條記錄信息*/

public static void test6() {

// 需要在Service類中添加相應的構造方法

String hql = "select " + "new Service(id,osUsername,unixHost) "

"from Service where account.id=?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

query.setInteger(0, 1011);

// 部分字段用Service封裝一行結果

List<Service> list = query.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getOsUsername() + " "

+ s.getUnixHost());

}

HibernateUtil.closeSession();

}

--在hbm.xml中定義hql

   Query query =  session.getNamedQuery("標識符")

eg:

  先在Service.hbm.xml中加入如下配置:(與class平級)

  <!-- 在TestHQL.java中被讀取用于查詢語句 -->

          <query name="findAll"><![CDATA[ from Account  ]]></query>

 

   /**在hbm.xml中定義hql*/

public static void test2() {

// String hql = "from Account";

Session session = HibernateUtil.getSession();

// Query query = session.createQuery(hql);

// 獲取hbm.xml定義的hql

Query query = session.getNamedQuery("findAll");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName() + " "

+ a.getIdcardNo());

 }

HibernateUtil.closeSession();

}

 

--分頁查詢用法

    query.setFirstResult(抓取起點從0開始計算)

    query.setMaxResult(抓取最大數量);

    List list = query.list();

/** 分頁查詢 */

public static void test7() {

Session session = HibernateUtil.getSession();

Query query = session.getNamedQuery("findAll");

// 設置分頁抓取參數,從第1條抓,最多抓取3個

query.setFirstResult(0);// 設置抓取起點(從0開始)

query.setMaxResults(3);// 設置最大抓取數量

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName());

 }

HibernateUtil.closeSession();

}

 

  b.Criteria查詢QBC

只需了解,具體參考示例和Hibernate幫助文檔

public static void test2() {

// 按status=0并且accountId=1011查詢Service

Session session = HibernateUtil.getSession();

Criteria c = // from Service

session.createCriteria(Service.class);

// 添加查詢條件

c.add(Restrictions.and(Restrictions.eq("status""0"), Restrictions.eq(

"account.id", 1011)));

// 執行查詢

List<Service> list = c.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getOsUsername());

}

HibernateUtil.closeSession();

}

 

public static void test1() {

Session session = HibernateUtil.getSession();

// 設置查詢的數據源

Criteria c = session.createCriteria(Account.class);

// 執行查詢

c.setFirstResult(0);

c.setMaxResults(5);

List<Account> list = c.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName());

}

HibernateUtil.closeSession();

}

  c.Native SQL查詢

只需了解,具體參考示例和Hibernate幫助文檔

/**

 * 默認采用Object[]封裝一行結果

 */

public static void test1() {

String sql = "select id,unix_host,os_username from SERVICE";

Session session = HibernateUtil.getSession();

SQLQuery query = session.createSQLQuery(sql);

// 默認采用Object[]封裝一行記錄

List<Object[]> list = query.list();

for (Object[] objs : list) {

System.out.println(objs[0] + " " + objs[1] + " " + objs[2]);

}

HibernateUtil.closeSession();

}

/**

 * 利用指定的類型封裝記錄結果

 */

public static void test2() {

String sql = "select * from SERVICE";

Session session = HibernateUtil.getSession();

SQLQuery query = session.createSQLQuery(sql);

// 指定采用哪種類型將一行記錄封裝,僅限于單表使用

query.addEntity(Service.class);

List<Service> list = query.list();

for (Service s : list) {

System.out.println(s.getId() + " " + s.getOsUsername());

}

HibernateUtil.closeSession();

}

 

21.Hibernate高級特性

 1)二級緩存技術

    SessionFactory級別的緩存,受SessionFactory管理,可以被不同

    Session訪問和操作。默認是關閉。一般在使用時需要利用

    SessionFactory.evict()等方法顯式的管理該緩存。

二級緩存和一級緩存的區別:

Hibernate提供了二級緩存機制。

首先,Hibernate中的一級緩存機制(也叫做事務內的緩存)是與Session綁定在一起的。當一個Session開啟,一級緩存創 建;當一個 Session關閉,一級緩存銷毀。若使用一級緩存機制(Session的緩存,每個用戶線程對應一塊Session緩存)現在有5個用戶(5 個線程)訪問Hibernate,那么Hibernate會為5個用戶創建5個不同的Session(一個線程分配一個Session)。


假設用戶1調用getId("1")方法查找id=1的Emp對象,Session會首先查找內部有沒有id=1的Emp對象,如果有,則返回給用戶;沒有則去數據庫中查找,并保存到該Session中,當用戶第二次訪問時,就不用去數據庫中取數據了。

 

一級緩存提高了效率,減少了訪問數據庫的壓力。如果5個用戶都調用getId("1")方法查找id=1的Emp對象,那么在這5個session中就分別保存著5個id=1的Emp對象,這樣顯然重復。

 

由此,我們引入了二級緩存機制(SessionFactory中的緩存,同一個項目中只有一份,所有用戶共用)當用戶1第一次調用getId("1")方法時,會到數據庫中查找出Emp對象,保存到一級緩存中的同時,也在二級緩存中保存一份。這樣,當其他用戶也需要id=1的Emp對象時,只需要到二級緩存中查找即可,就不用連接到數據庫了。

 

 

一級緩存是用戶線程專用的,二級緩存是大家共用的。

我們通過配置一些現成的緩存組件(如ehcache)來實現,同時我們還可以控制哪些對象需要放入二級緩存,哪些對象不需要做二級緩存。

 

   a.什么情況可以考慮使用二級緩存

     --該對象被多個不同用戶頻繁使用

     --該對象更新操作不頻繁

   b.如何使用二級緩存

     --添加ehcache.jar開發包和src/ehcache.xml配置

     --在hibernate.cfg.xml中開啟二級緩存,指定采用哪種二級

       緩存組件

    hibernate.cfg.xml:

<!-- 指定二級緩存組件 -->

  <property name="hibernate.cache.provider_class">

net.sf.ehcache.hibernate.EhCacheProvider

  </property>

       ehcache.xml:

     <!-- 

b.eternal表示是否設置這些放入二級緩存的數據對象為永久的

         (即放入即保存,不再清除)一般都為false

c.timeToIdleSeconds=120表示如果120秒內,放入的對象沒有被

          再次訪問到,就清除出去

d.timeToLiveSeconds=120表示對象在緩存中存活的時間,一個

           對象進入到本緩存中120秒后,就會自動被清除(一般 設置

           的時間會比timeToIdleSeconds時間長),設置此屬性是為了

           讓更多活躍的對象進入到緩存中來。

e.overflowToDisk="true"表示如果活躍對象已經超出

          maxElementInMemory設置的最大值時,

  超出的對象要被寫入到硬盤上保存下來,用于緩解活躍用戶較

          多的情況。

        -->

    <defaultCache

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="30"

        timeToLiveSeconds="240"

        overflowToDisk="true"

                      />Hibernate經典總結

<cache name="sampleCache1"

        maxElementsInMemory="10000"

        eternal="false"

        timeToIdleSeconds="300"

        timeToLiveSeconds="600"

        overflowToDisk="true"

  />

     --需要緩存哪個對象,就在hbm.xml中添加<cache>元素配

       置。

      <cache usage="read-only或read-write"

        region="采用ehcache.xml哪組參數緩存該對象"/>

Account.hbm.xml:

<!-- 指定采用二級緩存緩存Account對象 -->

    <cache usage="read-only" region="sampleCache1"/>

    注:上面的配置要寫在配置文件的頂端

其中:

region屬性表示指定使用哪個二級緩存。

usage屬性表示二級緩存的使用方式。有兩種:read-only

和read-write、read-only 表示如果值為read-only,那么就

不能修改,這樣ehcache就不用考慮修改和更新的操作。

read-write 設置為read-write,ehcache還需要考慮更新和

修改,這樣會降低效率。

所以,設置usage屬性是很重要的,需要根據實際情況判

斷存入的對象使用二級緩存的方式。

     c.二級緩存管理

         sessionFactory.evict方法

 

eg:

public class TestSecondCache {

/**

 * 二級緩存

 */

public static void main(String[] args) {

findAccount();

findAccount();

}

/**

 * 為使用二級緩存時,兩次調用findAccount()方法,會執行兩次查詢

 * 使用二級緩存后,兩次調用findAccount()方法,只執行一次查詢

 * */

public static void findAccount() {

Session session = HibernateUtil.getSession();

Account account = (Account) session.get(Account.class, 1011);

System.out.println(account.getRealName() + " " + 

          account.getIdcardNo());

HibernateUtil.closeSession();

}

}

 2)查詢緩存技術

查詢緩存基于二級緩存

二級緩存只保存單個對象,而不會管諸如取字符串、取數組、取集合等的

操作,但是查詢緩存支持這些。

    a.查詢緩存的使用

       --要對查詢的目標對象開啟二級緩存

  <!-- 設置開啟二級緩存 -->

<property name="hibernate.cache.use_second_level_cache">true

      </property>

<!-- 指定二級緩存組件 -->

<property name="hibernate.cache.provider_class">

net.sf.ehcache.hibernate.EhCacheProvider

 </property>

       --在hibernate.cfg.xml中開啟查詢緩存設置

<!-- 開啟查詢緩存 -->

<property name="hibernate.cache.use_query_cache">

true

</property>

       --在執行query.list()查詢之前,

         調用query.setCacheable(true);

    b.適合使用查詢緩存的情況

       --不同用戶都執行相同的SQL查詢和相同結果

       --查詢結果集不發生改變

    

   注意:在二級緩存和查詢緩存使用關聯映射時,關系屬性數據

         默認不參與緩存,如果需要緩存關聯屬性數據,需要

         對關聯屬性和hbm.xml(Account.hbm.xml和

         Service.hbm.xml)都設置<cache>元素

    查詢緩存是在二級緩存的基礎之上的,所以關于二級緩

    存的在hibernate.cfg.xml中的配置要保留。

   在Account.hbm.xml和Service.hbm.xml中均添加:

 <!--指定該關聯屬性也參與緩存操作,

  在需要查詢緩存時才需要添加-->

 <cache usage="read-only" region="sampleCache1"/>

  

   eg:

public class TestQueryCache {

/**

 * 查詢緩存

 */

public static void main(String[] args) {

findAll();

System.out.println("-----------");

findAll();

}

public static void findAll() {

String hql = "select distinct a " + "from Account a "

"left outer join fetch a.services where a.realName 

                     like ?";

Session session = HibernateUtil.getSession();

Query query = session.createQuery(hql);

// 設置采用查詢緩存機制

query.setCacheable(true);

query.setString(0, "guo%");

List<Account> list = query.list();

for (Account a : list) {

System.out.println(a.getId() + " " + a.getRealName());

// 業務賬號信息

for (Service s : a.getServices()) {

System.out.println("---" + s.getId() + " " + 

                s.getOsUsername());

}

}

HibernateUtil.closeSession();

}

}

 

             hibernate緩存機制總結:

Hibernate中的一級緩存機制(也叫做事務內的緩存)是與Session綁定在一起的。

當一個Session開啟,一級緩存創建;當一個Session關閉,一級緩存銷毀。

Hibernate中的二級緩存特點如下:

a.二級緩存被稱為SessionFactory級別緩存. 生命周期與SessionFactory對象相關

b. 二級緩存空間可以被不同的Session對象訪問 共享.

c. 二級緩存默認是關閉狀態.如果遇到某個數據對象被多個不同的Session頻繁訪 問,可以開啟.

Hibernate中的查詢緩存:

前面的一級和二級緩存, 緩存的時load,get出來的數據對象.不能緩存一個結果集.查詢緩存可以緩存查詢語句和結果集, 當重復執, 同一個查詢語句時,只取數據庫查詢一次,后續都是將緩存中的結果集取出。適用于頻繁的執行同一個查詢語句,而且查詢結果集很少發生變化的情況。

 

3)悲觀鎖和樂觀鎖

 當出現多個用戶同時執行更新等操作時,會出現事務交叉更新操作的沖突,會破

 壞業務和數據的完整性。可以使用悲觀鎖和樂觀鎖解決這類問題。

 悲觀鎖:

 a.悲觀鎖機制:在進行數據查詢時追加一個鎖機制,進行業務操作,此時其他用

               戶不能進行增刪改操作,在事務結束時會自動將鎖釋放,其他用

               戶可以繼續執行此類操作。

悲觀鎖的處理方式是當前線程的事務沒有結束前,其它事務都要

等著(事務串行化)。

  悲觀鎖特點:將用戶操作一個一個處理,可以解決更新并發問題,缺點是處理效

              率比較低。

   Hibernate悲觀鎖機制一般是借助于數據庫鎖機制。

   eg:

 Train train = 

(Train)session.load(Train.class, 1,LockMode.UPGRADE);

 

  樂觀鎖:

     b.樂觀鎖機制:多個不同用戶都可以同時對數據庫記錄進行查看和更新操作,

       但是最先commit提交的用戶會執行成功,后續用戶會以異常形  式提示失敗。

         樂觀鎖是借助于一個版本字段進行控制,當并發操作中一個用  戶成功提交了,版本字段值會自動加1,后續提交的對象版本信

                 息小于數據庫版本字段值會被hibernate阻止掉。

     樂觀鎖實現原理:

使用樂觀鎖,我們需要做數據庫更改,為數據庫增加一個字段version(版本號),當用戶讀取數據時,會將版本號version 一同讀出,如果該用戶修改了數據,會先將取出的版本號與數據庫中的版本號做對比,如果相同,才能修改;修改完成后,會將版本號version+1如果不相 同,則不能修改,會拋出異常。

如果不使用框架技術,那么我們需要手工做對比,使用Hibernate框架后,Hibernate可以幫助我們做version對比的操作。

 

       樂觀鎖特點:允許多個用戶同時操作,處理效率相對較高。

      樂觀所使用步驟:

        --將原有數據表追加一列版本字段,初始值0

        --在實體類中添加版本屬性

        --在映射描述文件中采用<version>元素定義版本屬性和版本字段的映射

<!--optimistic-lock 指定采用版本字段形式實現樂觀鎖控制-->

<hibernate-mapping>

<class name="org.tarena.netctoss.entity.Train" 

      optimistic-lock="version" table="TRAIN">

<id name="id" type="integer">

<column name="T_ID" precision="22" scale="0" />

<generator class="assigned" />

</id>

<!-- 描述版本字段的映射 -->

<version name="version" type="integer">

<column name="T_VERSION"></column>

</version>

<property name="value" type="integer">

<column name="T_VALUE" precision="22" scale="0" />

</property>

</class>

</hibernate-mapping>

        --當發生多個事務并行交叉執行時,第一個提交的成功,后續提交的會拋

          出異常org.hibernate.StaleObjectStateException。可以異常捕

         獲給用戶一個友善的提示。

   

==============示例表=================

create table train(

t_id number primary key,

t_value number,

t_version number);

insert into train values (1,100,0);

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!