閱讀952 返回首頁    go 技術社區[雲棲]


[Hibernate開發之路](1)Hibernate配置

一 準備工作

首先我們將創建一個簡單的基於控製台的(console-based)Hibernate應用程序。

我們所做的第一件事就是創建我們的開發目錄,並且把所有需要用到的Java庫文件放進去。解壓縮從Hibernate網站下載的Hibernate發布包,並把所有需要的庫文件拷到我們項目中去。
學習建User-library-hibernate,並加入相應的jar包

(a)項目右鍵-buildpath-configure build path-add library


(b)選擇User-library,在其中新建 hibernate,命名為HibernateLibraray

(c)在該library中加入hibernate所需jar包


到編寫本文時為止,這些是Hibernate運行所需要的最小庫文件集合(注意我們也拷貝了 Hibernate3.jar,這個是最主要的文件)。
你正使用的Hibernate版本可能需要比這更多或少一些的庫文件。請參見發布包中的lib/目錄下的README.txt,以獲取更多關於所需和可選的第三方庫文件信息(事實上,Log4j並不是必須的庫文件,但被許多開發者所喜歡)。

(d) 引入sqlserver的JDBC驅動包 

在sqlserver中創建表StudentInfo



接下來我們創建一個類,用來代表那些我們希望儲存在數據庫裏的student。


二 持久化類

我們的第一個持久化類是一個帶有一些屬性(property)的簡單JavaBean類:

package com.model;

public class StudentInfo {
	private int id;
	private String name;
	private int age;
	private String sex;
	
	//構造方法
	public StudentInfo(){
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
}

你可以看到這個類對屬性的存取方法(getter and setter method)使用了標準JavaBean命名約定,同時把類屬性(field)的訪問級別設成私有的(private)。
這是推薦的設計,但並不是必須的。Hibernate也可以直接訪問這些field,而使用訪問方法(accessor method)的好處是提供了重構時的健壯性(robustness)。
為了通過反射機製(Reflection)來實例化這個類的對象,我們需要提供一個無參的構造器(no-argument constructor)。 
對一特定的StudentInfo, id 屬性持有唯一的標識符(identifier)的值。如果我們希望使用Hibernate提供的所有特性,
那麼所有的持久化實體(persistent entity)類(這裏也包括一些次要依賴類)都需要一個這樣的標識符屬性。
而事實上,大多數應用程序(特別是web應用程序)都需要通過標識符來區別對象,所以你應該考慮使用標識符屬性而不是把它當作一種限製。
然而,我們通常不會操作對象的標識(identity),因此它的setter方法的訪問級別應該聲明private。這樣當對象被保存的時候,隻有Hibernate可以為它分配標識符值。
你可看到Hibernate可以直接訪問public,private和protected的訪問方法和field。所以選擇哪種方式完全取決於你,你可以使你的選擇與你的應用程序設計相吻合。 
所有的持久化類(persistent classes)都要求有無參的構造器,因為Hibernate必須使用Java反射機製來為你創建對象。
構造器(constructor)的訪問級別可以是private,然而當生成運行時代理(runtime proxy)的時候則要求使用至少是package 級別的訪問控製,
這樣在沒有字節碼指令(bytecode instrumentation)的情況下,從持久化類裏獲取數據會更有效率。 

下一步,我們把這個持久化類的信息告訴Hibernate。


三 映射文件

Hibernate需要知道怎樣去加載(load)和存儲(store)持久化類的對象。這正是Hibernate映射文件發揮作用的地方。
映射文件告訴Hibernate它,應該訪問數據庫(database)裏麵的哪個表(table)及應該使用表裏麵的哪些字段(column)。 

一個映射文件的基本結構看起來像這樣:

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

<hibernate-mapping>
[...]
</hibernate-mapping>

注意Hibernate的DTD是非常複雜的。你的編輯器或者IDE裏使用它來自動完成那些用來映射的XML元素(element)和屬性(attribute)。
你也可以在文本編輯器裏打開DTD-這是最簡單的方式來概覽所有的元素和attribute,並查看它們的缺省值以及注釋。注意Hibernate不會從web加載DTD文件,
但它會首先在應用程序的classpath中查找。DTD文件已包括在hibernate3.jar裏,同時也在Hibernate發布包的src/目錄下。 
為縮短代碼長度,在以後的例子裏我們會省略DTD的聲明。當然,在實際的應用程序中,DTD聲明是必須的。 
在hibernate-mapping標簽(tag)之間, 含有一個class元素。所有的持久化實體類(再次聲明,或許接下來會有依賴類,就是那些次要的實體)都需要一個這樣的映射,來把類對象映射到SQL數據庫裏的表。 
<hibernate-mapping>
    <class name="com.model.StudentInfo" table="StudentInfo">
    </class>
</hibernate-mapping>

到目前為止,我們告訴了Hibernate怎樣把StudentInfo類的對象持久化到數據庫的StudentInfo表裏,以及怎樣從StudentInfo表加載到StudentInfo類的對象。
每個實例對應著數據庫表中的一行。現在我們將繼續討論有關唯一標識符屬性到數據庫表的映射。另外,由於我們不關心怎樣處理這個標識符,我們就配置由Hibernate的標識符生成策略來產生代理主鍵字段。
<hibernate-mapping>
    <class name="com.model.StudentInfo" table="StudentInfo">
        <id name="id" column="ID">
            <generator />
        </id>
    </class>
</hibernate-mapping>

id元素是標識符屬性的聲明,name="id" 聲明了Java屬性的名字 - Hibernate會使用getId()和setId()來訪問它。 
column屬性則告訴Hibernate, 我們使用StudentInfo表的哪個字段作為主鍵。嵌套的generator元素指定了標識符生成策略,在這裏我們指定native,
它根據已配置的數據庫(方言)自動選擇最佳的標識符生成策略。Hibernate支持由數據庫生成,全局唯一性(globally unique)和應用程序指定(或者你自己為任何已有策略所寫的擴展)這些策略來生成標識符。 

最後我們在映射文件裏麵包含需要持久化屬性的聲明。默認情況下,類裏麵的屬性都被視為非持久化的:

<hibernate-mapping>
    <class name="com.model.StudentInfo" table="StudentInfo">
        <id name="id" column="ID">
            <generator />
        </id>
        <property name="date" type="timestamp" column="EnterDate"/>
        <property name="title"/>
    </class>
</hibernate-mapping>

id元素一樣,property元素的name屬性告訴Hibernate使用哪個getter和setter方法。在此例中,Hibernate會尋找getDate()/setDate(), 以及getTitle()/setTitle()

為什麼date屬性的映射含有column attribute,而title卻沒有?當沒有設定column attribute 的時候,Hibernate缺省地使用JavaBean的屬性名作為字段名。對於title,這樣工作得很好。然而,date在多數的數據庫裏,是一個保留關鍵字,所以我們最好把它映射成一個不同的名字。

另一有趣的事情是title屬性缺少一個type attribute。我們在映射文件裏聲明並使用的類型,卻不是我們期望的那樣,是Java數據類型,同時也不是SQL數據庫的數據類型。這些類型就是所謂的Hibernate 映射類型(mapping types),它們能把Java數據類型轉換到SQL數據類型,反之亦然。再次重申,如果在映射文件中沒有設置type屬性的話,Hibernate會自己試著去確定正確的轉換類型和它的映射類型。在某些情況下這個自動檢測機製(在Java 類上使用反射機製)不會產生你所期待或需要的缺省值。date屬性就是個很好的例子,Hibernate無法知道這個屬性(java.util.Date類型的)應該被映射成:SQLdate,或timestamp,還是time 字段。在此例中,把這個屬性映射成timestamp 轉換器,這樣我們預留了日期和時間的全部信息。

應該把這個映射文件保存為Event.hbm.xml,且就在StudentInfo Java類的源文件目錄下。映射文件可隨意地命名,但hbm.xml的後綴已成為Hibernate開發者社區的約定。

對於StudentInfo的配置如下:

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

<hibernate-mapping>
	<class name="com.model.StudentInfo" table="StudentInfo">
		<id name="id" column="ID">
            <generator />
        </id>
		<property name="name" column = "Name"/>
		<property name="age" column = "Age"/>
		<property name="sex" column = "Sex"/>
    </class>
</hibernate-mapping>

我們繼續進行Hibernate的主要配置。


四 Hibernate配置

現在我們已經有了一個持久化類和它的映射文件,該是配置Hibernate的時候了。在此之前,我們需要一個數據庫。

Hibernate是你的應用程序裏連接數據庫的那層,所以它需要連接用的信息。連接(connection)是通過一個也由我們配置的JDBC連接池(connection pool)來完成的。
Hibernate的發布包裏包含了許多開源的(open source)連接池。注意,如果你希望使用一個產品級(production-quality)的第三方連接池軟件,你必須拷貝所需的庫文件到你的classpath下,並使用不同的連接池設置。 
為了保存Hibernate的配置,我們可以使用一個簡單的hibernate.properties文件,或者一個稍微複雜的hibernate.cfg.xml,甚至可以完全使用程序來配置Hibernate。多數用戶更喜歡使用XML配置文件: 

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

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <!-- MySql配置 -->
        <!--<property name="connection.driver_class">com.mysql.jdbc.Driver</property> -->
        <!--<property name="connection.url">jdbc:mysql://localhost/hibernate</property> -->
        <!-- SQL dialect -->
        <!--<property name="dialect">org.hibernate.dialect.MySQLDialect</property>-->
        <!-- SqlServer配置 -->
        <property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
        <property name="connection.url">jdbc:sqlserver://localhost/Hibernate</property>
        <!-- 數據庫用戶名 -->
        <property name="connection.username">sa</property>
        <!-- 數據庫密碼 -->
        <property name="connection.password">123</property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>
        
        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.SQLServerDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>

        <mapping resource="com/model/StudentInfo.hbm.xml"/>
    </session-factory>

</hibernate-configuration>

注意這個XML配置使用了一個不同的DTD。在這裏,我們配置了Hibernate的SessionFactory-一個關聯於特定數據庫全局的工廠(factory)。
如果你要使用多個數據庫,就要用多個的<session-factory>,通常把它們放在多個配置文件中(為了更容易啟動)。 
最開始的4個property元素包含必要的JDBC連接信息。方言(dialect)的property元素指明Hibernate 生成的特定SQL變量。你很快會看到,Hibernate對持久化上下文的自動session管理就會派上用場。
 打開hbm2ddl.auto選項將自動生成數據庫模式(schema)- 直接加入數據庫中。當然這個選項也可以被關閉(通過去除這個配置選項)或者通過Ant任務SchemaExport的幫助來把數據庫schema重定向到文件中。
 最後,在配置中為持久化類加入映射文件。 



Demo下載地址:點擊打開鏈接


五啟動和輔助類

是時候來加載和儲存一些StudentInfo對象了,但首先我們得編寫一些基礎的代碼以完成設置。
我們必須啟動Hibernate,此過程包括創建一個全局的SessoinFactory,並把它儲存在應用程序代碼容易訪問的地方。SessionFactory可以創建並打開新的Session。一個Session代表一個單線程的單元操作,SessionFactory則是個線程安全的全局對象,隻需要被實例化一次。 

我們將創建一個HibernateUtil輔助類(helper class)來負責啟動Hibernate和更方便地操作SessionFactory。讓我們來看一下它的實現:

package com.util;

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

public class HibernateUtil {
	
	private static final SessionFactory sessionFactory;
    static 
    {
        try 
        {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new Configuration().configure().buildSessionFactory();
        }
        catch (Throwable ex) 
        {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

這個類不但在它的靜態初始化過程(僅當加載這個類的時候被JVM執行一次)中產生全局的SessionFactory,而且隱藏了它使用了靜態singleton的事實。
它也可能在應用程序服務器中的JNDI查找SessionFactory。 

示例的基本框架完成了 - 現在我們可以用Hibernate來做些真正的工作。 


六 加載並存儲對象

我們終於可以使用Hibernate來加載和存儲對象了,編寫一個帶有main()方法的StudentManager類: 

package com.test;

import org.hibernate.Session;
import com.model.StudentInfo;
import com.util.HibernateUtil;

public class StudentManager {
	
	public static void main(String[] args) {
		
		StudentManager mgr = new StudentManager();

		mgr.createAndStoreEvent();

        HibernateUtil.getSessionFactory().close();
    }

    private void createAndStoreEvent() {

        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        
        StudentInfo student = new StudentInfo();
        student.setName("無情");
        student.setAge(24);
        student.setSex("女");
        
        session.save(student);
        session.getTransaction().commit();
    }
}

我們創建了個新的StudentInfo對象並把它傳遞給Hibernate。現在Hibernate負責與SQL打交道,並把INSERT命令傳給數據庫。在運行之前,讓我們看一下處理Session和Transaction的代碼。 一個Session就是個單一的工作單元。我們暫時讓事情簡單一些,並假設HibernateSession和數據庫事務是一一對應的。為了讓我們的代碼從底層的事務係統中脫離出來(此例中是JDBC,但也可能是JTA),我們使用Hibernate Session中的Transaction API。 
sessionFactory.getCurrentSession()是幹什麼的呢?首先,隻要你持有SessionFactory(幸虧我們有HibernateUtil,可以隨時獲得),大可在任何時候、任何地點調用這個方法。getCurrentSession()方法總會返回“當前的”工作單元。記得我們在hibernate.cfg.xml中把這一配置選項調整為"thread"了嗎?因此,因此,當前工作單元被綁定到當前執行我們應用程序的Java線程。但是,這並非是完全準確的,你還得考慮工作單元的生命周期範圍 (scope),它何時開始,又何時結束. 
Session在第一次被使用的時候,即第一次調用getCurrentSession()的時候,其生命周期就開始。然後它被Hibernate綁定到當前線程。當事務結束的時候,不管是提交還是回滾,Hibernate會自動把Session從當前線程剝離,並且關閉它。假若你再次調用getCurrentSession(),你會得到一個新的Session,並且開始一個新的工作單元。這種線程綁定(thread-bound)的編程模型(model)是使用Hibernate的最廣泛的方式,因為它支持對你的代碼靈活分層(事務劃分可以和你的數據訪問代碼分離開來,在本教程的後麵部分就會這麼做)。 和工作單元的生命周期這個話題相關,Hibernate Session是否被應該用來執行多次數據庫操作?上麵的例子對每一次操作使用了一個Session,這完全是巧合,這個例子不是很複雜,無法展示其他方式。Hibernate Session的生命周期可以很靈活,但是你絕不要把你的應用程序設計成為每一次數據庫操作都用一個新的Hibernate Session。

七 Annotation注解方式

我們用Annotation注解方式來代替XML配置方式。

在持久化對象時進行注解:

package com.model;

import javax.persistence.Entity;
import javax.persistence.Id;

/**
 * Hibernate Annotation注解方式
 * @author xiaosi
 *
 */
//注解為一個實體
@ Entity
public  class TeacherInfo {
	private int id;
	private String name;
	private String sex;
	private int age;
	
	public TeacherInfo(){
	}
	//注解主鍵
	@ Id
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

在Hibernate.cfg.xml配置中:


啟動輔助類中有一個不同點:Configuration替換為AnnotationConfiguration

package com.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateAnoUtil {
	private static final SessionFactory sessionFactory;
    static 
    {
        try 
        {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        }
        catch (Throwable ex) 
        {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

加載存儲對象:

package com.test;

import org.hibernate.Session;

import com.model.TeacherInfo;
import com.util.HibernateAnoUtil;

public class TeacherManager {
	
	public static void main(String[] args) {
		TeacherManager mgr = new TeacherManager();
		mgr.createAndStoreEvent();
		HibernateAnoUtil.getSessionFactory().close();
	}

    private void createAndStoreEvent() {

        Session session = HibernateAnoUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        
        TeacherInfo teacher = new TeacherInfo();
        teacher.setId(1);
        teacher.setName("王倩");
        teacher.setAge(25);
        teacher.setSex("女");
        
        session.save(teacher);
        session.getTransaction().commit();
    }
}

數據庫插入的數據:









最後更新:2017-04-03 05:39:57

  上一篇:go 在web page中使鼠標右擊失效的幾種方法
  下一篇:go RDS推出隻讀實例