Hibernate學習筆記(1)Hibernate構造

一 準備工做

首先,咱們將建立一個簡單的基於控制檯(console-based)Hibernate應用。html

咱們所作的第一件事就是建立咱們的開發文件夾。並把全部須要用到的Java件放進去。解壓縮從Hibernate站點下載的Hibernate公佈包。並把所有需要的庫文件拷到咱們項目中去。
學習建User-library-hibernate,並增長對應的jar包

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


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

(c)在該library中增長hibernate所需jar包mysql


到編寫本文時爲止。這些是Hibernate執行所需要的最小庫文件集合(注意咱們也拷貝了 Hibernate3.jar,這個是最基本的文件)。
你正使用的Hibernate版本號可能需要比這不少其它或少一些的庫文件。請參見公佈包中的lib/文件夾下的README.txt,以獲取不少其它關於所需和可選的第三方庫文件信息(其實。Log4j並不是必須的庫文件,但被不少開發人員所喜歡)。web

(d) 引入sqlserver的JDBC驅動包 sql

在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。session

這樣當對象被保存的時候,僅僅有Hibernate可以爲它分配標識符值。
你可看到Hibernate可以直接訪問public,private和protected的訪問方法和field。app

因此選擇哪一種方式全然取決於你。你可以使你的選擇與你的應用程序設計相吻合。

 
所有的持久化類(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" "http://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 class="native"/>
        </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 class="native"/>
        </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在多數的數據庫裏。是一個保留keyword,因此咱們最好把它映射成一個不一樣的名字。

還有一有趣的事情是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"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	<class name="com.model.StudentInfo" table="StudentInfo">
		<id name="id" column="ID">
            <generator class="native"/>
        </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"
        "http://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>,一般把它們放在多個配置文件裏(爲了更easy啓動)。 
最開始的4個property元素包括必要的JDBC鏈接信息。方言(dialect)的property元素指明Hibernate 生成的特定SQL變量。你很是快會看到,Hibernate對持久化上下文的本身主動session管理就會派上用場。
 打開hbm2ddl.auto選項將本身主動生成數據庫模式(schema)- 直接增長數據庫中。固然這個選項也可以被關閉(經過去除這個配置選項)或者經過Ant任務SchemaExport的幫助來把數據庫schema重定向到文件裏。


 最後,在配置中爲持久化類增長映射文件。

 



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


五啓動和輔助類

是時候來載入和儲存一些StudentInfo對象了,但首先咱們得編寫一些基礎的代碼以完畢設置。
咱們必須啓動Hibernate,此過程包含建立一個全局的SessoinFactory,並把它儲存在應用程序代碼easy訪問的地方。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的事實。


它也可能在應用程序server中的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();
    }
}

數據庫插入的數據:

相關文章
相關標籤/搜索