1. 程式人生 > >Hibernate框架基礎——Hibernate入門

Hibernate框架基礎——Hibernate入門

Hibernate入門

Hibernate介紹

Hibernate是一個基於jdbc的開源的持久化框架,是一個優秀的ORM實現,它很大程度的簡化了dao層編碼工作。Hibernate對JDBC訪問資料庫的程式碼做了封裝,大大簡化了資料訪問層繁瑣的重複性程式碼。
在分層結構中處於持久化層,封裝對資料庫的訪問細節,使業務邏輯層更專注於實現業務邏輯。
Hibernate的主頁為:http://www.hibernate.org/
Hibernate的下載地址為:http://hibernate.org/orm/downloads/
HibernateTools的下載地址為:http://www.jboss.org/tools/download/

。在Eclipse中如何安裝Hibernate Tools外掛,請參看。
本人使用的是hibernate-release-4.3.11.Final

Hibernate的體系結構與開發步驟

Hibernate的體系結構
這裡寫圖片描述
Hibernate開發步驟

  1. 建立持久化類
  2. 建立物件-關係對映檔案
  3. 建立Hibernate配置檔案
  4. 通過Hibernate API編寫訪問資料庫的程式碼

這裡寫圖片描述

第一個Hibernate程式(HelloWorld)

建立Eclipse工程並引入相關的jar包

新建Java工程,並新增如下jar包:

  1. {hibernate_home}/lib/required/*.jar
  2. 資料庫對應的JDBC驅動(例如mysql-connector-java-5.1.38-bin.jar)

還可以加入日誌相關的jar包(不加也可以):

  1. log4j-1.x.x.jar
  2. slf4j-log4j12-1.x.x.jar

這裡寫圖片描述

建立持久化物件:User.java

在cn.itcast.a_helloworld包下建立User類,程式碼如下:

/**
 * 實體
 */
public class User {
    private int id;
    private String name;

    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; } @Override public String toString() { return "[User: id=" + id + ", name=" + name + "]"; } }

建立物件-關係對映檔案:User.hbm.xml

首先在MySQL資料庫裡面建立一個t_user表,建表語句如下:

create database hibernate_20160926 default character set utf8;
use hibernate_20160926;
create table t_user( id int primary key auto_increment, name varchar(20) );

t_user表建立好之後,我們就要建立相對應的物件-關係對映檔案:User.hbm.xml了,注意該對映配置檔案與User類要在一起,即同一個包下。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="cn.itcast.a_helloworld">
    <class name="User" table="t_user">
        <id name="id" type="int" column="id">
            <generator class="native"/>
        </id>
        <property name="name" type="string" column="name" />
    </class>
</hibernate-mapping>

關於此對映配置檔案中的內容後面會慢慢詳解。

建立Hibernate配置檔案:hibernate.cfg.xml

我們要在類路徑下(即src目錄下)建立Hibernate配置檔案——hibernate.cfg.xml。
初學者第一次學習Hibernate框架,肯定是不知道如何編寫Hibernate配置檔案的,但我們可以在{hibernate_home}/project/etc目錄下找到開發包給我們提供的Hibernate配置檔案,我們只須要拷貝進我們的專案,然後修修改改就可以了。
這樣我們的Hibernate配置檔案——hibernate.cfg.xml的內容就為:

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory name="foo">
        <!-- 1. 配置資料庫資訊 -->
        <!-- 方言(連線的資料庫型別) -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="connection.url">jdbc:mysql:///hibernate_20160926</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">root</property>
        <property name="connection.password">yezi</property>

        <!-- 2. 其他配置 -->
        <!-- 顯示生成的SQL語句 -->
        <property name="hibernate.show_sql">true</property>

        <!-- 3. 匯入對映檔案 -->
        <mapping resource="cn/itcast/a_helloworld/User.hbm.xml" />

    </session-factory>
</hibernate-configuration>

這裡寫圖片描述
關於此配置檔案中的內容後面會慢慢詳解。

通過Hibernate API編寫訪問資料庫的程式碼

程式設計步驟

  1. 獲取Configuration物件
  2. 獲取SessionFactory物件
  3. 獲取Session,開啟事務
  4. 用面向物件的方式操作資料庫
  5. 關閉事務,關閉Session

由於是Hibernate入門,所以我不會搞得很複雜,就只是簡單的向hibernate_20160926資料庫中的t_user表中插入一條記錄,然後再取出來而已。
我們在cn.itcast.a_helloworld包下建立一個App類,為了方便測試,我使用的是單元測試。

public class App {

    private static SessionFactory sessionFactory;

    static {
        Configuration cfg = new Configuration();
        cfg.configure("hibernate.cfg.xml"); // 讀取指定的主配置檔案
        sessionFactory = cfg.buildSessionFactory(); // 根據配置生成Session工廠
    }

    @Test
    public void testSave() {
        User user = new User();
        user.setName("張三");

        // 儲存
        Session session = sessionFactory.openSession(); // 開啟一個新的Session
        Transaction tx = session.beginTransaction(); // 開啟事務

        session.save(user);

        tx.commit(); // 提交事務
        session.close(); // 關閉Session,釋放資源(不一定是真正的關閉)
    }

    @Test
    public void testGet() {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();

        User user = (User) session.get(User.class, 1); // 獲取?
        System.out.println(user);

        tx.commit();
        session.close();
    }
}

測試第一個Hibernate程式

編寫完第一個Hibernate程式之後,我們就要來測試了,先測試向hibernate_20160926資料庫中的t_user表中插入一條記錄,發現報如下異常:

org.hibernate.engine.jndi.JndiException: Error parsing JNDI name [foo]
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.parseName(JndiServiceImpl.java:141)
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.bind(JndiServiceImpl.java:157)
    at org.hibernate.internal.SessionFactoryRegistry.addSessionFactory(SessionFactoryRegistry.java:103)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:497)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1930)
    at cn.itcast.a_helloworld.App.<clinit>(App.java:16)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:217)
    at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:266)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
    at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
    at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
    at javax.naming.InitialContext.getURLOrDefaultInitCtx(Unknown Source)
    at javax.naming.InitialContext.getNameParser(Unknown Source)
    at org.hibernate.engine.jndi.internal.JndiServiceImpl.parseName(JndiServiceImpl.java:135)
    ... 28 more

錯誤原因
在hibernate.cfg.xml檔案內容的配置

<session-factory name="foo">
...
</session-factory>

中添加了name屬性。
解決辦法
去掉

<session-factory name="foo">
...
</session-factory>

中的name屬性。
修改之後,測試,通過,大發!

第二個Hibernate程式——完整的資料庫操作(CRUD)

前面我們寫了一個Hibernate入門的小程式,但也只是簡單的向hibernate_20160926資料庫中的t_user表中插入一條記錄,然後再取出來而已。現在我們來寫第二個Hibernate程式——實現對資料庫完整的操作(CRUD)。
我們首先在cn.itcast.b_dao包下建立一個工具類——HibernateUtils.java,該工具類的作用專門用來獲取全域性唯一的SessionFactory,以及從全域性唯一的SessionFactory中開啟一個Session。

public class HibernateUtils {

    // SessionFactory全域性只需要有一個就可以了,因為它的建立和銷燬需要消耗大量的資源,初始化資訊會比較多,並且它是執行緒安全的,可以在多執行緒的環境下使用它
    private static SessionFactory sessionFactory;

    static {
        // 初始化SessionFactory方式一:
        /*
        Configuration cfg = new Configuration(); // 代表配置檔案的一個物件
        cfg.configure(); // 讀取預設的配置檔案(hibernate.cfg.xml)
        // cfg.configure("hibernate.cfg.xml"); // 讀取指定位置的配置檔案
        sessionFactory = cfg.buildSessionFactory();
        */

        // 初始化SessionFactory方式二:
        sessionFactory = new Configuration() //
                .configure() //
                .buildSessionFactory(); // 方法鏈
    }

    /**
     * 獲取全域性唯一的SessionFactory
     * 
     * @return
     */
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    /**
     * 從全域性唯一的SessionFactory中開啟一個Session
     * 
     * @return
     */
    public static Session openSession() {
        return sessionFactory.openSession();
    }

}

思考一個問題,若我們要編寫程式碼實現對資料庫完整的操作,那麼就必定涉及到分頁查詢,要實現分頁查詢,我們一定要弄清楚分頁設計結構圖,要是有人不知道,可翻閱我的記錄。所以我們還要在cn.itcast.b_dao包下建立一個類——QueryResult.java,用於封裝查詢結果。

public class QueryResult {

    private int count; // 總記錄數
    private List list; // 一頁的資料

    public QueryResult(int count, List list) {
        this.count = count;
        this.list = list;
    }

    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    public List getList() {
        return list;
    }
    public void setList(List list) {
        this.list = list;
    }

}

最後,我們在cn.itcast.b_dao包下建立一個類——UserDao.java。UserDao類裡面編寫程式碼實現對資料庫完整的操作(CRUD)。

public class UserDao {

    /*
     * 儲存
     */
    public void save(User user) {
        Session session = HibernateUtils.openSession();
        try {
            Transaction tx = session.beginTransaction(); // 開啟事務
            session.save(user);
            tx.commit(); // 提交事務
        } catch (RuntimeException e) {
            session.getTransaction().rollback(); // 回滾事務
            throw e;
        } finally {
            session.close(); // 關閉session
        }
    }

    /*
     * 更新
     */
    public void update(User user) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            session.update(user);// 操作

            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /*
     * 刪除
     */
    public void delete(int id) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            Object user = session.get(User.class, id); // 要先獲取到這個物件
            session.delete(user); // 刪除的是實體物件

            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /*
     * 根據id查詢一個User資料
     */
    public User getById(int id) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            User user = (User) session.get(User.class, id);// 操作
            tx.commit();
            return user;
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /*
     * 查詢所有
     */
    public List<User> findAll() {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();

            // 方式一:使用HQL語句
            List<User> list = session.createQuery("FROM User").list(); // 使用HQL查詢

            tx.commit();
            return list;
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    /**
     * 分頁的查詢資料列表
     * @param firstResult 從結果列表中的哪個索引開始取資料
     * @param maxResults 最多取多少條資料
     * @return 一頁的資料列表
     */
    @SuppressWarnings("unchecked")
    public QueryResult findAll(int firstResult, int maxResults) {
        Session session = HibernateUtils.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            // 查詢一頁的資料列表
            // 方式一:
            // Query query = session.createQuery("FROM User");
            // query.setFirstResult(firstResult);
            // query.setMaxResults(maxResults);
            // List<User> list = query.list(); // 使用HQL查詢

            // 方式二:方法鏈
            List<User> list = session.createQuery( //
                    "FROM User") //
                    .setFirstResult(firstResult) // 
                    .setMaxResults(maxResults) //
                    .list();

            // 查詢總記錄數
            // session.createQuery("SELECT COUNT(*) FROM User").list().get(0);
            // Long count = (Long) session.createQuery("SELECT COUNT(*) FROM User").uniqueResult();
            Long count = (Long) session.createQuery( //
                    "SELECT COUNT(*) FROM User") //
                    .uniqueResult();
            tx.commit();

            // 返回結果
            return new QueryResult(count.intValue(), list);
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

}

在如下兩個方法中:

  • public List<User> findAll()
  • public QueryResult findAll(int firstResult, int maxResults)

我們使用到了Hibernate查詢語句——HQL(Hibernate Query Language),後面將會詳細講解,這裡稍微瞭解一下即可,我們只要會用就好。
HQL(Hibernate Query Language)與SQL相似,查詢的是物件和物件中的屬性,關鍵字不區分大小寫,但類名與屬性名區分大小寫;而SQL查詢的是表和表中的欄位,同樣也不區分大小寫。
接下來,為方便測試,我們使用單元測試來測試以上編寫的程式碼。我們只要右鍵點選UserDao類→New→JUnit Test Case,如下:
這裡寫圖片描述
接下來會彈出如下對話方塊:
這裡寫圖片描述
這是要幫我們自動建立UserDao測試類的節奏。
接著點選Next按鈕,在彈出的對話方塊中選中我們要測試的UserDao類中的方法。
這裡寫圖片描述
最後點選Finish完成,這時Eclipse就幫我們自動建立UserDao類的測試類
——UserDaoTest.java了,然後我們再在其中編寫測試程式碼。

public class UserDaoTest {

    private UserDao userDao = new UserDao();

    @Test
    public void testSave_1() {
        User user = new User();
        user.setName("張三");

        // 儲存
        userDao.save(user);
    }

    @Test
    public void testGetById() {
        User user = userDao.getById(1);
        System.out.println(user);
    }

    @Test
    public void testUpdate() {
        // 從資料庫中獲取一條存在的資料
        User user = userDao.getById(1);
        user.setName("李四");
        // 更新
        userDao.update(user);
    }

    @Test
    public void testDelete() {
        userDao.delete(1);
    }

    // -------------------------

    @Test
    public void testSave_25() {
        for (int i = 1; i <= 25; i++) {
            User user = new User();
            user.setName("test_" + i);

            userDao.save(user); // 儲存
        }
    }

    @Test
    public void testFindAll() {
        List<User> list = userDao.findAll();
        for (User user : list) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindAllIntInt() {
        // 查詢
        // QueryResult qr = userDao.findAll(0, 10); // 第1頁,每頁10條
        // QueryResult qr = userDao.findAll(10, 10); // 第2頁,每頁10條
        QueryResult qr = userDao.findAll(20, 10); // 第3頁,每頁10條

        // 顯示結果
        System.out.println("總記錄數:" + qr.getCount());
        for (User user : (List<User>) qr.getList()) {
            System.out.println(user);
        }

    }

}

測試,全部通過,大發!