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/
本人使用的是hibernate-release-4.3.11.Final。
Hibernate的體系結構與開發步驟
Hibernate的體系結構:
Hibernate開發步驟
- 建立持久化類
- 建立物件-關係對映檔案
- 建立Hibernate配置檔案
- 通過Hibernate API編寫訪問資料庫的程式碼
第一個Hibernate程式(HelloWorld)
建立Eclipse工程並引入相關的jar包
新建Java工程,並新增如下jar包:
- {hibernate_home}/lib/required/*.jar
- 資料庫對應的JDBC驅動(例如mysql-connector-java-5.1.38-bin.jar)
還可以加入日誌相關的jar包(不加也可以):
- log4j-1.x.x.jar
- 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編寫訪問資料庫的程式碼
程式設計步驟:
- 獲取Configuration物件
- 獲取SessionFactory物件
- 獲取Session,開啟事務
- 用面向物件的方式操作資料庫
- 關閉事務,關閉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);
}
}
}
測試,全部通過,大發!