1. 程式人生 > >Hibernate學習筆記(二)——建立一個簡單的Hibernate專案

Hibernate學習筆記(二)——建立一個簡單的Hibernate專案

首先來看看Hibernate開發的一個簡單流程:

(1)準備開發環境,建立Hibernate專案。

(2)在資料庫中建立資料表。

(3)建立持久化類。

(4)設計對映檔案,使用Hibernate對映檔案將POJO物件對映到資料庫。

(5)建立Hibernate的配置檔案Hibernate.cfg.xml。

(6)編寫輔助工具類HibernateUtil類,用來實現對HIbernate的初始化並提供獲得Session的方法,此步可根據情況取捨。

(7)編寫DAO層類。

(8)編寫Service層類。

(9)編寫測試類。

下面來一步一步地做。

1.建立Eclipse專案

在Eclipse中建立一個Dynamic Web Project專案,命名為HibernateDemo。


HibernateDemo專案名稱上右擊,在快捷選單中選擇New->Other選項,在彈出的對話方塊中找到Hibernate節點,選擇Hibernate Configuration File(cfg.xml)選項,如下圖所示:


點選Next按鈕,在彈出的對話方塊中選擇配置檔案儲存的目錄,一般預設在src目錄,同時需要輸入配置檔案的名稱,一般預設為hibernate.cfg.xml即可。繼續Next,在彈出的對話方塊中填寫資料庫方言(Database dialect)、資料庫驅動(Driver class)、資料庫URL、使用者名稱、密碼等。MySQL資料庫的配置如下:


單擊Finish,配置檔案就建立成功了,後面有需要可以繼續編輯該檔案。

2.建立資料表USER

在MySQL中建立一個名為mysqldb的資料庫,在該資料庫中建立一張名為USER的表。建立USER表的語句如下:

create table user(
user_id int(11),
name varchar(20),
password varchar(12),
type varchar(6),
primary key(user_id));
建立好的USER表在MySQL中顯示如下:
mysql> describe user;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| user_id  | int(11)     | NO   | PRI | 0       |       |
| name     | varchar(20) | YES  |     | NULL    |       |
| password | varchar(12) | YES  |     | NULL    |       |
| type     | varchar(6)  | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+

3.編寫POJO對映類User.java

package org.hibernate.entity;

public class User {

	private int id;//持久化類的標識屬性,對映到資料表中的主鍵列
	private String name;
	private String password;
	private String type;
	public User() {
		// TODO Auto-generated constructor stub
	}
	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 getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	
}

4.編寫對映檔案User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2014-12-28 22:18:36 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
	<!-- name 持久化類的類名,table 資料表的表名,MySQL不區分大小寫 -->
    <class name="org.hibernate.entity.User" table="USER">
    	<!-- 將User類中的id屬性對映為資料表USER中的主鍵列user_id -->
        <id name="id" type="int">
            <column name="USER_ID" />
            <generator class="native" />
        </id>
        <!-- 對映User類的name屬性 -->
        <property name="name" type="java.lang.String">
            <column name="NAME" length="20"/>
        </property>
        <!-- 對映User類的password屬性 -->
        <property name="password" type="java.lang.String">
            <column name="PASSWORD"  length="12"/>
        </property>
        <!-- 對映User類的type屬性 -->
        <property name="type" type="java.lang.String">
            <column name="TYPE" length="6"/>
        </property>
    </class>
</hibernate-mapping>

5.編寫hibernate.cfg.xml配置檔案

前面我們通過Hibernate Tools的嚮導工具新建了一個hibernate.cfg.xml配置檔案,其實我們也可以直接在src目錄下新建一個XML檔案將其命名為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>
		<!-- 資料庫的JDBC驅動 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<!-- 資料庫的URL -->
		<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/mysqldb</property>
		<!-- 資料庫的使用者名稱和密碼 -->
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password"></property>
		<!-- 資料庫的方言 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<!-- Hibernate自動根據對映檔案建立或者更新資料表 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- 在控制檯輸出執行時生成的SQL語句,方便除錯 -->
		<property name="show_sql">true</property>
		<!-- 連線池大小 -->
		<property name="connection.pool_size">1</property>
		<!-- 列出所有對映檔案 -->
		<mapping resource="org/hibernate/entity/User.hbm.xml" />
	</session-factory>
</hibernate-configuration>

6.編寫輔助工具類HibernateUtil.java

package org.hibernate.entity;

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

public class HibernateUtil {

	private static SessionFactory sessionFactory;
	// 建立執行緒區域性變數threadLocal,用來儲存Hibernate的Session
	private static final ThreadLocal<Session> threadLocal=new ThreadLocal<Session>();
	// 使用靜態程式碼塊初始化Hibernate
	static
	{
		try
		{
			// 讀取配置檔案
			Configuration cfg=new Configuration().configure();
			// 建立SessionFactory
			sessionFactory=cfg.buildSessionFactory();
		}catch(Throwable ex)
		{
			throw new ExceptionInInitializerError(ex);
		}
	}
	// 獲得SessionFactory的例項
	public static SessionFactory getsSessionFactory()
	{
		return sessionFactory;
	}
	// 獲得ThreadLocal物件管理的Session
	public static Session getsSession() throws HibernateException
	{
		Session session=(Session) threadLocal.get();
		if(session==null||!session.isOpen())
		{
			if(sessionFactory==null)
			{
				rebuildSessionFactory();
			}
			// 通過SessionFactory物件建立Session物件
			session=(sessionFactory!=null)?sessionFactory.openSession():null;
			// 將Session物件儲存到執行緒區域性變數threadLocal中
			threadLocal.set(session);
		}
		return session;
	}
	// 關閉Session例項
	public static void closeSession()
	{
		// 從執行緒區域性變數threadLocal中獲取之前存入的Session例項
		Session session=(Session)threadLocal.get();
		threadLocal.set(null);
		if(session!=null)
		{
			session.close();
		}
	}
	// 重建SessionFactory
	public static void rebuildSessionFactory()
	{
		Configuration configuration=new Configuration();
		configuration.configure("/hibernate.cfg.xml");
		sessionFactory=configuration.buildSessionFactory();
	}
	// 關閉快取和連線池
	public static void shutdown()
	{
		getsSessionFactory().close();
	}
}

7.編寫DAO層介面UserDAO.java

package org.hibernate.dao;

import org.hibernate.entity.User;

public interface UserDAO {

	void save(User user);
	User findById(int id);
	void delete(User user);
	void update(User user);
}

8.編寫DAO層實現類UserDAOImpl.java

package org.hibernate.dao;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.entity.HibernateUtil;
import org.hibernate.entity.User;

public class UserDAOImpl implements UserDAO {

	// 新增使用者
	@Override
	public void save(User user) {
		// TODO Auto-generated method stub
		// 建立Session例項
		Session session = HibernateUtil.getsSession();
		// 建立Transaction例項
		Transaction tx = session.beginTransaction();
		try {
			// 使用Session的save方法將持久化物件儲存到資料庫
			session.save(user);
			// 提交事務
			tx.commit();
		} catch (Exception e) {
			e.printStackTrace();
			// 出現異常,回滾事務
			tx.rollback();
		} finally {
			// 關閉Session連線
			HibernateUtil.closeSession();
		}
	}

	// 根據id查詢使用者
	@Override
	public User findById(int id) {
		// TODO Auto-generated method stub
		User user = null;
		Session session = HibernateUtil.getsSession();
		Transaction tx = session.beginTransaction();
		try {
			// 使用session的get方法獲取指定id的使用者
			user = (User) session.get(User.class, id);
			tx.commit();
		} catch (Exception e) {
			e.printStackTrace();
			tx.rollback();
		} finally {
			HibernateUtil.closeSession();
		}
		return user;
	}

	// 刪除使用者
	@Override
	public void delete(User user) {
		// TODO Auto-generated method stub
		Session session = HibernateUtil.getsSession();
		Transaction tx = session.beginTransaction();
		try {
			// 使用session的delete方法將持久化物件刪除
			session.delete(user);
			tx.commit();
		} catch (Exception e) {
			e.printStackTrace();
			tx.rollback();
		} finally {
			HibernateUtil.closeSession();
		}
	}

	// 修改使用者資訊
	@Override
	public void update(User user) {
		// TODO Auto-generated method stub
		Session session = HibernateUtil.getsSession();
		Transaction tx = session.beginTransaction();
		try {
			// 使用session的update方法更新持久化物件
			session.update(user);
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
			e.printStackTrace();
		} finally {
			HibernateUtil.closeSession();
		}
	}

}

通過以上步驟,一個Hibernate專案就完成了,下面我們來測試一下。

9.編寫測試類UserTest.java

在HibernateDemo專案名稱上右擊,選擇Properties,在彈出的視窗左側選擇Java Build Path選項,然後在右側介面中選擇Libraries標籤,點選Add Library按鈕,在彈出的視窗中選擇Junit,如下圖所示:


然後點選Next,在version一欄選擇Junit 4,然後點選Finish。這樣Junit包就引入到專案中了。

接下來在專案中新建org.hibernate.test包,在包名上右擊,依次選擇New->Junit Test Case選單,在彈出的視窗中填寫測試類的名稱和需要測試的類的名稱(這個需要填寫完整包名),如下圖所示:



點選Next按鈕,可以選擇需要測試的方法,根據需要選擇即可。



點選Finish,系統會自動生成UserTest類的框架,裡面包含了一些空方法,我們將需要測試的方法進行重寫就可以了。

這裡以save方法為例,重寫testSave方法。

package org.hibernate.test;

import org.hibernate.dao.UserDAO;
import org.hibernate.dao.UserDAOImpl;
import org.hibernate.entity.User;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;

public class UserTest {

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
	}

	// @Test 註釋表名是一個測試方法
	@Test
	public void testSave() {
		UserDAO userDAO=new UserDAOImpl();
		try{
			User u=new User();
			// 設定User物件的各個屬性
			u.setId(20);
			u.setName("zhangsan");
			u.setPassword("123456");
			u.setType("admin");
			// 使用UserDAOImpl的save方法將User物件存入到資料庫
			userDAO.save(u);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}
接下來在UserTest.java檔名稱上右擊,依次選擇Run As ->Junit Test選單,我們可以在Eclipse的Junit View中看到測試結果,如果進度條正確,表示結果正確,如果進度條為紅色,表明有錯誤,我們可以看到有什麼錯誤。


然後我們到資料庫中看看資料有沒有正確儲存到資料庫。

mysql> select * from user;
+---------+----------+----------+-------+
| USER_ID | NAME     | PASSWORD | TYPE  |
+---------+----------+----------+-------+
|       1 | zhangsan | 123456   | admin |
+---------+----------+----------+-------+
這裡可能有人會感到奇怪,為什麼UserTest類中設定了id為20,結果儲存到資料庫中確是1呢?

因為我們在User.hbm.xml檔案中將id設定成了主鍵,如下所示:

    	<!-- 將User類中的id屬性對映為資料表USER中的主鍵列user_id -->
        <id name="id" type="int">
            <column name="USER_ID" />
            <generator class="native" />
        </id>
其中的generator元素指的是主鍵生成策略,hibernate會按照主鍵生成策略為id賦值,而不會將程式中的id值儲存到資料庫。


最後結合上面的專案我們再看看Hibernate的工作原理:

(1)Hibernate初始化,建立Configuration物件。

a)從Hibernate配置檔案hibernate.properties或者hibernate.cfg.xml中讀取配置資訊,存放到Configuration物件中。

b)根據配置檔案中的mapping元素載入所有實體類對應的對映檔案到Configuration物件中。

說明:Hibernate可以採取兩種形式的配置檔案,一種是hibernate.properties檔案,另一種是hibernate.cfg.xml檔案。這兩種檔案本質上是一樣的,都可以完成對Hibernate的配置工作,在實際開發中,更多地採用XML格式的配置檔案。若兩種配置檔案同時存在且都有相同的配置資訊,則hibernate.cfg.xml中的配置會覆蓋掉hibernate.properties中的配置,這其中的原因結合程式碼來解釋。看如下程式碼:

Configuration cfg=new Configuration().configure();
這是建立Configuration例項並讀取配置檔案的程式碼。
  • 當執行new Configuration()方法時,Hibernate會在classpath的根目錄下查詢hibernate.properties檔案。如果找到了該檔案,則所有的hibernate.*的屬性被裝載到Configuration物件中。
  • 當呼叫configure()方法時,Hibernate會在classpath根目錄下查詢hibernate.cfg.xml。如果找不到則丟擲HibernateException。如果hibernate.cfg.xml中的某些屬性和hibernate.properties中的重複了,則會覆蓋。

(2)建立SessionFactory例項。

Configuration物件將配置資訊存入SessionFactory的屬性中,建立完SessionFactory例項,Configuration物件的使命就結束了,SessionFactory與Configuration之間的關聯也斷開了。SessionFactory充當資料來源的代理,並負責建立Session物件。

  • SessionFactory例項是全域性唯一的,它對應著應用程式中的資料來源,一個數據源只需要一個SessionFactory例項,只有當應用中有多個數據源時,才為每個資料來源建立一個SessionFactory例項。
  • SessionFactory的例項是重量級的,建立和銷燬都要消耗較多資源,因此只建立一次。
  • 通過SessionFactory可以獲得多個Session例項。
  • SessionFactory是執行緒安全的,可以被多個執行緒共享。Session不是執行緒安全的,多個併發執行緒同時操作一個Session例項時會出現問題,通常使用ThreadLocal模式管理Session。

(3)建立Session例項,建立資料庫連線。

SessionFactory可以有兩種方式建立Session。

a)openSession()方法。此方法直接建立一個新的Session例項,使用完之後需要呼叫close方法手動關閉。

b)getCurrentSession()方法。此方法建立的Session例項會繫結到當前執行緒,在事務提交(commit)或回滾(rollback)後會自動關閉。使用此方法必須在hibernate.cfg.xml配置檔案中新增如下配置:

  <!-- 如果使用的是本地事務 -->
  <propertyname="hibernate.current_session_context_class">thread</property>
  <!-- 如果使用的是全域性事務 -->
  <propertyname="hibernate.current_session_context_class">jta</property>

(4)建立Transaction例項,開始一個事務。

Hibernate的事務是對資料庫底層事務的封裝,在對Hibernate進行增、刪、改操作的時候必須先建立一個Transaction物件,一個事務就是一個原子操作。

(5)利用Session的方法進行持久化操作。將實體物件持久化到資料庫中。

(6)提交事務。

(7)關閉Session,斷開與資料庫的連線。