1. 程式人生 > >hibernate sessionFactory(轉)

hibernate sessionFactory(轉)

hibernate中的SessionFactory,Session,configuration

Session介面
  Session介面對於Hibernate   開發人員來說是一個最重要的介面。然而在Hibernate中,例項化的Session是一個輕量級的類,建立和銷燬它都不會佔用很多資源。這在實際專案中確實很重要,因為在客戶程式中,可能會不斷地建立以及銷燬Session物件,如果Session的開銷太大,會給系統帶來不良影響。但值得注意的是Session物件是非執行緒安全的,因此在你的設計中,最好是一個執行緒只建立一個Session物件。

    在Hibernate的設計者的頭腦中,他們將

session看作介於資料連線與事務管理一種中間介面。我們可以將session想象成一個持久物件的緩衝區,Hibernate能檢測到這些持久物件的改變,並及時重新整理資料庫。我們有時也稱Session是一個持久層管理器,因為它包含這一些持久層相關的操作,諸如儲存持久物件至資料庫,以及從資料庫從獲得它們。請注意,Hibernate   session不同於JSP應用中的HttpSession。當我們使用session這個術語時,我們指的是Hibernate中的session,而我們以後會將HttpSesion物件稱為使用者session
   
  SessionFactory   介面

   
    這裡用到了一個設計模式――工廠模式,使用者程式從工廠類SessionFactory中取得Session的例項。
   
    令你感到奇怪的是SessionFactory並不是輕量級的!實際上它的設計者的意圖是讓它能在整個應用中共享。典型地來說,一個專案通常只需要一個SessionFactory就夠了,但是當你的專案要操作多個數據庫時,那你必須為每個資料庫指定一個SessionFactory
  SessionFactoryHibernate中實際起到了一個緩衝區的作用,它緩衝了Hibernate自動生成的SQL語句和一些其它的對映資料,還緩衝了一些將來有可能重複利用的資料。

   
  Configuration   介面
   
  Configuration介面的作用是對Hibernate進行配置,以及對它進行啟動。在Hibernate的啟動過程中,Configuration類的例項首先定位對映文件的位置,讀取這些配置,然後建立一個SessionFactory物件。
   
    雖然Configuration介面在整個Hibernate專案中只扮演著一個很小的角色,但它是啟動hibernate時你所遇到的每一個物件。
   
  Transaction   介面
   
  Transaction介面是一個可選的API,你可以選擇不使用這個介面,取而代之的是Hibernate的設計者自己寫的底層事務處理程式碼。   Transaction介面是對實際事務實現的一個抽象,這些實現包括JDBC的事務、JTA中的UserTransaction、甚至可以是CORBA事務。之所以這樣設計是能讓開發者能夠使用一個統一事務的操作介面,使得自己的專案可以在不同的環境和容器之間方便地移值。
   
  QueryCriteria介面
   
  Query介面讓你方便地對資料庫及持久物件進行查詢,它可以有兩種表達方式:HQL語言或本地資料庫的SQL語句。Query經常被用來繫結查詢引數、限制查詢記錄數量,並最終執行查詢操作。
   
  Criteria介面與Query介面非常類似,它允許你建立並執行面向物件的標準化查詢。
   
    值得注意的是Query介面也是輕量級的,它不能在Session之外使用。
   
  Callback   介面
   
    當一些有用的事件發生時――例如持久物件的載入、儲存、刪除時,Callback介面會通知Hibernate去接收一個通知訊息。一般而言,Callback介面在使用者程式中並不是必須的,但你要在你的專案中建立審計日誌時,你可能會用到它。

使用SessionFactory和Session:

整個程式中只能有一個SessionFactory例項,每個執行緒中只能有一個Session(使用TreadLocal)
SessionFactory裡面有許多的Map,每個Map都存了許多的東西,佔用記憶體較大

注意Hibernate3中SessionFactory提供了一個getCurrentSession方法避免了執行緒間session的衝突問題。
也就是執行語句:session=factory.getCurrentSession();

注意要想執行上述語句的話必須首先在配置檔案中的session-factory元素中加入:
<property name="current_session_context_class">thread</property>

或者在屬性檔案中加入:hibernate.current_session_context_class thread

配置檔案和對映檔案;SessionFactorySession的使用注意事項

配置檔案
1 使用XML :
呼叫Configuration.configure()方法,如果不傳入引數的話,必須用預設名稱hibernate.cfg.xml
傳入引數的話就可以用別的名稱了

2 使用properties檔案:
檔名只能是hibernate.properties,放在工程目錄下,不用呼叫configure()方法,如果呼叫了的話,
xml配置資訊將會覆蓋屬性檔案中配置好的資訊。

對映檔案
1 直接在hibernate.cfg.xml中,以mapping元素形式加入對映檔案(如果用的是屬性檔案的話就沒戲了)
2 addClass,addFile,addResource方法匯入對映檔案,注意如果使用addClass的話,匯入的是與類同名的、
並以hbm.xml結尾的檔案。

--------------------
使用SessionFactory和Session:

整個程式中只能有一個SessionFactory例項,每個執行緒中只能有一個Session(使用TreadLocal)
SessionFactory裡面有許多的Map,每個Map都存了許多的東西,佔用記憶體較大

注意Hibernate3中SessionFactory提供了一個getCurrentSession方法避免了執行緒間session的衝突問題。
也就是執行語句:session=factory.getCurrentSession();

注意要想執行上述語句的話必須首先在配置檔案中的session-factory元素中加入:
<property name="current_session_context_class">thread</property>
或者在屬性檔案中加入:hibernate.current_session_context_class thread
也是可以的。
---------------------
1 知道如何在不使用預設檔名時,也能配置Hibernate。知道SessionFactory和Session在程式設計的時候需要注意什麼問題?
2 物件的三種狀態分別是什麼?含義?
3 update方法和merge方法的區別,應用場合?
4 saveOrUpdate方法應用場合?
5 主鍵生成器什麼意思?為什麼實體一定要有識別符號?如果資料表中沒有主鍵的話,能否對映物件???
6 HQL,Hibernate Query Language 要求必須非常熟練的掌握。

-----------------------------
一次會話狀態中,持久化物件經歷以下三種狀態:
1 transient:物件不與資料庫中任意資料相關聯。(也就是主鍵上面沒有值)
2 persistent:物件與資料庫中的資料關聯,包含持久標識,Hibernate保證物件持久標識與資料庫主鍵一致。
3 detached(脫管、遊離狀態)

session = factory.getCurrentSession();
session.beginTransaction();
User u = new User();//u相對於session沒有發生過任何關係,是臨時狀態的
u.setId(3);        //現在u設定了主鍵,因此是脫管狀態的(transient)

session.save(u); //將與事件關聯的物件快取到與當前Session相對應的PersistenceContext物件中,
      //Session.flush()會生成一個flush()事件,從而寫資料庫。
      //save(u)也就是讓u快取到session中成為一個持久狀態
u.setUsername("a");
session.getTransaction().commit();
u.setPassword("bb"); //事務一旦commit,u已經不存在於session的快取裡面,這樣再對u進行的操作就不會影響資料庫了
       //這時u是脫管或是遊離狀態。所謂脫管就是和資料庫沒有關係了。
       //想讓它再和session發生關係的話,就再建立一個session
注意:如果使用的是getCurrentSession來建立session的話,在commit後,session就自動被關閉了,
也就是不用再session.close()了。
但是如果使用的是openSession方法建立的session的話,那麼必須顯示的關閉session,也就是
呼叫session.close()方法。這樣commit後,session並沒有關閉,物件還是持久狀態,而非脫管狀態。
除非呼叫了session.close(),才是脫管狀態。

session = factory.getCurrentSession();
session.beginTransaction();
session.saveOrUpdate(u);
session.getTransaction().commit();

//不論是脫管狀態還是臨時狀態,save一定作插入,update則一定作更新!
//使用saveOrUpdate方法的話,如果物件處於臨時狀態的話會進行插入,如果是脫管狀態的話會更新。

//如果我把一個脫管的物件u.setId(0)的話,由於id是主鍵,是整型,那麼被置為0後就會被認為沒有主鍵了,
//既然沒有了主鍵那就是臨時狀態,而不再是脫管狀態了

//就是說,如果物件裡面的整型主鍵是0的話,且和session沒有關係,那麼就是臨時狀態,如果是非0值的話,
//就是脫管狀態!

//在對映檔案中的id元素中有一個屬性叫做unsaved-value="",如果設定了是整數5的話,那麼主鍵設定為5
//會被預設為沒有主鍵,呼叫saveOrUpdate方法的話肯定是去插入而不是去更新

saveOrUpdate執行的虛擬碼:
if(obj.getId()==unsavedValue)
{
save(obj);
}
else
{
update(obj);
}

session的save,persist,load,update,get等方法都是把臨時狀態轉換為一個持久狀態,將物件
和session相關聯。

注意:在持久狀態中,對物件的任何操作都會先寫快取,在commit後這些操作會反映到資料庫。

save無論物件是否處於臨時狀態都會向資料庫中儲存的,但是persisit只會去持久化那些處於臨時狀態中
的物件(那些沒有主鍵的物件,也就是被設定成unsaved-value值主鍵的物件)

臨時狀態的最大特點是主鍵值與unsaved-value值相同!!
------------------------------------------
程式碼片斷1:

//臨時狀態
    //最大特點是ID與unsaved-value的值相同。
    User u = new User();
  
  
    session = factory.openSession();
    session.beginTransaction();
  
    //持久狀態
    /*
    * 因為u的ID值為0,而在對映檔案中沒有指定unsaved-value值,所以預設為0
    * 程式在執行saveOrUpdate時,會判斷ID值是否與unsaved-value相同,如果
    * 相同則執行save
    */
    session.saveOrUpdate(u);
    u.setUsername("a");
  
    session.getTransaction().commit();

   注意:session.saveOrUpdate(u)這條語句://如果執行的是save則立刻執行,如果是update則到flush時才執行
還有一點,session.save(Object)方法返回的值是主鍵的值!
----------------------------------------
session.beginTransaction();
session.get(User.class,new Integer(u.getId()));
session.update(u); //注意上面那句話已經讓session中快取了物件u,如果再次進行update的操作的話
      //會出現錯誤!!如果不想錯誤發生的話,應該把update方法改為merge來進行更新!
session.getTransaction.commit();

----------------------------------------
程式碼片斷2:
//臨時狀態
    //最大特點是ID與unsaved-value的值相同。
    User u = new User();
  
    session = factory.openSession();
    session.beginTransaction();
  
    //持久狀態
    /*
    * 因為u的ID值為5,而在對映檔案中沒有指定unsaved-value值,所以預設為0
    * 程式在執行saveOrUpdate時,會判斷ID值是否與unsaved-value相同,如果
    * 不相同則執行update
    */
  
    session.saveOrUpdate(u);//如果執行的是save則立刻執行,如果是update則到flush時才執行
  
    u.setUsername("a");
  
    session.getTransaction().commit();
  
    //使用session.openSession在事務提交不會關閉session,
    //所以u處於持久狀態(只有session關閉之後,u才會變為脫管狀態)
    u.setPassword("bb");
    session.beginTransaction();
    session.getTransaction().commit();
    session.close();
  
    //脫管狀態(遊離)detached
    u.setUsername("cccc");
  
    session = factory.getCurrentSession();
    session.beginTransaction();
    session.get(User.class, new Integer(u.getId()));
    session.update(u); //這裡如果不用merge的話是沒有辦法更新的,但是這裡可以用save進行插入
    session.getTransaction().commit();
------------------------------------------------
明天是張老師講郵件伺服器的細節,具體來說是怎麼樣用程式來實現收發郵件的問題。


下午提前30分鐘上課,不知為何……
物件的三種狀態分別是什麼?含義?
1 臨時:沒有和session發生關係的,沒有主鍵的(預設情況下整型主鍵值為0,物件型主鍵值為null時)。
2 脫管:和臨時相比有了主鍵資訊,也就是主鍵值不等於unsaved-value
3 持久:和session相關物件。

只有session裡面沒有快取u時,才可以使用session.update(u)。如果session中有了一個物件和u的主鍵
已經相同了,就不允許使用session的update方法了

如果不確定這個session快取裡面是否有一個物件和u的主鍵有相同的值,那麼使用merge來覆蓋掉原來快取
中的物件,從而達到更新的目的。

saveOrUpdate方法的應用場合:不確定記錄是否要被插入到資料表中。
什麼時候執行save呢?
物件主鍵的id等於unsaved-value的時候執行save。

什麼時候執行update呢?
物件主鍵的id不等於unsaved-value的時候執行update。
--------------------------
再說說ThreadLocal,
教室裡面每個人都是一個執行緒,每個人的杯子都是一個ThreadLocal,前面有一杯水是session,還有一個
水缸是sessionFactory,如果每個人都要取水的話都得在水缸前面排隊,這就是執行緒安全。

這樣每一個執行緒都有了一個session,ThreadLocal是每個人手裡的杯子,杯子裡面都可以裝水(session)。
--------------------------
主鍵生成器是什麼意思??為什麼實體一定要有識別符號?
沒有主鍵的話還能不能對映物件了?

主鍵生成器:<generator class="identity"/>
在org.hibernate.id中包含IdentifierGenerator介面。
可以用org.hibernate.id.IdentityGenerator替換上面的identity。

其實identity是簡寫。

主鍵是不用插入的,主鍵生成器是用來把插入記錄生成的主鍵讀出來放到物件裡面的相應標識中去。
注意:select last_insert_id();如果和插入語句放在同一個事務中,那麼可以用它來得到最後一條
插入記錄的主鍵,如果它沒有和插入語句放在同一個事務中,那執行起來就沒有效果了。
-------------------------------------

下面講HQL
session = factory.getCurrentSession();//注意如果要想使用這句話的話必須
         //<property name="current_session_context_class">thread</property>
session.beginTransaction();
User u = (User)session.load(User.class,new Integer(3));
//上面這種方式的載入如果不知道主鍵的話根本沒有意義,如果我要想根據某些欄位來查詢表中的相應
//記錄集合的時候,用這種方法來載入是不行的
//弊端:只能通過主鍵來載入物件,限制了Hibernate的應用

//如果我想根據使用者名稱和密碼查詢的話,HQL語句應該如何寫呢?

//如下所示,語句hql是從類裡面找滿足條件的物件,注意user是vo.User的別名,vo是類所在的包名。
//如果我不想加入包名的話,那麼就得在對映檔案的hibernate-mapping元素中設定auto-import屬性,
//將之設定為true,就不用引入包了!
String hql = "from vo.User user where user.username=? and user.password=? "
Query q = session.createQuery(hql); //x需要匯入org.Hibernate.Query包
q.setString(0,"j2se");
q.setString(1,"1");  
List l = q.list();    //這句話也需要匯入包!
Iterator it = l.iterator();
if(it.hasNext())
{
System.out.println("找到了!");
}
else
{
System.out.println("哎,沒找到啊!");
}

session.getTransaction().commit();

//上面iterator中存的就是物件!

while(it.hasNext())
{
System.out.println(it.next().getClass());
User u = (User)it.next();
}
-----------------------------------------------------------------
使用Hibernate實現使用者登陸和修改密碼的小例子:


package practice;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class Test {

public static String readLine()
{
   String result = "";
   BufferedReader in = new BufferedReader(
        new InputStreamReader(System.in));
   try {
    result = in.readLine();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   return result ;
}

public static void main(String[] args) {

 
   System.out.println("請您輸入登陸的使用者名稱和密碼!");
   System.out.println("×××××××××××××××××××××××××××××××");
   System.out.println("請您輸入使用者名稱:");
   String username = Test.readLine();
   System.out.println("請您輸入密碼:");
   String password = Test.readLine();
 
 
   Entity entity = new Entity();
 
   SessionFactory factory =
    new Configuration().addClass(Entity.class).
     configure().buildSessionFactory();
   Session session = factory.getCurrentSession();
 
   session.beginTransaction();
 
   String hql = "from practice.Entity e where e.name=? and e.password=?";
 
   Query q = session.createQuery(hql);
   q.setString(0,username);
   q.setString(1,password);
 
   List list = q.list();
   Iterator it = list.iterator();
 
   if(it.hasNext())
   {
    System.out.println("您的輸入正確!歡迎登陸");
    entity = (Entity)it.next();
   }
   else
   {
    System.out.println("輸入的使用者名稱、密碼有誤,下次小心點!");
    System.exit(1);
   } 
   session.getTransaction().commit();//很重要!在登陸完畢後就關閉session!後面用到的時候再建立!
 
   System.out.println("*************************************");
   System.out.println("請您輸入您的新密碼:");
   String password1 = Test.readLine();
   System.out.println("請您再次輸入您的新密碼:");
   String password2 = Test.readLine();
   if(!password1.equals(password2))
   {
    System.out.println("兩次輸入的密碼不一致,下次小心點!");
    System.out.println("***************");
    System.exit(1);
   }
 
   entity.setPassword(password1); //這時候entity物件毫無疑問處於脫管狀態!
 
   session = factory.getCurrentSession();
   session.beginTransaction();
   session.update(entity);//注意這裡可以用saveOrUpdate和merge方法也可以達到同樣的效果!
   System.out.println("密碼已經成功的被更新!!!");
   session.getTransaction().commit();

}
}
---------------------------------
再次注意下面的配置語句:
<hibernate-mapping auto-import="true" package="vo">
這裡的auto-import是在hql語句中不用寫包名了,那個package則是保證在下面的配置資訊中的class的name屬性
中不必輸入包名了!

//如果不加select的話預設選取出的是整個物件,只有像下面這樣寫才能夠取出具體的屬性
Query q = session.createQuery("select username from User where id=1");
String name = (String)q.uniqueResult();
System.out.println(name);
-----------------------------------------

<property name="name" column="name"/>
其實裡面還有一個叫做type的屬性可以加上資料型別,這樣指明瞭每個欄位的資料型別的話執行起來會快一點,
要不程式還得自己去根據反射機制去查詢資料型別,挺麻煩的。

mysql中的timestamp對插入操作會相應的把時間置為當前的時間。
但是oracle中的時間戳完全不一樣

select systimestamp from dual;
會得到09-12月-06 04.26.46.312000 下午 +08.00 ,也就是說oracle中包含了很詳細的時間
甚至有毫秒、時區等資訊。

date,time, timestamp,calendar,calendar_date
string , varchar ,
integer,long,short,float,double,byte,character,boolean,true_false,yes_no

以上的這些型別都可以加入到<property name="name" column="name"/>
的type屬性中去。

---------------------
總結:

物件三種狀態,update和merge區別,saveOrUpdate應用場合,unsaved-value,HQL語句掌握
使用SessionFactory和Session注意的問題,

當然還有一個今天沒有太講清楚的主鍵生成器!

注意以下每行方法的對比、區別
save、persist、saveOrUpdate
update、merge、saveOrUpdate
load、get
load->setter、update

練習:
使用命令列方式實現使用者註冊,修改使用者註冊資訊的功能,要求實現Hibernate實現。
考慮效能問題,一定要注意及時的關閉session!

明天是張老師講JavaMail,後天是Hibernate中hql的qbe和qbc之類的東西,Hibernate進行完後
是javaScript要講一講,然後是Javaweb、Struts、Sprint、設計模式、工作流之類的東東。

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

使用命令列方式實現使用者註冊,修改使用者註冊資訊的功能,要求實現Hibernate實現。
考慮效能問題,一定要注意及時的關閉session!

晚上練習的答案:

配置檔案hibernate.cfg.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///j2ee</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>


<property name="format_sql">root</property>
<property name="current_session_context_class">thread</property>
<property name="show_sql">false</property>

<mapping resource="aw/User.xml"/>
</session-factory>

</hibernate-configuration>
**********************************************

對映檔案User.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="aw.User" table="users">
   <id name="id">
    <generator class="identity"/>
   </id>
   <property name="name"/>
   <property name="password"/>
   <property name="loginTimes"/>
   <property name="loginTime"/>
</class>

</hibernate-mapping>
**********************************************

實體類User:

package aw;

import java.sql.Timestamp;

public class User {
private int loginTimes ;
private Timestamp loginTime ;
private String name ;
private String password ;
private int id ;
public int getId() {
   return id;
}
public void setId(int id) {
   this.id = id;
}
public Timestamp getLoginTime() {
   return loginTime;
}
public void setLoginTime(Timestamp loginTime) {
   this.loginTime = loginTime;
}
public int getLoginTimes() {
   return loginTimes;
}
public void setLoginTimes(int loginTimes) {
   this.loginTimes = loginTimes;
}
public String getName() {
   return name;
}
public void setName(String name) {
   this.name = name;
}
public String getPassword() {
   return password;
}
public void setPassword(String password) {
   this.password = password;
}


}
×××××××××××××××××××××××××××××××××××××××××××××××××
源程式:

package aw;
import java.util.Iterator;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import java.io.*;
public class Aaa {

//從控制檯讀一行後返回相應的字串!
public static String readLine()
{
   try {
    return new BufferedReader(new InputStreamReader(System.in)).readLine();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    return "";
   }
}

//判斷使用者名稱是否已經存在
public static boolean existsUser(SessionFactory factory,String username)
{
   boolean result = false ;
   Session session = factory.getCurrentSession();
 
   session.beginTransaction();
 
   String hql = "from aw.User where name=?";
   Query q = session.createQuery(hql);
   q.setString(0,username);
   List list = q.list();
   Iterator it = list.iterator();
   if(it.hasNext())
    result = true ;
 
   session.getTransaction().commit();
 
   return result ;
 
}




//進行註冊處理
public static void register(User u, SessionFactory factory)
{
   boolean flag = false ;
   System.out.println("現在開始註冊:請您輸入使用者名稱");
   String username= readLine();
   if(existsUser(factory,username))
   {
    System.out.println("真是非常非常的抱歉,該使用者名稱存在了!");
    flag = true ;
    register(u,factory);
   }
   //遞規呼叫後必然要回到這裡,如果執行了遞規呼叫的話那麼下面的程式碼沒有執行
   //的意義,所以要根據flag的值決定是否返回入口點。
   if(flag==true)
    return ; //這句話非常的重要!不可丟失
   //下面是正常的註冊邏輯!
   System.out.println("使用者名稱輸入成功,您再輸入一下密碼吧:");
   String password = readLine();
   //下面開始插入資料庫!!
 
   u.setName(username);
   u.setPassword(password);
   Session session = factory.getCurrentSession();
   session.beginTransaction();
    session.save(u);
 
   System.out.println("您的註冊已經完成了!");
   session.getTransaction().commit();
   return;
}

//修改密碼處理:
public static void modify(User u, SessionFactory factory)
{
   System.out.println("請您輸入新密碼");
   String password = readLine();
 
   u.setPassword(password);
   Session session = factory.getCurrentSession();
   session.beginTransaction();
    session.update(u);
    System.out.println("密碼修改完畢!!");
   session.getTransaction().commit();
}

public static void main(String[] args)
{
   SessionFactory factory = new Configuration().configure()
         .buildSessionFactory();
   User u = new User();
   System.out.println("*****************************");
   register(u,factory);
   System.out.println("*****************************");
   System.out.println("請您輸入新的密碼:");
 
   modify(u,factory);
}
}