- Hibernate 教程
- Hibernate - 首頁
- ORM - 概述
- Hibernate - 概述
- Hibernate - 架構
- Hibernate - 環境
- Hibernate - 配置
- Hibernate - 會話
- Hibernate - 持久類
- Hibernate - 對映檔案
- Hibernate - 對映型別
- Hibernate - 示例
- Hibernate - O/R 對映
- Hibernate - 級聯型別
- Hibernate - 註解
- Hibernate - 查詢語言
- Hibernate - Criteria 查詢
- Hibernate - 原生 SQL
- Hibernate - 快取
- Hibernate - 實體生命週期
- Hibernate - 批次處理
- Hibernate - 攔截器
- Hibernate - ID 生成器
- Hibernate - 儲存影像
- Hibernate - log4j 整合
- Hibernate - Spring 整合
- Hibernate - Struts 2 整合
- Hibernate - Web 應用程式
- 對映表示例
- Hibernate - 表繼承策略
- Hibernate - 表對應具體類策略
- Hibernate - 表對應子類策略
- Hibernate 有用資源
- Hibernate - 問題與解答
- Hibernate - 快速指南
- Hibernate - 有用資源
- Hibernate - 討論
Hibernate - 快速指南
Hibernate - ORM 概述
什麼是 JDBC?
JDBC 代表 **Java 資料庫連線**。它提供了一套 Java API,用於從 Java 程式訪問關係資料庫。這些 Java API 使 Java 程式能夠執行 SQL 語句並與任何符合 SQL 標準的資料庫進行互動。
JDBC 提供了一個靈活的架構,可以編寫資料庫無關的應用程式,該應用程式可以在不同的平臺上執行並與不同的 DBMS 互動,而無需進行任何修改。
JDBC 的優缺點
| JDBC 的優點 | JDBC 的缺點 |
|---|---|
|
簡潔明瞭的 SQL 處理 處理大量資料時效能良好 非常適合小型應用程式 語法簡單,易於學習 |
如果在大型專案中使用,則會變得複雜 程式設計開銷大 沒有封裝 難以實現 MVC 概念 查詢特定於 DBMS |
為什麼需要物件關係對映 (ORM)?
當我們使用面向物件的系統時,物件模型和關係資料庫之間存在不匹配。RDBMS 以表格格式表示資料,而面向物件的語言(如 Java 或 C#)則將其表示為物件的互連圖。
考慮以下具有適當建構函式和相關公共函式的 Java 類 -
public class Employee {
private int id;
private String first_name;
private String last_name;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.first_name = fname;
this.last_name = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public String getFirstName() {
return first_name;
}
public String getLastName() {
return last_name;
}
public int getSalary() {
return salary;
}
}
假設以上物件要儲存到以下 RDBMS 表中並從中檢索 -
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
第一個問題是,如果在開發了一些頁面或應用程式後需要修改資料庫的設計,該怎麼辦?其次,在關係資料庫中載入和儲存物件會使我們面臨以下五個不匹配問題 -
| 序號 | 不匹配 & 描述 |
|---|---|
| 1 |
粒度 有時您將擁有一個物件模型,該模型具有的類數多於資料庫中相應表的數量。 |
| 2 |
繼承 RDBMS 沒有定義任何類似於繼承的內容,而繼承是面向物件程式語言中的自然正規化。 |
| 3 |
標識 RDBMS 定義了唯一一個“相同性”的概念:主鍵。但是,Java 定義了物件標識 (a==b) 和物件相等性 (a.equals(b))。 |
| 4 |
關聯 面向物件的語言使用物件引用來表示關聯,而 RDBMS 使用外部索引鍵列來表示關聯。 |
| 5 |
導航 在 Java 和 RDBMS 中訪問物件的方式從根本上不同。 |
**物件關係對映** (ORM) 是處理所有上述阻抗不匹配的解決方案。
什麼是 ORM?
ORM 代表 **物件關係對映** (ORM) 是一種程式設計技術,用於在關係資料庫和麵向物件程式語言(如 Java、C# 等)之間轉換資料。
與普通的 JDBC 相比,ORM 系統具有以下優勢 -
| 序號 | 優勢 |
|---|---|
| 1 | 讓業務程式碼訪問物件而不是資料庫表。 |
| 2 | 隱藏 SQL 查詢的細節,使其與 OO 邏輯分離。 |
| 3 | 基於底層的 JDBC。 |
| 4 | 無需處理資料庫實現。 |
| 5 | 基於業務概念而不是資料庫結構的實體。 |
| 6 | 事務管理和自動鍵生成。 |
| 7 | 加快應用程式開發。 |
ORM 解決方案包含以下四個實體 -
| 序號 | 解決方案 |
|---|---|
| 1 | 一個 API,用於對持久類物件執行基本的 CRUD 操作。 |
| 2 | 一種語言或 API,用於指定引用類和類屬性的查詢。 |
| 3 | 一個可配置的工具,用於指定對映元資料。 |
| 4 | 一種與事務物件互動的技術,用於執行髒資料檢查、延遲關聯獲取和其他最佳化功能。 |
Java ORM 框架
Java 中有幾個持久框架和 ORM 選項。持久框架是一種 ORM 服務,用於將物件儲存到關係資料庫中並從中檢索物件。
- 企業級 JavaBeans 實體 Bean
- Java 資料物件
- Castor
- TopLink
- Spring DAO
- Hibernate
- 等等
Hibernate - 概述
Hibernate 是一個用於 JAVA 的 **物件關係對映** (ORM) 解決方案。它是一個開源的持久框架,由 Gavin King 於 2001 年建立。它是一個功能強大、高效能的物件關係永續性和查詢服務,適用於任何 Java 應用程式。
Hibernate 將 Java 類對映到資料庫表,並將 Java 資料型別對映到 SQL 資料型別,從而使開發人員免於 95% 的常見資料永續性相關程式設計任務。
Hibernate 位於傳統的 Java 物件和資料庫伺服器之間,處理所有基於適當的 O/R 機制和模式來持久化這些物件的工作。
Hibernate 的優點
Hibernate 使用 XML 檔案(無需編寫任何程式碼)來處理將 Java 類對映到資料庫表的任務。
提供簡單的 API,用於將 Java 物件直接儲存到資料庫中並從中檢索。
如果資料庫或任何表發生更改,則只需更改 XML 檔案的屬性即可。
抽象化不熟悉的 SQL 型別,並提供一種使用熟悉的 Java 物件的方法。
Hibernate 不需要應用程式伺服器即可執行。
操作資料庫物件之間複雜的關聯。
透過智慧獲取策略最大程度地減少資料庫訪問。
提供簡單的查詢資料功能。
支援的資料庫
Hibernate 支援幾乎所有主要的 RDBMS。以下是 Hibernate 支援的一些資料庫引擎 -
- HSQL 資料庫引擎
- DB2/NT
- MySQL
- PostgreSQL
- FrontBase
- Oracle
- Microsoft SQL Server 資料庫
- Sybase SQL Server
- Informix Dynamic Server
支援的技術
Hibernate 支援各種其他技術,包括 -
- XDoclet Spring
- J2EE
- Eclipse 外掛
- Maven
Hibernate - 架構
Hibernate 具有分層架構,有助於使用者無需瞭解底層 API 即可操作。Hibernate 利用資料庫和配置資料為應用程式提供永續性服務(和永續性物件)。
以下是 Hibernate 應用程式架構的非常高級別的檢視。
以下是 Hibernate 應用程式架構及其重要核心類的詳細檢視。
Hibernate 使用各種現有的 Java API,如 JDBC、Java 事務 API (JTA) 和 Java 命名和目錄介面 (JNDI)。JDBC 提供了關係資料庫常見功能的基本抽象級別,允許 Hibernate 支援幾乎任何具有 JDBC 驅動程式的資料庫。JNDI 和 JTA 允許 Hibernate 與 J2EE 應用程式伺服器整合。
以下部分簡要介紹了 Hibernate 應用程式架構中涉及的每個類物件。
配置物件
Configuration 物件是您在任何 Hibernate 應用程式中建立的第一個 Hibernate 物件。通常在應用程式初始化時僅建立一次。它表示 Hibernate 所需的配置檔案或屬性檔案。
Configuration 物件提供兩個關鍵元件 -
**資料庫連線** - 這透過 Hibernate 支援的一個或多個配置檔案來處理。這些檔案是 **hibernate.properties** 和 **hibernate.cfg.xml**。
**類對映設定** - 此元件建立 Java 類和資料庫表之間的連線。
SessionFactory 物件
SessionFactory 物件使用提供的配置檔案為應用程式配置 Hibernate,並允許例項化 Session 物件。SessionFactory 是一個執行緒安全的物件,並由應用程式的所有執行緒使用。
SessionFactory 是一個重量級物件;它通常在應用程式啟動時建立,並保留以供以後使用。每個資料庫可以使用單獨的配置檔案來使用一個 SessionFactory 物件。因此,如果您使用多個數據庫,則必須建立多個 SessionFactory 物件。
Session 物件
Session 用於獲取與資料庫的物理連線。Session 物件是輕量級的,旨在每次需要與資料庫互動時例項化。持久物件是透過 Session 物件儲存和檢索的。
Session 物件不應長時間保持開啟狀態,因為它們通常不是執行緒安全的,並且應根據需要建立和銷燬它們。
Transaction 物件
Transaction 表示與資料庫的工作單元,大多數 RDBMS 都支援事務功能。Hibernate 中的事務由底層事務管理器和事務(來自 JDBC 或 JTA)處理。
這是一個可選的物件,Hibernate 應用程式可以選擇不使用此介面,而是在其自己的應用程式程式碼中管理事務。
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
Metadata meta = new MetadataSources(ssr).getMetadataBuilder().build();
// Create the SessionFactory Instance
SessionFactory factory = meta.getSessionFactoryBuilder().build();
// Create the session
Session session = factory.openSession();
// Create the transaction
Transaction t = session.beginTransaction();
Query 物件
Query 物件使用 SQL 或 Hibernate 查詢語言 (HQL) 字串從資料庫檢索資料並建立物件。Query 例項用於繫結查詢引數、限制查詢返回的結果數,以及最終執行查詢。
Criteria 物件
Criteria 物件用於建立和執行面向物件的 Criteria 查詢以檢索物件。
Hibernate - 環境設定
本章介紹如何安裝 Hibernate 和其他關聯包,以準備用於 Hibernate 應用程式的環境。我們將使用 MySQL 資料庫來試驗 Hibernate 示例,因此請確保您已準備好 MySQL 資料庫的設定。有關 MySQL 的更多詳細資訊,您可以檢視我們的 MySQL 教程。
下載 Hibernate
假設您已經在系統上安裝了最新版本的 Java。以下是在系統上下載和安裝 Hibernate 的簡單步驟 -
選擇是要在 Windows 上安裝 Hibernate 還是在 Unix 上安裝,然後繼續執行下一步,下載 Windows 的 .zip 檔案和 Unix 的 .tz 檔案。
從 http://www.hibernate.org/downloads 下載最新版本的 Hibernate。
在撰寫本教程時,我從mvnrepository下載了hibernate-distribution 5.3.1.Final,解壓縮下載的檔案後,會得到如下所示的目錄結構。
安裝Hibernate
下載並解壓縮最新版本的Hibernate安裝檔案後,您需要執行以下兩個簡單的步驟。確保您正確設定了CLASSPATH變數,否則在編譯應用程式時會遇到問題。
現在,將/lib中的所有庫檔案複製到您的CLASSPATH中,並更改您的classpath變數以包含所有JAR檔案 -
最後,將hibernate3.jar檔案複製到您的CLASSPATH中。此檔案位於安裝的根目錄中,是Hibernate執行其工作所需的主要JAR檔案。
Hibernate先決條件
以下是Hibernate所需軟體包/庫的列表,您應該在開始使用Hibernate之前安裝它們。要安裝這些軟體包,您需要將庫檔案從/lib複製到您的CLASSPATH中,並相應地更改您的CLASSPATH變數。
| 序號 | 軟體包/庫 |
|---|---|
| 1 | MySQL Connector/J |
| 2 | Java EE Java EE API J2EE API |
Hibernate可選先決條件
以下是Hibernate所需可選軟體包/庫的列表,您可以安裝它們來開始使用Hibernate。要安裝這些軟體包,您需要將庫檔案從/lib複製到您的CLASSPATH中,並相應地更改您的CLASSPATH變數。
| 序號 | 軟體包/庫 |
|---|---|
| 1 | dom4j XML解析 www.dom4j.org/ |
| 2 | Xalan XSLT處理器 https://xml.apache.org/xalan-j/ |
| 3 | Xerces Xerces Java解析器 https://xml.apache.org/xerces-j/ |
| 4 | cglib 在執行時對Java類進行適當的更改 http://cglib.sourceforge.net/ |
| 5 | log4j |
| 6 | Commons 日誌記錄、電子郵件等 https://jakarta.apache.org/commons |
| 7 | SLF4J Java日誌門面 https://www.slf4j.org |
Hibernate - 配置
Hibernate需要提前知道在哪裡可以找到定義Java類如何與資料庫表相關聯的對映資訊。Hibernate還需要一組與資料庫和其他相關引數相關的配置設定。所有這些資訊通常都作為名為hibernate.properties的標準Java屬性檔案或名為hibernate.cfg.xml的XML檔案提供。
在我的示例中,我將考慮使用XML格式的檔案hibernate.cfg.xml來指定所需的Hibernate屬性。大多數屬性都採用其預設值,並且無需在屬性檔案中指定它們,除非確實需要。此檔案儲存在應用程式類路徑的根目錄中。
Hibernate屬性
以下是您在獨立情況下需要為資料庫配置的重要屬性列表 -
| 序號 | 屬性和描述 |
|---|---|
| 1 |
hibernate.dialect 此屬性使Hibernate為所選資料庫生成適當的SQL。 |
| 2 | hibernate.connection.driver_class JDBC驅動程式類。 |
| 3 | hibernate.connection.url 到資料庫例項的JDBC URL。 |
| 4 | hibernate.connection.username 資料庫使用者名稱。 |
| 5 | hibernate.connection.password 資料庫密碼。 |
| 6 | hibernate.connection.pool_size 限制Hibernate資料庫連線池中等待的連線數。 |
| 7 | hibernate.connection.autocommit 允許對JDBC連線使用自動提交模式。 |
如果您正在使用資料庫以及應用程式伺服器和JNDI,那麼您需要配置以下屬性 -
| 序號 | 屬性和描述 |
|---|---|
| 1 | hibernate.connection.datasource 在您用於應用程式的應用程式伺服器上下文中定義的JNDI名稱。 |
| 2 | hibernate.jndi.class JNDI的InitialContext類。 |
| 3 | hibernate.jndi.<JNDIpropertyname> 將您喜歡的任何JNDI屬性傳遞給JNDI InitialContext。 |
| 4 | hibernate.jndi.url 提供JNDI的URL。 |
| 5 | hibernate.connection.username 資料庫使用者名稱。 |
| 6 | hibernate.connection.password 資料庫密碼。 |
Hibernate與MySQL資料庫
MySQL是當今最流行的開源資料庫系統之一。讓我們建立hibernate.cfg.xml配置檔案並將其放在應用程式類路徑的根目錄中。您需要確保您的MySQL資料庫中存在testdb資料庫,並且您有一個名為test的使用者可以訪問該資料庫。
XML配置檔案必須符合Hibernate 3 Configuration DTD,該DTD可在 http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd 找到。
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 5.3//EN"
"http://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>
<property name="connection.url">jdbc:mysql:///TUTORIALSPOINT</property>
<property name="connection.username">root</property>
<property name="connection.password">guest123</property>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<mapping resource="employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
上述配置檔案包含<mapping>標籤,這些標籤與hibernatemapping檔案相關,我們將在下一章中瞭解hibernate對映檔案到底是什麼以及如何以及為什麼要使用它?
hbm2ddl.auto屬性
Hibernate中的hbm2ddl.auto屬性定義瞭如何處理資料庫模式。可能的值為
create - 如果值為'create',則在建立SessionFactory物件時,Hibernate會在資料庫中建立一個新表。如果資料庫中存在具有相同名稱的表,則會刪除該表以及資料,並建立一個新表。
update - 如果值為'update',則Hibernate首先驗證資料庫中是否存在該表。如果存在,則根據更改修改該表。如果不存在,則建立一個新的表。
validate - 如果值為'validate',則Hibernate僅驗證表是否存在。如果表不存在,則會丟擲異常。
create-drop - 如果值為'create-drop',則在建立SessionFactory時,Hibernate會建立一個新表,執行所需的運算,並在銷燬SessionFactory時刪除該表。此值用於測試Hibernate程式碼。
none - 它不會對模式進行任何更改。
Hibernate方言
資料庫方言是一個配置選項,它允許軟體將通用SQL語句轉換為特定於供應商的DDL和DML。不同的資料庫產品,如PostgreSQL、MySQL、Oracle和SQL Server,都有自己的SQL變體,稱為SQL方言。
以下是各種重要資料庫方言屬性型別的列表 -
| 序號 | 資料庫和方言屬性 |
|---|---|
| 1 | Caché 2007.1 org.hibernate.dialect.Cache71Dialect |
| 2 | DB2 org.hibernate.dialect.DB2Dialect |
| 3 | DB2/390 org.hibernate.dialect.DB2390Dialect |
| 4 | DB2/400 org.hibernate.dialect.DB2400Dialect |
| 5 | Cloudscape 10 - 也稱為Derby。 org.hibernate.dialect.DerbyDialect |
| 6 | Firebird org.hibernate.dialect.FirebirdDialect |
| 7 | FrontBase org.hibernate.dialect.FrontBaseDialect |
| 8 | H2 org.hibernate.dialect.H2Dialect |
| 9 | HSQLDB(HyperSQL) org.hibernate.dialect.HSQLDialect |
| 10 | Informix org.hibernate.dialect.InformixDialect |
| 11 | Ingres 9.2 org.hibernate.dialect.IngresDialect |
| 12 | Ingres 9.3及更高版本 org.hibernate.dialect.Ingres9Dialect |
| 13 | Ingres 10及更高版本 org.hibernate.dialect.Ingres10Dialect |
| 14 | Interbase org.hibernate.dialect.InterbaseDialect |
| 15 | Microsoft SQL Server 2000 org.hibernate.dialect.SQLServerDialect |
| 16 | Microsoft SQL Server 2005 org.hibernate.dialect.SQLServerDialect |
| 17 | Microsoft SQL Server 2008 org.hibernate.dialect.SQLServer2008Dialect |
| 18 | MySQL(5.x之前) org.hibernate.dialect.MySQLDialect |
| 19 | MySQL 5.x org.hibernate.dialect.MySQL5Dialect |
| 20 | Oracle 8i org.hibernate.dialect.Oracle8iDialect |
| 21 | Oracle 9i org.hibernate.dialect.Oracle9iDialect |
| 22 | Oracle 10g org.hibernate.dialect.Oracle10gDialect |
| 23 | Oracle 11g org.hibernate.dialect.Oracle10gDialect |
| 24 | Pointbase org.hibernate.dialect.PointbaseDialect |
| 25 | PostgreSQL org.hibernate.dialect.PostgreSQLDialect |
| 26 | PostgreSQL Plus org.hibernate.dialect.PostgrePlusDialect |
| 27 | Progress org.hibernate.dialect.ProgressDialect |
| 28 | Unisys 2200關係資料庫(RDMS) org.hibernate.dialect.RDMSOS2200Dialect |
| 29 | SAP DB org.hibernate.dialect.SAPDBDialect |
| 30 | Sybase 11.9.2 org.hibernate.dialect.Sybase11Dialect |
| 31 | Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect |
| 32 | Sybase Adaptive Server Enterprise (ASE) 15 org.hibernate.dialect.SybaseASE15Dialect |
| 33 | Teradata org.hibernate.dialect.TeradataDialect |
| 34 | TimesTen 5.1 org.hibernate.dialect.TimesTenDialect |
Hibernate - 會話
Session 用於獲取與資料庫的物理連線。Session 物件是輕量級的,旨在每次需要與資料庫互動時例項化。持久物件是透過 Session 物件儲存和檢索的。
會話物件不應長時間保持開啟狀態,因為它們通常不是執行緒安全的,並且應根據需要建立和銷燬它們。Session的主要功能是為對映實體類的例項提供建立、讀取和刪除操作。
在給定時間點,例項可能存在於以下三種狀態之一 -
瞬時 - 持久類的新例項,它與Session無關,在資料庫中沒有表示,也沒有識別符號值,Hibernate認為它是瞬時的。
持久化 - 您可以透過將其與Session關聯來使瞬時例項持久化。持久化例項在資料庫中具有表示,具有識別符號值,並且與Session相關聯。
脫管 - 關閉Hibernate Session後,持久化例項將變為脫管例項。
如果其持久化類是可序列化的,則Session例項是可序列化的。一個典型的交易應該使用以下習慣用法 -
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// do some work
...
tx.commit();
}
catch (Exception e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
如果Session丟擲異常,則必須回滾事務,並且必須丟棄會話。
Session介面方法
Session介面提供了許多方法,但我只列出了一些重要方法,我們將在本教程中使用這些方法。您可以檢視Hibernate文件以獲取與Session和SessionFactory相關的所有方法的完整列表。
| 序號 | Session方法和描述 |
|---|---|
| 1 |
Transaction beginTransaction() 開始一個工作單元並返回關聯的Transaction物件。 |
| 2 |
void cancelQuery() 取消當前查詢的執行。 |
| 3 |
void clear() 完全清除會話。 |
| 4 |
Connection close() 透過釋放JDBC連線並清理來結束會話。 |
| 5 |
Criteria createCriteria(Class persistentClass) 為給定的實體類或實體類的超類建立一個新的Criteria例項。 |
| 6 |
Criteria createCriteria(String entityName) 為給定的實體名稱建立一個新的Criteria例項。 |
| 7 |
Serializable getIdentifier(Object object) 返回給定實體的識別符號值,如與該會話相關聯。 |
| 8 |
Query createFilter(Object collection, String queryString) 為給定的集合和過濾字串建立一個新的 Query 例項。 |
| 9 |
Query createQuery(String queryString) 為給定的 HQL 查詢字串建立一個新的 Query 例項。 |
| 10 |
SQLQuery createSQLQuery(String queryString) 為給定的 SQL 查詢字串建立一個新的 SQLQuery 例項。 |
| 11 |
void delete(Object object) 從資料儲存中刪除一個持久化例項。 |
| 12 |
void delete(String entityName, Object object) 從資料儲存中刪除一個持久化例項。 |
| 13 |
Session get(String entityName, Serializable id) 返回具有給定識別符號的給定命名實體的持久化例項,如果不存在此類持久化例項,則返回 null。 |
| 14 |
SessionFactory getSessionFactory() 獲取建立此會話的 SessionFactory。 |
| 15 |
void refresh(Object object) 從底層資料庫重新讀取給定例項的狀態。 |
| 16 |
Transaction getTransaction() 獲取與此會話關聯的 Transaction 例項。 |
| 17 |
boolean isConnected() 檢查會話當前是否已連線。 |
| 18 |
boolean isDirty() 此會話是否包含必須與資料庫同步的任何更改? |
| 19 |
boolean isOpen() 檢查會話是否仍然開啟。 |
| 20 |
Serializable save(Object object) 持久化給定的瞬態例項,首先分配一個生成的識別符號。 |
| 21 |
void saveOrUpdate(Object object) 儲存 (Object) 或更新 (Object) 給定例項。 |
| 22 |
void update(Object object) 使用給定分離例項的識別符號更新持久化例項。 |
| 23 |
void update(String entityName, Object object) 使用給定分離例項的識別符號更新持久化例項。 |
Hibernate - 持久類
Hibernate 的核心思想是從 Java 類屬性中獲取值並將其持久化到資料庫表中。對映文件幫助 Hibernate 確定如何從類中提取值並將其與表和相關欄位對映。
其物件或例項將儲存在資料庫表中的 Java 類在 Hibernate 中稱為持久化類。如果這些類遵循一些簡單的規則(也稱為**普通舊 Java 物件**(POJO)程式設計模型),Hibernate 的工作效果最佳。
持久化類有以下主要規則,但是,這些規則都不是硬性要求 -
所有將被持久化的 Java 類都需要一個預設建構函式。
所有類都應包含一個 ID,以便於在 Hibernate 和資料庫中輕鬆識別您的物件。此屬性對映到資料庫表的 primary key 列。
所有將被持久化的屬性都應宣告為 private,並在 JavaBean 樣式中定義**getXXX** 和**setXXX** 方法。
Hibernate 的一個核心特性,代理,依賴於持久化類要麼是非 final 的,要麼是實現一個宣告所有公共方法的介面。
所有不擴充套件或實現 EJB 框架所需的某些專門類和介面的類。
POJO 名稱用於強調給定物件是一個普通的 Java 物件,而不是一個特殊物件,尤其是指的不是 Enterprise JavaBean。
簡單的 POJO 示例
根據上面提到的幾條規則,我們可以定義一個 POJO 類如下 -
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
Hibernate - 對映檔案
物件/關係對映通常在 XML 文件中定義。此對映檔案指示 Hibernate - 如何將定義的類或類對映到資料庫表?
儘管許多 Hibernate 使用者選擇手動編寫 XML,但存在許多工具可以生成對映文件。對於高階 Hibernate 使用者,這些工具包括**XDoclet、Middlegen** 和**AndroMDA**。
讓我們考慮我們之前定義的 POJO 類,其物件將持久化到下一節中定義的表中。
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
對於您希望提供持久化的每個物件,都會有一個相應的表。考慮以上物件需要儲存和檢索到以下 RDBMS 表中 -
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
基於以上兩個實體,我們可以定義以下對映檔案,該檔案指示 Hibernate 如何將定義的類或類對映到資料庫表。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
您應該將對映文件儲存在格式為<classname>.hbm.xml 的檔案中。我們將對映文件儲存在 Employee.hbm.xml 檔案中。
讓我們瞭解一下對映檔案中使用的對映元素的詳細資訊 -
對映文件是一個 XML 文件,其根元素為**<hibernate-mapping>**,其中包含所有**<class>** 元素。
**<class>** 元素用於定義從 Java 類到資料庫表的特定對映。Java 類名使用 class 元素的**name** 屬性指定,資料庫**table** 名使用 table 屬性指定。
**<meta>** 元素是可選元素,可用於建立類描述。
**<id>** 元素將類中唯一的 ID 屬性對映到資料庫表的 primary key。id 元素的**name** 屬性引用類中的屬性,**column** 屬性引用資料庫表中的列。**type** 屬性儲存 hibernate 對映型別,此對映型別將 Java 轉換為 SQL 資料型別。
id 元素內的**<generator>** 元素用於自動生成 primary key 值。generator 元素的**class** 屬性設定為**native**,以便 hibernate 根據底層資料庫的功能選擇**identity、sequence** 或**hilo** 演算法來建立 primary key。
**<property>** 元素用於將 Java 類屬性對映到資料庫表中的列。該元素的**name** 屬性引用類中的屬性,**column** 屬性引用資料庫表中的列。**type** 屬性儲存 hibernate 對映型別,此對映型別將 Java 轉換為 SQL 資料型別。
還有其他屬性和元素可用,這些屬性和元素將在對映文件中使用,在討論其他與 Hibernate 相關的主題時,我會盡量涵蓋儘可能多的屬性和元素。
Hibernate - 對映型別
當您準備 Hibernate 對映文件時,您會發現您將 Java 資料型別對映到 RDBMS 資料型別。對映檔案中宣告和使用的**型別**既不是 Java 資料型別,也不是 SQL 資料庫型別。這些型別稱為**Hibernate 對映型別**,可以將 Java 轉換為 SQL 資料型別,反之亦然。
本章列出了所有基本、日期和時間、大型物件以及各種其他內建對映型別。
原始型別
| 對映型別 | Java 型別 | ANSI SQL 型別 |
|---|---|---|
| integer | int 或 java.lang.Integer | INTEGER |
| long | long 或 java.lang.Long | BIGINT |
| short | short 或 java.lang.Short | SMALLINT |
| float | float 或 java.lang.Float | FLOAT |
| double | double 或 java.lang.Double | DOUBLE |
| big_decimal | java.math.BigDecimal | NUMERIC |
| character | java.lang.String | CHAR(1) |
| string | java.lang.String | VARCHAR |
| byte | byte 或 java.lang.Byte | TINYINT |
| boolean | boolean 或 java.lang.Boolean | BIT |
| yes/no | boolean 或 java.lang.Boolean | CHAR(1) ('Y' 或 'N') |
| true/false | boolean 或 java.lang.Boolean | CHAR(1) ('T' 或 'F') |
日期和時間型別
| 對映型別 | Java 型別 | ANSI SQL 型別 |
|---|---|---|
| date | java.util.Date 或 java.sql.Date | DATE |
| time | java.util.Date 或 java.sql.Time | TIME |
| timestamp | java.util.Date 或 java.sql.Timestamp | TIMESTAMP |
| calendar | java.util.Calendar | TIMESTAMP |
| calendar_date | java.util.Calendar | DATE |
二進位制和大物件型別
| 對映型別 | Java 型別 | ANSI SQL 型別 |
|---|---|---|
| binary | byte[] | VARBINARY(或 BLOB) |
| text | java.lang.String | CLOB |
| serializable | 實現 java.io.Serializable 的任何 Java 類 | VARBINARY(或 BLOB) |
| clob | java.sql.Clob | CLOB |
| blob | java.sql.Blob | BLOB |
JDK 相關型別
| 對映型別 | Java 型別 | ANSI SQL 型別 |
|---|---|---|
| class | java.lang.Class | VARCHAR |
| locale | java.util.Locale | VARCHAR |
| timezone | java.util.TimeZone | VARCHAR |
| currency | java.util.Currency | VARCHAR |
Hibernate - 示例
現在讓我們舉一個例子來了解如何使用 Hibernate 在獨立應用程式中提供 Java 永續性。我們將逐步介紹使用 Hibernate 技術建立 Java 應用程式的不同步驟。
建立 POJO 類
建立應用程式的第一步是構建 Java POJO 類或類,具體取決於將持久化到資料庫的應用程式。讓我們考慮我們的**Employee** 類,它具有**getXXX** 和**setXXX** 方法,使其成為符合 JavaBeans 的類。
POJO(普通舊 Java 物件)是一個 Java 物件,它不擴充套件或實現 EJB 框架分別所需的某些專門類和介面。所有正常的 Java 物件都是 POJO。
當您設計一個類由 Hibernate 持久化時,提供符合 JavaBeans 的程式碼以及一個屬性很重要,該屬性將充當索引,例如**Employee** 類中的**id** 屬性。
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
建立資料庫表
第二步是在您的資料庫中建立表。對於您希望提供持久化的每個物件,都會有一個相應的表。考慮以上物件需要儲存和檢索到以下 RDBMS 表中 -
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
建立對映配置檔案
此步驟是建立一個對映檔案,該檔案指示 Hibernate 如何將定義的類或類對映到資料庫表。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
您應該將對映文件儲存在格式為<classname>.hbm.xml 的檔案中。我們將對映文件儲存在 Employee.hbm.xml 檔案中。讓我們瞭解一下對映文件的詳細資訊 -
對映文件是一個 XML 文件,其根元素為<hibernate-mapping>,其中包含所有<class> 元素。
**<class>** 元素用於定義從 Java 類到資料庫表的特定對映。Java 類名使用 class 元素的**name** 屬性指定,資料庫表名使用**table** 屬性指定。
**<meta>** 元素是可選元素,可用於建立類描述。
**<id>** 元素將類中唯一的 ID 屬性對映到資料庫表的 primary key。id 元素的**name** 屬性引用類中的屬性,**column** 屬性引用資料庫表中的列。**type** 屬性儲存 hibernate 對映型別,此對映型別將 Java 轉換為 SQL 資料型別。
id 元素內的**<generator>** 元素用於自動生成 primary key 值。generator 元素的**class** 屬性設定為**native**,以便 hibernate 根據底層資料庫的功能選擇**identity、sequence** 或**hilo** 演算法來建立 primary key。
**<property>** 元素用於將 Java 類屬性對映到資料庫表中的列。該元素的**name** 屬性引用類中的屬性,**column** 屬性引用資料庫表中的列。**type** 屬性儲存 hibernate 對映型別,此對映型別將 Java 轉換為 SQL 資料型別。
還有其他屬性和元素可用,這些屬性和元素將在對映文件中使用,在討論其他與 Hibernate 相關的主題時,我會盡量涵蓋儘可能多的屬性和元素。
建立應用程式類
最後,我們將建立我們的應用程式類,其中包含 main() 方法來執行應用程式。我們將使用此應用程式儲存一些 Employee 的記錄,然後我們將對這些記錄應用 CRUD 操作。
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 1000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 10000);
/* List down all the employees */
ME.listEmployees();
/* Update employee's records */
ME.updateEmployee(empID1, 5000);
/* Delete an employee from the database */
ME.deleteEmployee(empID2);
/* List down new list of the employees */
ME.listEmployees();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees */
public void listEmployees( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
List employees = session.createQuery("FROM Employee").list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to UPDATE salary for an employee */
public void updateEmployee(Integer EmployeeID, int salary ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
employee.setSalary( salary );
session.update(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to DELETE an employee from the records */
public void deleteEmployee(Integer EmployeeID){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
session.delete(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
編譯和執行
以下是編譯和執行上述應用程式的步驟。在繼續進行編譯和執行之前,請確保您已正確設定 PATH 和 CLASSPATH。
建立 hibernate.cfg.xml 配置檔案,如配置章節中所述。
建立 Employee.hbm.xml 對映檔案,如上所示。
建立 Employee.java 原始檔,如上所示,並編譯它。
建立 ManageEmployee.java 原始檔,如上所示,並編譯它。
執行 ManageEmployee 二進位制檔案以執行程式。
輸出
您將獲得以下結果,並且記錄將建立在 EMPLOYEE 表中。
$java ManageEmployee .......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ First Name: Zara Last Name: Ali Salary: 1000 First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 10000 First Name: Zara Last Name: Ali Salary: 5000 First Name: John Last Name: Paul Salary: 10000
如果您檢查您的 EMPLOYEE 表,它應該包含以下記錄 -
mysql> select * from EMPLOYEE; +----+------------+-----------+--------+ | id | first_name | last_name | salary | +----+------------+-----------+--------+ | 29 | Zara | Ali | 5000 | | 31 | John | Paul | 10000 | +----+------------+-----------+--------+ 2 rows in set (0.00 sec mysql>
Hibernate - O/R 對映
到目前為止,我們已經瞭解了使用Hibernate進行非常基本的O/R對映,但是還有三個最重要的對映主題,我們需要詳細學習。
它們是:
- 集合的對映,
- 實體類之間關聯的對映,以及
- 元件對映。
集合對映
如果一個實體或類對某個特定變數具有值的集合,那麼我們可以使用Java中提供的任何一個集合介面來對映這些值。Hibernate可以持久化java.util.Map、java.util.Set、java.util.SortedMap、java.util.SortedSet、java.util.List以及任何持久化實體或值的陣列的例項。
| 序號 | 集合型別與對映描述 |
|---|---|
| 1 | java.util.Set
這與<set>元素對映,並使用java.util.HashSet初始化。 |
| 2 | java.util.SortedSet
這與<set>元素對映,並使用java.util.TreeSet初始化。sort屬性可以設定為比較器或自然排序。 |
| 3 | java.util.List
這與<list>元素對映,並使用java.util.ArrayList初始化。 |
| 4 | java.util.Collection
這與<bag>或<ibag>元素對映,並使用java.util.ArrayList初始化。 |
| 5 | java.util.Map
這與<map>元素對映,並使用java.util.HashMap初始化。 |
| 6 | java.util.SortedMap
這與<map>元素對映,並使用java.util.TreeMap初始化。sort屬性可以設定為比較器或自然排序。 |
Hibernate支援使用<primitive-array>用於Java基本值型別和<array>用於其他所有型別的陣列。但是,它們很少使用,因此我不會在本教程中討論它們。
如果要對映Hibernate不支援的使用者定義的集合介面,則需要告訴Hibernate自定義集合的語義,這並不容易,也不建議使用。
關聯對映
實體類之間關聯的對映以及表之間的關係是ORM的核心。以下是表示物件之間關係基數的四種方式。關聯對映可以是單向的,也可以是雙向的。
| 序號 | 對映型別與描述 |
|---|---|
| 1 | 多對一
使用Hibernate對映多對一關係 |
| 2 | 一對一
使用Hibernate對映一對一關係 |
| 3 | 一對多
使用Hibernate對映一對多關係 |
| 4 | 多對多
使用Hibernate對映多對多關係 |
元件對映
實體類很可能具有對另一個類作為成員變數的引用。如果被引用的類沒有自己的生命週期,並且完全依賴於擁有實體類的生命週期,那麼被引用的類因此被稱為元件類。
元件集合的對映也以類似於常規集合對映的方式進行,只是配置略有不同。我們將透過示例詳細瞭解這兩種對映。
| 序號 | 對映型別與描述 |
|---|---|
| 1 | 元件對映
為具有對另一個類作為成員變數的引用的類進行對映。 |
Hibernate - 級聯型別
在hbm檔案中配置集合時,以及在一對多、多對多對映中,hbm檔案中的集合元素(例如,list)包含一個屬性cascade。
示例
...
<class name="Student" table="student_tbl_100">
<id name="studentid">
<generator class="native"></generator>
</id>
<property name="name"></property>
<map name="courses" table="course_tbl_100" cascade="all">
<key column="id"></key>
<index column="course_id" type="string"></index>
<element column="course_name" type="string"></element>
</map>
</class>
...
如下所示,可以在註解中提及級聯型別
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private Set<Order> orders = new HashSet<>();
// getters and setters
}
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
// getters and setters
}
當持久化、更新或刪除Customer實體時,所有關聯的Order實體也將被持久化、更新或刪除。
Hibernate中的不同級聯型別
Hibernate提供了多種型別的級聯選項,可用於管理實體之間的關係。以下是Hibernate中的不同級聯型別
CascadeType.ALL - Hibernate中的一種級聯型別,指定所有狀態轉換(建立、更新、刪除和重新整理)都應從父實體級聯到子實體。
CascadeType.PERSIST - Hibernate中的一種級聯型別,指定建立(或持久化)操作應從父實體級聯到子實體。
CascadeType.MERGE - Hibernate中的一種級聯型別,指定更新(或合併)操作應從父實體級聯到子實體。
CascadeType.REMOVE - Hibernate中的一種級聯型別,指定刪除操作應從父實體級聯到子實體。
CascadeType.REFRESH - Hibernate中的一種級聯型別,指定重新整理操作應從父實體級聯到子實體。
CascadeType.DETACH - Hibernate中的一種級聯型別,指定分離操作應從父實體級聯到子實體。
CascadeType.REPLICATE - Hibernate中的一種級聯型別,指定複製操作應從父實體級聯到子實體。
CascadeType.SAVE_UPDATE - Hibernate中的一種級聯型別,指定儲存或更新操作應從父實體級聯到子實體。
這些級聯型別可以單獨使用或組合使用,以根據應用程式的要求管理實體之間的關係。必須謹慎使用級聯型別,因為如果使用不當,可能會導致意外後果。
Hibernate - 註解
到目前為止,您已經瞭解了Hibernate如何使用XML對映檔案將資料從POJO轉換為資料庫表,反之亦然。Hibernate註解是定義對映的最新方法,無需使用XML檔案。您可以將註解與XML對映元資料一起使用或作為XML對映元資料的替代。
Hibernate註解是提供物件和關係表對映元資料的強大方法。所有元資料都與程式碼一起組合到POJO Java檔案中,這有助於使用者在開發過程中同時理解表結構和POJO。
如果要使應用程式可移植到其他符合EJB 3的ORM應用程式,則必須使用註解來表示對映資訊,但如果您希望獲得更大的靈活性,則應使用基於XML的對映。
Hibernate註解的環境設定
首先,您必須確保您使用的是JDK 5.0,否則您需要將JDK升級到JDK 5.0才能利用對註解的原生支援。
其次,您需要安裝Hibernate 3.x註解分發包,該包可從sourceforge獲取:(下載Hibernate註解)並將hibernate-annotations.jar、lib/hibernate-comons-annotations.jar和lib/ejb3-persistence.jar從Hibernate註解分發複製到您的CLASSPATH中。
帶註解的類示例
如上所述,在使用Hibernate註解時,所有元資料都與程式碼一起組合到POJO Java檔案中,這有助於使用者在開發過程中同時理解表結構和POJO。
假設我們將使用以下EMPLOYEE表來儲存我們的物件:
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
以下是使用註解將Employee類與定義的EMPLOYEE表對映的對映:
import javax.persistence.*;
@Entity
@Table(name = "EMPLOYEE")
public class Employee {
@Id @GeneratedValue
@Column(name = "id")
private int id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "salary")
private int salary;
public Employee() {}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
Hibernate檢測到@Id註解位於欄位上,並假設它應該在執行時透過欄位直接訪問物件的屬性。如果將@Id註解放在getId()方法上,則預設情況下將啟用透過getter和setter方法訪問屬性。因此,所有其他註解也放置在欄位或getter方法上,遵循所選策略。
以下部分將解釋上述類中使用的註解。
@Entity註解
EJB 3標準註解包含在javax.persistence包中,因此我們首先匯入此包。其次,我們將@Entity註解用於Employee類,這將此類標記為實體bean,因此它必須具有一個可見性至少為受保護範圍的無引數建構函式。
@Table註解
@Table註解允許您指定將用於在資料庫中持久化實體的表的詳細資訊。
@Table註解提供了四個屬性,允許您覆蓋表名稱、目錄和模式,並在表中的列上強制唯一約束。目前,我們只使用表名,即EMPLOYEE。
@Id和@GeneratedValue註解
每個實體bean都將具有主鍵,您可以在類上使用@Id註解對其進行註釋。主鍵可以是單個欄位,也可以是多個欄位的組合,具體取決於您的表結構。
預設情況下,@Id註解將自動確定要使用的最合適的PrimaryKey生成策略,但您可以透過應用@GeneratedValue註解來覆蓋此策略,該註解有兩個引數strategy和generator,我不會在這裡討論它們,因此讓我們只使用預設金鑰生成策略。讓Hibernate確定要使用哪個生成器型別可以使您的程式碼在不同的資料庫之間可移植。
@Column註解
@Column註解用於指定將欄位或屬性對映到的列的詳細資訊。您可以將列註解與以下最常用的屬性一起使用:
name屬性允許顯式指定列的名稱。
length屬性允許指定用於對映值的列的大小,特別是對於字串值。
nullable屬性允許在生成模式時將列標記為NOT NULL。
unique屬性允許將列標記為僅包含唯一值。
建立應用程式類
最後,我們將建立我們的應用程式類,其中包含 main() 方法來執行應用程式。我們將使用此應用程式儲存一些 Employee 的記錄,然後我們將對這些記錄應用 CRUD 操作。
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new AnnotationConfiguration().
configure().
//addPackage("com.xyz") //add package if used.
addAnnotatedClass(Employee.class).
buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 1000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 10000);
/* List down all the employees */
ME.listEmployees();
/* Update employee's records */
ME.updateEmployee(empID1, 5000);
/* Delete an employee from the database */
ME.deleteEmployee(empID2);
/* List down new list of the employees */
ME.listEmployees();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee();
employee.setFirstName(fname);
employee.setLastName(lname);
employee.setSalary(salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees */
public void listEmployees( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
List employees = session.createQuery("FROM Employee").list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to UPDATE salary for an employee */
public void updateEmployee(Integer EmployeeID, int salary ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
employee.setSalary( salary );
session.update(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to DELETE an employee from the records */
public void deleteEmployee(Integer EmployeeID){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
session.delete(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
資料庫配置
現在讓我們建立hibernate.cfg.xml配置檔案以定義與資料庫相關的引數。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql:///test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
cohondob
</property>
</session-factory>
</hibernate-configuration>
編譯和執行
以下是編譯和執行上述應用程式的步驟。在繼續進行編譯和執行之前,請確保您已正確設定 PATH 和 CLASSPATH。
從路徑中刪除Employee.hbm.xml對映檔案。
建立 Employee.java 原始檔,如上所示,並編譯它。
建立 ManageEmployee.java 原始檔,如上所示,並編譯它。
執行 ManageEmployee 二進位制檔案以執行程式。
您將獲得以下結果,並且記錄將被建立到EMPLOYEE表中。
$java ManageEmployee .......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ First Name: Zara Last Name: Ali Salary: 1000 First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 10000 First Name: Zara Last Name: Ali Salary: 5000 First Name: John Last Name: Paul Salary: 10000
如果您檢查您的 EMPLOYEE 表,它應該包含以下記錄 -
mysql> select * from EMPLOYEE; +----+------------+-----------+--------+ | id | first_name | last_name | salary | +----+------------+-----------+--------+ | 29 | Zara | Ali | 5000 | | 31 | John | Paul | 10000 | +----+------------+-----------+--------+ 2 rows in set (0.00 sec mysql>
Hibernate - 查詢語言
Hibernate查詢語言(HQL)是一種面向物件的查詢語言,類似於SQL,但它不是對錶和列進行操作,而是對持久化物件及其屬性進行操作。HQL查詢由Hibernate轉換為傳統的SQL查詢,然後由SQL查詢對資料庫執行操作。
雖然您可以使用Hibernate直接使用本地SQL語句,但我建議儘可能使用HQL以避免資料庫可移植性問題,並利用Hibernate的SQL生成和快取策略。
像SELECT、FROM和WHERE等關鍵字不區分大小寫,但表和列名稱等屬性在HQL中區分大小寫。
FROM子句
如果要將完整的持久化物件載入到記憶體中,則可以使用FROM子句。以下是使用FROM子句的簡單語法:
String hql = "FROM Employee"; Query query = session.createQuery(hql); List results = query.list();
如果需要在HQL中完全限定類名,只需按如下方式指定包和類名:
String hql = "FROM com.hibernatebook.criteria.Employee"; Query query = session.createQuery(hql); List results = query.list();
AS子句
AS子句可用於為HQL查詢中的類分配別名,尤其是在查詢較長的情況下。例如,我們之前的簡單示例將如下所示:
String hql = "FROM Employee AS E"; Query query = session.createQuery(hql); List results = query.list();
AS關鍵字是可選的,您也可以在類名後直接指定別名,如下所示:
String hql = "FROM Employee E"; Query query = session.createQuery(hql); List results = query.list();
SELECT子句
SELECT子句提供了比from子句更細粒度的結果集控制。如果要獲取物件的某些屬性而不是整個物件,請使用SELECT子句。以下是使用SELECT子句僅獲取Employee物件first_name欄位的簡單語法:
String hql = "SELECT E.firstName FROM Employee E"; Query query = session.createQuery(hql); List results = query.list();
這裡值得注意的是,Employee.firstName是Employee物件的屬性,而不是EMPLOYEE表的欄位。
WHERE子句
如果要縮小從儲存中返回的特定物件範圍,可以使用 WHERE 子句。以下是使用 WHERE 子句的簡單語法:
String hql = "FROM Employee E WHERE E.id = 10"; Query query = session.createQuery(hql); List results = query.list();
ORDER BY 子句
要對 HQL 查詢的結果進行排序,需要使用ORDER BY 子句。可以按結果集中物件的任何屬性進行排序,升序 (ASC) 或降序 (DESC)。以下是使用 ORDER BY 子句的簡單語法:
String hql = "FROM Employee E WHERE E.id > 10 ORDER BY E.salary DESC"; Query query = session.createQuery(hql); List results = query.list();
如果要按多個屬性排序,只需將其他屬性新增到 order by 子句的末尾,並用逗號分隔,如下所示:
String hql = "FROM Employee E WHERE E.id > 10 " +
"ORDER BY E.firstName DESC, E.salary DESC ";
Query query = session.createQuery(hql);
List results = query.list();
GROUP BY 子句
此子句允許 Hibernate 從資料庫中提取資訊,並根據屬性的值對其進行分組,並且通常使用結果來包含聚合值。以下是使用 GROUP BY 子句的簡單語法:
String hql = "SELECT SUM(E.salary), E.firtName FROM Employee E " +
"GROUP BY E.firstName";
Query query = session.createQuery(hql);
List results = query.list();
使用命名引數
Hibernate 在其 HQL 查詢中支援命名引數。這使得編寫接受使用者輸入的 HQL 查詢變得容易,並且不必防禦 SQL 注入攻擊。以下是使用命名引數的簡單語法:
String hql = "FROM Employee E WHERE E.id = :employee_id";
Query query = session.createQuery(hql);
query.setParameter("employee_id",10);
List results = query.list();
UPDATE 子句
批次更新是 Hibernate 3 中 HQL 的新功能,並且刪除操作在 Hibernate 3 中的工作方式與 Hibernate 2 中不同。Query 介面現在包含一個名為 executeUpdate() 的方法,用於執行 HQL UPDATE 或 DELETE 語句。
UPDATE 子句可用於更新一個或多個物件的一個或多個屬性。以下是使用 UPDATE 子句的簡單語法:
String hql = "UPDATE Employee set salary = :salary " +
"WHERE id = :employee_id";
Query query = session.createQuery(hql);
query.setParameter("salary", 1000);
query.setParameter("employee_id", 10);
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
DELETE 子句
DELETE 子句可用於刪除一個或多個物件。以下是使用 DELETE 子句的簡單語法:
String hql = "DELETE FROM Employee " +
"WHERE id = :employee_id";
Query query = session.createQuery(hql);
query.setParameter("employee_id", 10);
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
INSERT 子句
HQL 僅支援INSERT INTO 子句,其中可以將記錄從一個物件插入到另一個物件。以下是使用 INSERT INTO 子句的簡單語法:
String hql = "INSERT INTO Employee(firstName, lastName, salary)" +
"SELECT firstName, lastName, salary FROM old_employee";
Query query = session.createQuery(hql);
int result = query.executeUpdate();
System.out.println("Rows affected: " + result);
聚合方法
HQL 支援一系列聚合方法,類似於 SQL。它們在 HQL 中的工作方式與在 SQL 中相同,以下是可用函式的列表:
| 序號 | 函式和描述 |
|---|---|
| 1 |
avg(property name) 屬性值的平均值 |
| 2 |
count(property name 或 *) 屬性在結果中出現的次數 |
| 3 |
max(property name) 屬性值的最大值 |
| 4 |
min(property name) 屬性值的最小值 |
| 5 |
sum(property name) 屬性值的總和 |
distinct 關鍵字僅計算行集中唯一的值。以下查詢將僅返回唯一計數:
String hql = "SELECT count(distinct E.firstName) FROM Employee E"; Query query = session.createQuery(hql); List results = query.list();
使用 Query 進行分頁
Query 介面有兩種分頁方法。
| 序號 | 方法和描述 |
|---|---|
| 1 |
Query setFirstResult(int startPosition) 此方法接受一個整數,表示結果集中的第一行,從第 0 行開始。 |
| 2 |
Query setMaxResults(int maxResult) 此方法告訴 Hibernate 檢索固定數量的maxResults 物件。 |
將以上兩種方法結合使用,可以在 Web 或 Swing 應用程式中構建分頁元件。以下是一個示例,可以擴充套件它以每次獲取 10 行:
String hql = "FROM Employee"; Query query = session.createQuery(hql); query.setFirstResult(1); query.setMaxResults(10); List results = query.list();
Hibernate - Criteria 查詢
Hibernate 提供了操作物件以及 RDBMS 表中可用資料的其他方法。其中一種方法是 Criteria API,它允許您以程式設計方式構建 Criteria 查詢物件,您可以在其中應用過濾規則和邏輯條件。
Hibernate 的Session 介面提供createCriteria() 方法,該方法可用於建立一個Criteria 物件,當應用程式執行 Criteria 查詢時,該物件將返回持久化物件類的例項。
以下是最簡單的 Criteria 查詢示例,它將簡單地返回與 Employee 類對應的每個物件。
Criteria cr = session.createCriteria(Employee.class); List results = cr.list();
Criteria 的限制
可以使用Criteria 物件提供的add() 方法為 Criteria 查詢新增限制。以下是如何新增限制以返回工資等於 2000 的記錄的示例:
Criteria cr = session.createCriteria(Employee.class);
cr.add(Restrictions.eq("salary", 2000));
List results = cr.list();
以下是涵蓋不同場景的更多示例,可以根據需要使用:
Criteria cr = session.createCriteria(Employee.class);
// To get records having salary more than 2000
cr.add(Restrictions.gt("salary", 2000));
// To get records having salary less than 2000
cr.add(Restrictions.lt("salary", 2000));
// To get records having fistName starting with zara
cr.add(Restrictions.like("firstName", "zara%"));
// Case sensitive form of the above restriction.
cr.add(Restrictions.ilike("firstName", "zara%"));
// To get records having salary in between 1000 and 2000
cr.add(Restrictions.between("salary", 1000, 2000));
// To check if the given property is null
cr.add(Restrictions.isNull("salary"));
// To check if the given property is not null
cr.add(Restrictions.isNotNull("salary"));
// To check if the given property is empty
cr.add(Restrictions.isEmpty("salary"));
// To check if the given property is not empty
cr.add(Restrictions.isNotEmpty("salary"));
可以使用 LogicalExpression 限制建立 AND 或 OR 條件,如下所示:
Criteria cr = session.createCriteria(Employee.class);
Criterion salary = Restrictions.gt("salary", 2000);
Criterion name = Restrictions.ilike("firstNname","zara%");
// To get records matching with OR conditions
LogicalExpression orExp = Restrictions.or(salary, name);
cr.add( orExp );
// To get records matching with AND conditions
LogicalExpression andExp = Restrictions.and(salary, name);
cr.add( andExp );
List results = cr.list();
儘管所有上述條件都可以像在上一教程中解釋的那樣直接與 HQL 一起使用。
使用 Criteria 進行分頁
Criteria 介面有兩種分頁方法。
| 序號 | 方法和描述 |
|---|---|
| 1 |
public Criteria setFirstResult(int firstResult) 此方法接受一個整數,表示結果集中的第一行,從第 0 行開始。 |
| 2 |
public Criteria setMaxResults(int maxResults) 此方法告訴 Hibernate 檢索固定數量的maxResults 物件。 |
將以上兩種方法結合使用,可以在 Web 或 Swing 應用程式中構建分頁元件。以下是一個示例,可以擴充套件它以每次獲取 10 行:
Criteria cr = session.createCriteria(Employee.class); cr.setFirstResult(1); cr.setMaxResults(10); List results = cr.list();
排序結果
Criteria API 提供org.hibernate.criterion.Order 類,用於根據物件的屬性之一,按升序或降序對結果集進行排序。此示例演示瞭如何使用 Order 類對結果集進行排序:
Criteria cr = session.createCriteria(Employee.class);
// To get records having salary more than 2000
cr.add(Restrictions.gt("salary", 2000));
// To sort records in descening order
cr.addOrder(Order.desc("salary"));
// To sort records in ascending order
cr.addOrder(Order.asc("salary"));
List results = cr.list();
投影和聚合
Criteria API 提供org.hibernate.criterion.Projections 類,該類可用於獲取屬性值的平均值、最大值或最小值。Projections 類類似於 Restrictions 類,因為它提供了幾個靜態工廠方法來獲取Projection 例項。
以下是涵蓋不同場景的幾個示例,可以根據需要使用:
Criteria cr = session.createCriteria(Employee.class);
// To get total row count.
cr.setProjection(Projections.rowCount());
// To get average of a property.
cr.setProjection(Projections.avg("salary"));
// To get distinct count of a property.
cr.setProjection(Projections.countDistinct("firstName"));
// To get maximum of a property.
cr.setProjection(Projections.max("salary"));
// To get minimum of a property.
cr.setProjection(Projections.min("salary"));
// To get sum of a property.
cr.setProjection(Projections.sum("salary"));
Criteria 查詢示例
考慮以下 POJO 類:
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
讓我們建立以下 EMPLOYEE 表來儲存 Employee 物件:
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
對映檔案如下所示。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
最後,我們將建立包含 main() 方法的應用程式類來執行應用程式,在其中我們將使用Criteria 查詢:
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Projections;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 2000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 5000);
Integer empID4 = ME.addEmployee("Mohd", "Yasee", 3000);
/* List down all the employees */
ME.listEmployees();
/* Print Total employee's count */
ME.countEmployee();
/* Print Total salary */
ME.totalSalary();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees having salary more than 2000 */
public void listEmployees( ) {
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(Employee.class);
// Add restriction.
cr.add(Restrictions.gt("salary", 2000));
List employees = cr.list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to print total number of records */
public void countEmployee(){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(Employee.class);
// To get total row count.
cr.setProjection(Projections.rowCount());
List rowCount = cr.list();
System.out.println("Total Coint: " + rowCount.get(0) );
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to print sum of salaries */
public void totalSalary(){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria cr = session.createCriteria(Employee.class);
// To get total salary.
cr.setProjection(Projections.sum("salary"));
List totalSalary = cr.list();
System.out.println("Total Salary: " + totalSalary.get(0) );
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
編譯和執行
以下是編譯和執行上述應用程式的步驟。在繼續進行編譯和執行之前,請確保您已正確設定 PATH 和 CLASSPATH。
建立 hibernate.cfg.xml 配置檔案,如配置章節中所述。
建立 Employee.hbm.xml 對映檔案,如上所示。
建立 Employee.java 原始檔,如上所示,並編譯它。
建立 ManageEmployee.java 原始檔,如上所示,並編譯它。
執行 ManageEmployee 二進位制檔案以執行程式。
您將獲得以下結果,並且記錄將建立在 EMPLOYEE 表中。
$java ManageEmployee .......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 5000 First Name: Mohd Last Name: Yasee Salary: 3000 Total Coint: 4 Total Salary: 15000
如果檢查 EMPLOYEE 表,它應該包含以下記錄:
mysql> select * from EMPLOYEE; +----+------------+-----------+--------+ | id | first_name | last_name | salary | +----+------------+-----------+--------+ | 14 | Zara | Ali | 2000 | | 15 | Daisy | Das | 5000 | | 16 | John | Paul | 5000 | | 17 | Mohd | Yasee | 3000 | +----+------------+-----------+--------+ 4 rows in set (0.00 sec) mysql>
Hibernate - 原生 SQL
如果要利用資料庫特定功能(例如 Oracle 中的查詢提示或 CONNECT 關鍵字),可以使用本地 SQL 來表達資料庫查詢。Hibernate 3.x 允許您為所有建立、更新、刪除和載入操作指定手寫的 SQL,包括儲存過程。
應用程式將使用 Session 介面上的createSQLQuery() 方法從 Session 建立本地 SQL 查詢:
public SQLQuery createSQLQuery(String sqlString) throws HibernateException
將包含 SQL 查詢的字串傳遞給 createSQLQuery() 方法後,可以使用 addEntity()、addJoin() 和 addScalar() 方法分別將 SQL 結果與現有的 Hibernate 實體、聯接或標量結果關聯。
標量查詢
最基本的 SQL 查詢是從一個或多個表中獲取標量(值)列表。以下是使用本地 SQL 獲取標量值的語法:
String sql = "SELECT first_name, salary FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP); List results = query.list();
實體查詢
以上查詢都是關於返回標量值,基本上是從結果集中返回“原始”值。以下是透過 addEntity() 從本地 sql 查詢獲取整個實體物件的語法。
String sql = "SELECT * FROM EMPLOYEE"; SQLQuery query = session.createSQLQuery(sql); query.addEntity(Employee.class); List results = query.list();
命名 SQL 查詢
以下是透過 addEntity() 並使用命名 SQL 查詢從本地 sql 查詢獲取實體物件的語法。
String sql = "SELECT * FROM EMPLOYEE WHERE id = :employee_id";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
query.setParameter("employee_id", 10);
List results = query.list();
本地 SQL 示例
考慮以下 POJO 類:
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
讓我們建立以下 EMPLOYEE 表來儲存 Employee 物件:
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
對映檔案如下所示:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
最後,我們將建立包含 main() 方法的應用程式類來執行應用程式,在其中我們將使用本地 SQL 查詢:
import java.util.*;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.SQLQuery;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 2000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 5000);
Integer empID4 = ME.addEmployee("Mohd", "Yasee", 3000);
/* List down employees and their salary using Scalar Query */
ME.listEmployeesScalar();
/* List down complete employees information using Entity Query */
ME.listEmployeesEntity();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees using Scalar Query */
public void listEmployeesScalar( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
String sql = "SELECT first_name, salary FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List data = query.list();
for(Object object : data) {
Map row = (Map)object;
System.out.print("First Name: " + row.get("first_name"));
System.out.println(", Salary: " + row.get("salary"));
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to READ all the employees using Entity Query */
public void listEmployeesEntity( ){
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
String sql = "SELECT * FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
List employees = query.list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
編譯和執行
以下是編譯和執行上述應用程式的步驟。在繼續進行編譯和執行之前,請確保您已正確設定 PATH 和 CLASSPATH。
建立 hibernate.cfg.xml 配置檔案,如配置章節中所述。
建立 Employee.hbm.xml 對映檔案,如上所示。
建立 Employee.java 原始檔,如上所示,並編譯它。
建立 ManageEmployee.java 原始檔,如上所示,並編譯它。
執行 ManageEmployee 二進位制檔案以執行程式。
您將獲得以下結果,並且記錄將建立在 EMPLOYEE 表中。
$java ManageEmployee .......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ First Name: Zara, Salary: 2000 First Name: Daisy, Salary: 5000 First Name: John, Salary: 5000 First Name: Mohd, Salary: 3000 First Name: Zara Last Name: Ali Salary: 2000 First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 5000 First Name: Mohd Last Name: Yasee Salary: 3000
如果您檢查您的 EMPLOYEE 表,它應該包含以下記錄 -
mysql> select * from EMPLOYEE; +----+------------+-----------+--------+ | id | first_name | last_name | salary | +----+------------+-----------+--------+ | 26 | Zara | Ali | 2000 | | 27 | Daisy | Das | 5000 | | 28 | John | Paul | 5000 | | 29 | Mohd | Yasee | 3000 | +----+------------+-----------+--------+ 4 rows in set (0.00 sec) mysql>
Hibernate - 快取
快取是一種增強系統性能的機制。它是一個位於應用程式和資料庫之間的緩衝記憶體。快取記憶體儲存最近使用的資料項,以便儘可能減少資料庫命中次數。
快取對 Hibernate 也很重要。它利用如下所述的多級快取方案:
一級快取
一級快取是 Session 快取,是所有請求都必須經過的強制快取。Session 物件在將其提交到資料庫之前,會自行儲存物件。
如果對某個物件發出多個更新,Hibernate 會嘗試儘可能延遲執行更新,以減少發出的更新 SQL 語句的數量。如果關閉 Session,則快取的所有物件都將丟失,並在資料庫中持久化或更新。
二級快取
二級快取是可選快取,在嘗試在二級快取中查詢物件之前,始終會先查詢一級快取。二級快取可以在每個類和每個集合的基礎上進行配置,主要負責跨 Session 快取物件。
任何第三方快取都可以與 Hibernate 一起使用。提供了org.hibernate.cache.CacheProvider 介面,必須實現該接口才能為 Hibernate 提供對快取實現的控制代碼。
查詢級快取
Hibernate 還為查詢結果集實現了快取,該快取與二級快取緊密整合。
這是一個可選功能,需要兩個額外的物理快取區域來儲存快取的查詢結果以及上次更新表的時間戳。這僅對頻繁使用相同引數執行的查詢有用。
二級快取
Hibernate 預設使用一級快取,無需執行任何操作即可使用一級快取。讓我們直接進入可選的二級快取。並非所有類都受益於快取,因此能夠停用二級快取非常重要。
Hibernate 二級快取的設定分兩個步驟。首先,必須決定使用哪種併發策略。之後,使用快取提供程式配置快取過期和物理快取屬性。
併發策略
併發策略是一種中介,負責將資料項儲存在快取中並從快取中檢索它們。如果要啟用二級快取,則必須為每個持久化類和集合決定使用哪種快取併發策略。
事務型 - 在很少更新的情況下,對於主要用於讀取的資料,如果必須防止併發事務中出現陳舊資料,請使用此策略。
讀寫 - 在很少更新的情況下,對於主要用於讀取的資料,如果必須防止併發事務中出現陳舊資料,請再次使用此策略。
非嚴格讀寫 − 此策略不保證快取和資料庫之間的一致性。如果資料幾乎從不更改,並且陳舊資料的小機率不構成嚴重問題,則使用此策略。
只讀 − 適用於從不更改的資料的併發策略。僅用於參考資料。
如果我們要為Employee類使用二級快取,讓我們新增所需的對映元素以告訴Hibernate使用讀寫策略快取Employee例項。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<cache usage = "read-write"/>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
usage="read-write" 屬性告訴Hibernate對定義的快取使用讀寫併發策略。
快取提供程式
在考慮了併發策略之後,下一步將使用快取候選類來選擇快取提供程式。Hibernate 強制您為整個應用程式選擇一個快取提供程式。
| 序號 | 快取名稱和描述 |
|---|---|
| 1 |
EHCache 它可以快取到記憶體或磁碟,以及叢集快取,並且支援可選的Hibernate查詢結果快取。 |
| 2 |
OSCache 支援在單個JVM中快取到記憶體和磁碟,並提供豐富的過期策略和查詢快取支援。 |
| 3 |
warmCache 基於JGroups的叢集快取。它使用叢集失效,但不支援Hibernate查詢快取。 |
| 4 |
JBoss Cache 一個完全事務性的複製叢集快取,也基於JGroups組播庫。它支援複製或失效、同步或非同步通訊,以及樂觀和悲觀鎖定。支援Hibernate查詢快取。 |
並非每個快取提供程式都與每種併發策略相容。以下相容性矩陣將幫助您選擇合適的組合。
| 策略/提供程式 | 只讀 | 非嚴格讀寫 | 讀寫 | 事務性 |
|---|---|---|---|---|
| EHCache | X | X | X | |
| OSCache | X | X | X | |
| SwarmCache | X | X | ||
| JBoss Cache | X | X |
您將在hibernate.cfg.xml配置檔案中指定快取提供程式。我們選擇EHCache作為我們的二級快取提供程式 -
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql:///test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
root123
</property>
<property name = "hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
現在,您需要指定快取區域的屬性。EHCache有自己的配置檔案ehcache.xml,它應該位於應用程式的CLASSPATH中。ehcache.xml中Employee類的快取配置可能如下所示 -
<diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory = "1000" eternal = "false" timeToIdleSeconds = "120" timeToLiveSeconds = "120" overflowToDisk = "true" /> <cache name = "Employee" maxElementsInMemory = "500" eternal = "true" timeToIdleSeconds = "0" timeToLiveSeconds = "0" overflowToDisk = "false" />
就是這樣,現在我們為Employee類和Hibernate啟用了二級快取,現在無論何時導航到Employee或按識別符號載入Employee時,Hibernate都會命中二級快取。
您應該分析所有類併為每個類選擇合適的快取策略。有時,二級快取可能會降低應用程式的效能。因此,建議您首先對應用程式進行基準測試,在不啟用快取的情況下,然後啟用適合您的快取並檢查效能。如果快取沒有提高系統性能,那麼啟用任何型別的快取都沒有意義。
查詢級快取
要使用查詢快取,您必須首先使用配置檔案中的hibernate.cache.use_query_cache="true"屬性啟用它。透過將此屬性設定為true,您使Hibernate在記憶體中建立必要的快取以儲存查詢和識別符號集。
接下來,要使用查詢快取,您使用Query類的setCacheable(Boolean)方法。例如 -
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
List users = query.list();
SessionFactory.closeSession();
Hibernate還透過快取區域的概念支援非常細粒度的快取支援。快取區域是快取的一部分,並被賦予一個名稱。
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
query.setCacheRegion("employee");
List users = query.list();
SessionFactory.closeSession();
此程式碼使用該方法告訴Hibernate將查詢儲存在快取的employee區域中並從中查詢。
Hibernate - 實體生命週期
Hibernate中的實體是對映到資料庫表的物件。實體可以處於以下四種狀態之一 -
瞬時 − 不持久。剛剛例項化,或者可能呼叫了一些方法,但未與Session關聯。
持久化 − 與Session關聯。一旦我們呼叫save或persist,物件就處於持久化狀態。
脫管 − 以前附加到Session,但現在不再附加。
移除 − 當實體被標記為從資料庫中刪除時,它會進入移除狀態。
讓我們詳細討論每個狀態。
瞬時狀態
瞬時狀態表示實體不持久。它只是被例項化,或者可能呼叫了一些方法,但未與Session關聯。物件可以透過呼叫save、persist或saveOrUpdate()方法進入持久化狀態。
示例
Employee employee = new Employee();
employee.setName("John Doe");
employee.setSalary(50000.0);
在上面的示例中,Employee物件處於瞬時狀態,因為它尚未與Hibernate會話關聯。
持久化狀態
持久化狀態指的是與會話的關聯。一旦我們呼叫save或persist,物件就處於持久化狀態。當呼叫delete()方法時,持久化物件可以變為瞬時。在Session上呼叫get()和load()會返回一個持久化實體。
示例
Session session = sessionFactory.openSession();
session.beginTransaction();
Employee employee = new Employee();
employee.setName("John Doe");
employee.setSalary(50000.0);
session.save(employee); // The entity is now in the persistent //state
session.getTransaction().commit();
session.close();
這裡,Employee物件在使用session.save()方法儲存時變為持久化。對employee物件所做的任何更改都將反映在資料庫中。
脫管狀態
脫管狀態指的是實體以前與會話關聯,但現在不再關聯的狀態。當管理它的Hibernate會話關閉時,實體會進入脫管狀態。在此狀態下,實體不再與任何會話關聯,對其所做的更改不會自動與資料庫同步。
示例
Session session = sessionFactory.openSession(); session.beginTransaction(); Employee employee = session.get(Employee.class, 1L); session.getTransaction().commit(); session.close(); // The entity is now in the detached state employee.setSalary(60000.0); // Changes are not synchronized with the database
移除狀態
當實體被標記為從資料庫中刪除時,它會進入移除狀態。在此狀態下,實體仍然與會話關聯,但當事務提交時,它將從資料庫中刪除。
示例
Session session = sessionFactory.openSession(); session.beginTransaction(); Employee employee = session.get(Employee.class, 1L); session.delete(employee); // The entity is now in the removed state session.getTransaction().commit(); session.close();
狀態之間的轉換
實體可以透過各種Hibernate方法在這些狀態之間轉換 -
瞬時到持久化 − 呼叫session.save()或session.persist()方法,將實體從瞬時狀態移動到持久化狀態。
持久化到脫管 − 呼叫session.evit()或session.clear()或session.close()方法,會導致實體處於脫管狀態。
脫管到持久化 − 呼叫session.update()或session.merge()方法來持久化脫管實體。
持久化到移除 − 透過呼叫session.delete()方法移除實體。
Hibernate - 批次處理
考慮一種情況,您需要使用Hibernate將大量記錄上傳到資料庫中。以下是使用Hibernate實現此目的的程式碼片段 -
Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Employee employee = new Employee(.....);
session.save(employee);
}
tx.commit();
session.close();
預設情況下,Hibernate會將所有持久化物件快取在會話級快取中,最終您的應用程式會在大約第50,000行左右出現OutOfMemoryException。如果您正在使用Hibernate進行批處理,則可以解決此問題。
要使用批處理功能,首先將hibernate.jdbc.batch_size作為批處理大小設定為一個數字,例如20或50,具體取決於物件大小。這將告訴hibernate容器每X行作為批處理插入。為了在您的程式碼中實現這一點,我們需要進行一些修改,如下所示 -
Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Employee employee = new Employee(.....);
session.save(employee);
if( i % 50 == 0 ) { // Same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
上面的程式碼對於INSERT操作可以正常工作,但是如果您希望進行UPDATE操作,那麼您可以使用以下程式碼實現 -
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults employeeCursor = session.createQuery("FROM EMPLOYEE").scroll();
int count = 0;
while ( employeeCursor.next() ) {
Employee employee = (Employee) employeeCursor.get(0);
employee.updateEmployee();
seession.update(employee);
if ( ++count % 50 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
批處理示例
讓我們修改配置檔案以新增hibernate.jdbc.batch_size屬性 -
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql:///test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
root123
</property>
<property name = "hibernate.jdbc.batch_size">
50
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
考慮以下POJO Employee類 -
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
讓我們建立以下EMPLOYEE表來儲存Employee物件 -
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
以下將是將Employee物件與EMPLOYEE表對映的對映檔案 -
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
最後,我們將建立包含main()方法的應用程式類來執行應用程式,在其中我們將使用Session物件可用的flush()和clear()方法,以便Hibernate將這些記錄寫入資料庫而不是快取在記憶體中。
import java.util.*;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add employee records in batches */
ME.addEmployees( );
}
/* Method to create employee records in batches */
public void addEmployees( ){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
String fname = "First Name " + i;
String lname = "Last Name " + i;
Integer salary = i;
Employee employee = new Employee(fname, lname, salary);
session.save(employee);
if( i % 50 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return ;
}
}
編譯和執行
以下是編譯和執行上述應用程式的步驟。在繼續進行編譯和執行之前,請確保您已正確設定 PATH 和 CLASSPATH。
建立如上所述的hibernate.cfg.xml配置檔案。
建立 Employee.hbm.xml 對映檔案,如上所示。
建立 Employee.java 原始檔,如上所示,並編譯它。
建立 ManageEmployee.java 原始檔,如上所示,並編譯它。
執行ManageEmployee二進位制檔案以執行程式,這將在EMPLOYEE表中建立100000條記錄。
Hibernate - 攔截器
正如您所瞭解的那樣,在Hibernate中,將建立和持久化物件。一旦物件發生更改,就必須將其儲存回資料庫。此過程將持續到下次需要該物件時,並且它將從持久化儲存中載入。
因此,物件在其生命週期中會經歷不同的階段,並且Interceptor介面提供方法,這些方法可以在不同的階段被呼叫以執行某些所需的任務。這些方法是從會話到應用程式的回撥,允許應用程式在持久化物件儲存、更新、刪除或載入之前檢查和/或操作其屬性。以下是Interceptor介面中所有可用方法的列表 -
| 序號 | 方法和描述 |
|---|---|
| 1 |
findDirty() 當在Session物件上呼叫flush()方法時,將呼叫此方法。 |
| 2 |
instantiate() 當例項化持久化類時,將呼叫此方法。 |
| 3 |
isUnsaved() 當物件傳遞給saveOrUpdate()方法時,將呼叫此方法。 |
| 4 |
onDelete() 在刪除物件之前,將呼叫此方法。 |
| 5 |
onFlushDirty() 當Hibernate在重新整理(即更新操作)期間檢測到物件已髒(即已更改)時,將呼叫此方法。 |
| 6 |
onLoad() 在初始化物件之前,將呼叫此方法。 |
| 7 |
onSave() 在儲存物件之前,將呼叫此方法。 |
| 8 |
postFlush() 在刷新發生並且物件已在記憶體中更新後,將呼叫此方法。 |
| 9 |
preFlush() 在重新整理之前,將呼叫此方法。 |
Hibernate Interceptor使我們能夠完全控制物件在應用程式和資料庫中的外觀。
如何使用攔截器?
要構建攔截器,您可以直接實現Interceptor類或擴充套件EmptyInterceptor類。以下是在使用Hibernate Interceptor功能時的簡單步驟。
建立攔截器
在我們的示例中,我們將擴充套件EmptyInterceptor,其中Interceptor的方法將在建立和更新Employee物件時自動呼叫。您可以根據需要實現更多方法。
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.type.Type;
public class MyInterceptor extends EmptyInterceptor {
private int updates;
private int creates;
private int loads;
public void onDelete(Object entity, Serializable id,
Object[] state, String[] propertyNames, Type[] types) {
// do nothing
}
// This method is called when Employee object gets updated.
public boolean onFlushDirty(Object entity, Serializable id,
Object[] currentState, Object[] previousState, String[] propertyNames,
Type[] types) {
if ( entity instanceof Employee ) {
System.out.println("Update Operation");
return true;
}
return false;
}
public boolean onLoad(Object entity, Serializable id,
Object[] state, String[] propertyNames, Type[] types) {
// do nothing
return true;
}
// This method is called when Employee object gets created.
public boolean onSave(Object entity, Serializable id,
Object[] state, String[] propertyNames, Type[] types) {
if ( entity instanceof Employee ) {
System.out.println("Create Operation");
return true;
}
return false;
}
//called before commit into database
public void preFlush(Iterator iterator) {
System.out.println("preFlush");
}
//called after committed into database
public void postFlush(Iterator iterator) {
System.out.println("postFlush");
}
}
建立 POJO 類
現在,讓我們稍微修改一下我們第一個使用EMPLOYEE表和Employee類的示例 -
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
建立資料庫表
第二步是在您的資料庫中建立表。將有一個表對應於您希望提供永續性的每個物件。考慮上面解釋的物件,需要儲存和檢索到以下RDBMS表中 -
create table EMPLOYEE ( id INT NOT NULL auto_increment, first_name VARCHAR(20) default NULL, last_name VARCHAR(20) default NULL, salary INT default NULL, PRIMARY KEY (id) );
建立對映配置檔案
此步驟用於建立一個對映檔案,該檔案指示Hibernate如何將定義的類或類對映到資料庫表。
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
建立應用程式類
最後,我們將建立包含main()方法的應用程式類來執行應用程式。這裡需要注意的是,在建立會話物件時,我們使用了Interceptor類作為引數。
import java.util.List;
import java.util.Date;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add few employee records in database */
Integer empID1 = ME.addEmployee("Zara", "Ali", 1000);
Integer empID2 = ME.addEmployee("Daisy", "Das", 5000);
Integer empID3 = ME.addEmployee("John", "Paul", 10000);
/* List down all the employees */
ME.listEmployees();
/* Update employee's records */
ME.updateEmployee(empID1, 5000);
/* Delete an employee from the database */
ME.deleteEmployee(empID2);
/* List down new list of the employees */
ME.listEmployees();
}
/* Method to CREATE an employee in the database */
public Integer addEmployee(String fname, String lname, int salary){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
Employee employee = new Employee(fname, lname, salary);
employeeID = (Integer) session.save(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employeeID;
}
/* Method to READ all the employees */
public void listEmployees( ){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
try {
tx = session.beginTransaction();
List employees = session.createQuery("FROM Employee").list();
for (Iterator iterator = employees.iterator(); iterator.hasNext();){
Employee employee = (Employee) iterator.next();
System.out.print("First Name: " + employee.getFirstName());
System.out.print(" Last Name: " + employee.getLastName());
System.out.println(" Salary: " + employee.getSalary());
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to UPDATE salary for an employee */
public void updateEmployee(Integer EmployeeID, int salary ){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
employee.setSalary( salary );
session.update(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
/* Method to DELETE an employee from the records */
public void deleteEmployee(Integer EmployeeID){
Session session = factory.openSession( new MyInterceptor() );
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee employee = (Employee)session.get(Employee.class, EmployeeID);
session.delete(employee);
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
}
編譯和執行
以下是編譯和執行上述應用程式的步驟。在繼續進行編譯和執行之前,請確保您已正確設定 PATH 和 CLASSPATH。
建立 hibernate.cfg.xml 配置檔案,如配置章節中所述。
建立 Employee.hbm.xml 對映檔案,如上所示。
建立 Employee.java 原始檔,如上所示,並編譯它。
建立MyInterceptor.java原始檔,如上所示,並編譯它。
建立 ManageEmployee.java 原始檔,如上所示,並編譯它。
執行 ManageEmployee 二進位制檔案以執行程式。
您將獲得以下結果,並且記錄將建立在 EMPLOYEE 表中。
$java ManageEmployee .......VARIOUS LOG MESSAGES WILL DISPLAY HERE........ Create Operation preFlush postFlush Create Operation preFlush postFlush Create Operation preFlush postFlush First Name: Zara Last Name: Ali Salary: 1000 First Name: Daisy Last Name: Das Salary: 5000 First Name: John Last Name: Paul Salary: 10000 preFlush postFlush preFlush Update Operation postFlush preFlush postFlush First Name: Zara Last Name: Ali Salary: 5000 First Name: John Last Name: Paul Salary: 10000 preFlush postFlush
如果您檢查您的 EMPLOYEE 表,它應該包含以下記錄 -
mysql> select * from EMPLOYEE; +----+------------+-----------+--------+ | id | first_name | last_name | salary | +----+------------+-----------+--------+ | 29 | Zara | Ali | 5000 | | 31 | John | Paul | 10000 | +----+------------+-----------+--------+ 2 rows in set (0.00 sec mysql>
Hibernate - ID 生成器
生成器類用於為物件生成ID,該ID將成為資料庫表的主鍵。所有生成器類都實現了org.hibernate.id.IdentifierGenerator介面。可以透過實現上述介面並覆蓋generator(SharedSessionContractImplementor sess, Object obj)方法來建立自己的生成器類。
檢視下面的employee.hbm.xml檔案片段
<hibernate-mapping>
<class name="com.mypackage.Employee" table="emp">
<id name="id">
<generator class="assigned"></generator>
</id>
...
</hibernate-mapping>
生成器類的型別
Hibernate提供了許多預定義的生成器類。Hibernate中一些重要的預定義生成器類包括
assigned - 此生成器表示應用程式將分配主鍵值。在這種情況下,Hibernate不會生成任何值。
identity - 此生成器使用資料庫的自增功能生成主鍵值。它適用於大多數資料庫,並且適用於簡單的用例。Oracle不支援identity生成器。MySQL、MS SQL Server、DB2等支援此功能。
sequence - 此生成器使用資料庫序列生成主鍵值。在某些情況下,它提供了比identity更好的效能和控制。
建立序列的命令
create sequence <sequence_name> start with <number> increment by <number>
注意 - MySQL不支援序列。Oracle支援序列。
employee.hbm.xml中的條目
<hibernate-mapping> <class name="com.mypackage.Employee" table="emp"> <id name="id"> <generator class=”sequence”> <param name=”sequence”>datasource_name</param> </generator> </id> ... </hibernate-mapping>increment - 此生成器透過遞增儲存在記憶體中的值來生成主鍵值。
hilo - 此生成器使用高低演算法生成主鍵值。它結合了sequence和increment的優點。
uuid - 此生成器生成通用唯一識別符號(UUID)作為主鍵值。它適用於需要唯一ID的分散式系統。
native - 此生成器將主鍵生成策略委託給底層資料庫。如果底層資料庫支援identity,則選擇identity。否則,選擇sequence或hilo。它根據資料庫方言選擇最佳策略。
foreign - 此生成器使用另一個關聯實體的主鍵值作為當前實體的主鍵值。
使用註解生成ID
我們可以使用IDENTITY生成器註解來生成ID欄位。請參閱以下示例
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// ... other fields
}
Hibernate - 儲存影像
通常我們需要將影像或大型文件儲存在資料庫中。資料庫提供將字元或影像檔案儲存為BLOB(二進位制大物件)或CLOB(字元大物件)。在資料庫中,型別為BLOB/LONGBLOB, LONGTEXT等。
為了指示實體具有必須儲存為BLOB,CLOB的欄位,使用了@Lob註解。例如,考慮以下程式碼片段 -
@Entity
@Table(name = "save_image")
public class LobEntity {
...
@Lob
@Column(name = "image")
private byte[] image;
...
}
讓我們詳細討論一下在Hibernate中儲存影像,並以示例說明。
建立對映類
讓我們建立其資料要持久化到資料庫中的POJO類。
LobEntity.java
package com.tutorialspoint;
import javax.persistence.*;
@Entity
@Table(name = "save_image")
public class LobEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Lob
@Column(name = "image")
private byte[] image;
public byte [] getImage() {
return image;
}
public void setImage(byte[] imageData) {
this.image = imageData;
}
}
建立Hibernate配置檔案
現在為資料庫和其他詳細資訊建立一個Hibernate配置檔案。
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 5.3//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>
<property name="connection.url">jdbc:mysql:///TUTORIALSPOINT</property>
<property name="connection.username">root</property>
<property name="connection.password">guest123</property>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<mapping class="com.tutorialspoint.LobEntity"/>
</session-factory>
</hibernate-configuration>
請注意,在檔案中,對映元素具有class而不是resource。並且將hbm2ddl.auto屬性設定為update會在表不存在時自動建立表。
建立應用程式類
最後,我們將建立包含main()方法的應用程式類來執行應用程式。我們將使用此應用程式來測試Table per Hiearchy對映。
SaveBlob.java
package com.tutorialspoint;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import java.io.*;
public class SaveBlob {
public static void main(String[] args) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("C:\\Users\\Saikat\\OneDrive\\Pictures\\almoural_castle.jpg");
byte[] bytes = new byte[fis.available()];
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
Metadata meta = new MetadataSources(ssr).getMetadataBuilder().build();
SessionFactory factory = meta.getSessionFactoryBuilder().build();
Session session = factory.openSession();
Transaction t = session.beginTransaction();
LobEntity lob = new LobEntity();
lob.setImage(bytes);
session.save(lob);
session.getTransaction().commit();
fis.close();
System.out.println("Successfully inserted image in table.");
}
}
編譯和執行
執行SaveBlob二進位制檔案以執行程式。
輸出
您將獲得以下結果,並且記錄將建立在save_image表中。
$java SaveBlob Successfully inserted image in table.
如果檢查save_image表,它應該包含以下記錄 -
mysql> select id from save_image; +----+ | id | +----+ | 1 | +----+ 1 row in set (0.00 sec) mysql> desc save_image; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | bigint | NO | PRI | NULL | auto_increment | | image | longblob | YES | | NULL | | +-------+----------+------+-----+---------+----------------+ 2 rows in set (0.00 sec)
Hibernate - log4j 整合
Log4j是一個完全用Java編寫的日誌框架。它是Apache軟體基金會的一個開源專案。
為了使用Log4j,您需要將log4j庫放到專案的CLASSPATH中。然後,您需要設定一個配置檔案,它可以是以下之一 -
一個名為log4j.xml的XML檔案
一個名為log4j.properties的屬性檔案
Log4j的不同日誌級別
以下是log4j框架支援的日誌級別列表。
| 序號 | 級別和描述 |
|---|---|
| 1 | ALL 最高的除錯級別。輸出所有可能的內容。 |
| 2 | DEBUG 記錄有用的詳細除錯資訊。 |
| 3 | INFO 記錄資訊性訊息,以高層次突出顯示應用程式的進度。 |
| 4 | WARN 記錄可能存在危害的情況,但不一定是錯誤。 |
| 5 | ERROR 指定可能仍然允許應用程式繼續執行的錯誤事件。 |
| 6 | FATAL 指定非常嚴重的錯誤事件,這些事件可能會導致應用程式中止。 |
| 7 | OFF 停用日誌記錄。用於完全關閉日誌記錄。 |
| 8 | TRACE 指定比DEBUG更細粒度的資訊事件。 |
使用log4j.xml配置
對於xml檔案配置,您需要將log4j.jar放在CLASSPATH中。請記住將log4j.xml儲存在與hibernate.cfg.xml相同的目錄(src/)中。
log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
<appender name="ConsoleAppender" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{MM/dd HH:mm:ss} %-5p
%30.30c %x - %m\n"/>
</layout>
</appender>
<appender name="FileAppender" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="C:/hibernate-log4j.log"/>
<param name="MaxFileSize" value="10MB"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{MM/dd HH:mm:ss} %-5p
%30.30c %x - %m\n"/>
</layout>
</appender>
<root>
<level value="info"/>
<appender-ref ref="ConsoleAppender"/>
<appender-ref ref="FileAppender"/>
</root>
</log4j:configuration>
在此配置中,除錯訊息將輸出到控制檯(因為使用了“ConsoleAppender”)以及C:/ > hibernate-log4j.log中的檔案(因為使用了“FileAppender”)。您可以根據需要更改檔名,但副檔名必須為log。
使用log4j.properties配置
與XML檔案類似,log4j.properties檔案必須放在src/資料夾下(與hibernate.cfg.xml相同)。
log4j.properties
# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILE, stdout
# Output log messages to file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C:\\hibernate.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L -
%m%n
# Output log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-
5p %c{1}:%L - %m%n
# Hibernate logging
-------------------------------------------------
# General Hibernate logging
log4j.logger.org.hibernate=INFO
# Logs all SQL statements generated by Hibernate
log4j.logger.org.hibernate.SQL=DEBUG
Hibernate - Spring 整合
Spring是一個用於構建企業級Java應用程式的開源框架。Spring有一個applicationContext.xml檔案。我們將使用此檔案來提供所有資訊,包括資料庫配置。由於資料庫配置包含在Hibernate的hibernate.cfg.xml中,因此我們不再需要它。
在Hibernate中,我們使用StandardServiceRegistry、MetadataSources、SessionFactory來獲取Session、Transaction。Spring框架提供了一個HibernateTemplate類,您只需呼叫save方法即可持久化到資料庫。HibernateTemplate類位於org.springframework.orm.hibernate3 包中。
語法
// create a new student object Student s1 = new Student( 111, "Karan", "Physics"); // save the student object in database using HibernateTemplate instance hibernateTemplate.persist(s1);
所需庫
要使用HibernateTemplate,您需要從https://mvnrepository.com/artifact/org.springframework/spring-hibernate3/2.0.8獲取jar檔案
您需要從https://mvnrepository.com/artifact/org.springframework/spring-dao/2.0.3獲取spring-dao jar
您還需要將Hibernate-core jar放在CLASSPATH中。
HibernateTemplate類的有用方法
以下是HibernateTemplate類重要方法的列表
| 序號 | 方法和描述 |
|---|---|
| 1 | void persist(Object entity) 將物件儲存到資料庫中對映的表中。 |
| 2 | Serializable save(Object entity) 將物件儲存到資料庫中對映的表記錄中並返回id。 |
| 3 | void saveOrUpdate(Object entity) 儲存或更新對映到物件的表。如果輸入了id,則更新記錄,否則儲存。 |
| 4 | void update(Object entity) 更新對映到物件的表。 |
| 5 | void delete(Object entity) 根據id刪除資料庫中對映到表的給定物件。 |
示例
讓我們詳細討論一下Spring Hibernate整合,並以示例說明。
建立對映類
讓我們建立其資料要持久化到資料庫中的POJO類。
Student.java
package com.tutorialspoint;
public class Student {
private long id;
private String name;
private String dept;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
}
建立DAO類
建立DAO類,它將使用HibernateTemplate類來建立、更新和刪除學生物件。
StudentDAO.java
package com.tutorialspoint;
import org.springframework.orm.hibernate3.HibernateTemplate;
public class StudentDAO{
HibernateTemplate template;
public void setTemplate(HibernateTemplate template) {
this.template = template;
}
public void saveStudent(Student s){
template.save(s);
}
public void updateStudent(Student s){
template.update(s);
}
public void deleteStudent(Student s){
template.delete(s);
}
}
建立對映XML
為學生物件建立Hibernate對映以持久化到資料庫中。
student.hbm.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="Student" table="student_hib">
<id name="id">
<generator class="assigned"></generator>
</id>
<property name="name"></property>
<property name="dept"></property>
</class>
</hibernate-mapping>
建立應用程式上下文XML
建立Spring應用程式上下文
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="dataSource" class=" com.mysql.cj.jdbc.MysqlDataSource">
<property name="driverClassName" value=" com.mysql.cj.jdbc.Driver"></property>
<property name="url" value=" jdbc:mysql:///TUTORIALSPOINT"></property>
<property name="username" value="root"></property>
<property name="password" value="guest123"></property>
</bean>
<bean id="localSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mappingResources">
<list>
<value>student.hbm.xml</value>
</list>
</property>
<props>
<prop key="hibernate.dialect"> org.hibernate.dialect.MySQL8Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="localSessionFactory"></property>
</bean>
<bean id="st" class="StudentDAO">
<property name="template" ref="hibernateTemplate"></property>
</bean>
</beans>
建立應用程式類
最後,我們將建立包含main()方法的應用程式類來執行應用程式。我們將使用此應用程式來測試Spring Hibernate整合。
PersistStudent.java
package com.tutorialspoint;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class PersistStudent {
public static void main(String[] args) {
Resource r=new ClassPathResource("applicationContext.xml");
BeanFactory factory=new XmlBeanFactory(r);
StudentDAO dao=(StudentDAO)factory.getBean("st");
Student s=new Student();
s.setId(190);
s.setName("Danny Jing");
s.setDept("Physics");
dao.persistStudent(s);
System.out.println(" Successfully persisted the student. Please check your database for results.");
}
}
編譯和執行
執行PersistStudent二進位制檔案以執行程式。
輸出
您將獲得以下結果,並且記錄將建立在Shape表中。
$java PersistStudent Successfully persisted the student. Please check your database for results.
如果檢查表,它們應該包含以下記錄 -
mysql> select * from student; mysql> select * from student; +------+------------+---------+ | id | name | dept | +------+------------+---------+ | 190 | Danny Jing | Physics | +------+------------+---------+ 1 row in set (0.00 sec)
Hibernate - Struts 2 整合
Struts 2是一個Web框架,它是免費的開源框架,可從Apache獲得,並可從https://struts.apache.org/releases.html下載
在本教程中,我們將使用Struts和Hibernate構建一個Web應用程式。您需要擁有Struts 2和Hibernate的jar檔案。將jar檔案放在CLASSPATH中。
這是一個簡單的應用程式,包含2個jsp檔案,index.jsp和showResults.jsp。使用者將輸入學生ID並點選“搜尋”按鈕。showResults.jsp將根據學生ID顯示學生資訊。
建立UI頁面
讓我們建立以下jsp頁面,以用於此Web應用程式。
index.jsp
<form action="DisplayResults" method="post"> <center>Student id:<input type="text" name="StudentId"/><br><br/> <input type="submit" value="Search"/></center> </form>
showResults.jsp
<%
String as = request.getParameter("StudentId");
System.out.println("StudentId: " + as);
if(as.equals("")){
out.print("Sorry, you have not entered Student Id");
}
int idr = Integer.parseInt(as);
Student st1 = StudentDAO.getStudent(idr);
%>
<body>
<table border="1" align="center">
<thead><tr><td>StudentID</td><td>First Name</td><td>Last Name</td><td>Dept<td></tr></thead>
<tr><td><%= st1.getStudentid() %></td><td><%= st1.getfName()%></td><td><%= st1.getlName() %></td><td><%= st1.getDept() %></td></tr>
</table>
</body>
建立資料庫表
讓我們使用以下SQL查詢建立一個Students表並插入資料 -
create table students( StudentID int, LastName varchar(255), FirstName varchar(255), Address varchar(255), Dept varchar(255), primary key(StudentID) ); insert into students values (1000, 'Agarwal', 'Bonny', '12 Southern Ave', 'Mathematics') (1001, 'Pandey', 'Amit', ' 8 Ganesh Chandra Rd. ', 'Physics') (1002, 'Kumar', 'Kalyan', '42 Brick Rd., Alipur', 'English');
建立Hibernate配置檔案
現在為資料庫和其他詳細資訊建立一個Hibernate配置檔案。
hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>
<property name="connection.url">jdbc:mysql:///TUTORIALSPOINT</property>
<property name="connection.username">root</property>
<property name="connection.password">guest123</property>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<mapping resource="student.hbm.xml"/>
</session-factory>
</hibernate-configuration>
建立對映配置檔案
現在建立一個對映檔案,該檔案指示Hibernate如何將Student物件對映到資料庫表。
student.hbm.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.tutorialspoint.webhibernate.Student" table="students">
<id name="studentid">
<generator class="increment"></generator>
</id>
<property name="firstName" column="firstname"></property>
<property name="lastName" column="lastname"></property>
<property name="dept" column="dept"></property>
</class>
</hibernate-mapping>
建立Struts 2配置檔案
現在為Struts建立一個配置檔案,該檔案指示Struts如何對映Struts操作。
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation
//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<action name="DisplayResults" class="Student" >
<result name="success">showResults.jsp</result>
</action>
</struts>
建立WEB.XML檔案
現在建立一個web.xml,即WEB-INF資料夾下的部署描述符。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" SYSTEM “web-app_2_5.dtd” >
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>studentFilter</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>studentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
建立Java類
現在讓我們建立Student類和一個DAO類來持久化Student物件。Student類充當Struts的操作類和Hibernate的持久化類。
Student.java
package com.tutorialspoint.webhibernate;
public class Student {
private int studentid;
private String firstName;
private String lastName;
private String dept;
public Student() {}
public Student(int i) {
this.studentid = i;
}
public String execute() {
return "success";
}
public Student getStudentForId(int j) {
Student s1 = StudentDAO.getStudent(studentid);
return s1;
}
public int getStudentid() {
return studentid;
}
public void setStudentid(int studID) {
studentid = studID;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String fName) {
this.firstName = fName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lName) {
this.lastName = lName;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
}
StudentDAO.java
package com.tutorialspoint.webhibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import java.util.List;
import java.util.Iterator;
public class StudentDAO {
public static Student getStudent(int i) {
StandardServiceRegistry ssr=new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
Metadata meta=new MetadataSources(ssr).getMetadataBuilder().build();
SessionFactory factory=meta.getSessionFactoryBuilder().build();
Session session=factory.openSession();
Transaction tx = session.beginTransaction();
String hql = " from Student where studentid = :student_id ";
Query<?> query = session.createQuery(hql, Student.class);
query.setParameter("student_id", i);
System.out.println("student_id: " + i);
List<Student> k = (List<Student>) query.getResultList();
System.out.println("List size: " + k.size());
tx.commit();
session.close();
Student st = new Student(1);
for (Iterator<?> iterator = k.iterator(); iterator.hasNext();){
st = (Student) iterator.next();
}
return st;
}
}
應用程式流程
使用者在index.jsp中輸入學生ID
在index.jsp中,表單被設定為處理'DisplayResults'操作。'DisplayResults'在struts.xml中對映到Student.java,它是Action類。呼叫Student.java的execute()方法,該方法返回"success"。
在返回"success"後,如struts.xml中宣告的那樣,控制權傳遞到"showResults.jsp"。
部署Web應用程式的步驟
我們使用Tomcat來部署此Web應用程式。安裝Tomcat後,我們假設TOMCAT_HOME環境變量表示Tomcat安裝目錄。
在TOMCAT_HOME/webapps目錄下建立一個名為WebHibernate的目錄。
在此目錄(TOMCAT_HOME/webapps)下,放置index.jsp、showResults.jsp
在同一目錄(TOMCAT_HOME/webapps)中建立一個名為WEB-INF的目錄。
在WEB-INF下,複製web.xml。
在WEB-INF下建立一個名為lib的目錄,並將所有依賴項放在其中。
在WEB-INF/classes/com/tutorialspoint/webhibernate/下建立目錄結構,並將Student.class、StudentDAO.class放在其中
啟動Tomcat
輸出
開啟瀏覽器,輸入URL:https://:8080/WebHibernate。您的索引頁面將如下所示
輸入1000作為學生ID。結果頁面如下所示
Hibernate - 示例Web應用程式
在本教程中,我們將構建一個示例Web應用程式。這是一個簡單的應用程式,包含2個jsp檔案,index.jsp和showResults.jsp。使用者將輸入學生ID並點選“搜尋”按鈕。showResults.jsp將根據學生ID顯示學生資訊。
建立UI頁面
讓我們建立以下jsp頁面,以用於此Web應用程式。
index.jsp
<form action="showResults.jsp" method="post"> <center>Student id:<input type="text" name="StudentId"/><br><br/> <input type="submit" value="Search"/></center> </form>
showResults.jsp
<%@ page import="com.tutorialspoint.webhibernate.*" %>
<%
String as = request.getParameter("StudentId");
System.out.println("StudentId: " + as);
if(as.equals("")){
out.print("Sorry, you have not entered Student Id");
}
int idr = Integer.parseInt(as);
Student st1 = StudentDAO.getStudent(idr);
%>
<body>
<table border="1" align="center">
<thead><tr><td>StudentID</td><td>First Name</td><td>Last Name</td><td>Dept<td></tr></thead>
<tr><td><%= st1.getStudentid() %></td><td><%= st1.getfName()%></td><td><%= st1.getlName() %></td><td><%= st1.getDept() %></td></tr>
</table>
</body>
建立資料庫表
讓我們使用以下SQL查詢建立一個Students表並插入資料 -
create table students( StudentID int, LastName varchar(255), FirstName varchar(255), Address varchar(255), Dept varchar(255), primary key(StudentID) ); insert into students values (1000, 'Agarwal', 'Bonny', '12 Southern Ave', 'Mathematics') (1001, 'Pandey', 'Amit', ' 8 Ganesh Chandra Rd. ', 'Physics') (1002, 'Kumar', 'Kalyan', '42 Brick Rd., Alipur', 'English');
建立Hibernate配置檔案
現在為資料庫和其他詳細資訊建立一個Hibernate配置檔案。
hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>
<property name="connection.url">jdbc:mysql:///TUTORIALSPOINT</property>
<property name="connection.username">root</property>
<property name="connection.password">guest123</property>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<mapping resource="student.hbm.xml"/>
</session-factory>
</hibernate-configuration>
建立對映配置檔案
現在建立一個對映檔案,該檔案指示Hibernate如何將Student物件對映到資料庫表。
student.hbm.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.tutorialspoint.webhibernate.Student" table="students">
<id name="studentid">
<generator class="increment"></generator>
</id>
<property name="fName" column="firstname"></property>
<property name="lName" column="lastname"></property>
<property name="dept" column="dept"></property>
</class>
</hibernate-mapping>
建立Java類
現在讓我們建立 Student POJO 類和一個 DAO 類來持久化 Student 物件。
Student.java
package com.tutorialspoint.webhibernate;
public class Student {
private int studentid;
private String fName;
private String lName;
private String dept;
public Student() {}
public Student(int i) {
this.studentid = i;
}
public Student getStudentForId(int j) {
Student s1 = StudentDAO.getStudent(studentid);
return s1;
}
public int getStudentid() {
return studentid;
}
public void setStudentid(int studID) {
studentid = studID;
}
public String getfName() {
return fName;
}
public void setfName(String fName) {
this.fName = fName;
}
public String getlName() {
return lName;
}
public void setlName(String lName) {
this.lName = lName;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
}
StudentDAO.java
package com.tutorialspoint.webhibernate;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import java.util.List;
import java.util.Iterator;
public class StudentDAO {
public static Student getStudent(int i) {
StandardServiceRegistry ssr=new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
Metadata meta=new MetadataSources(ssr).getMetadataBuilder().build();
SessionFactory factory=meta.getSessionFactoryBuilder().build();
Session session=factory.openSession();
Transaction tx = session.beginTransaction();
String hql = " from Student where studentid = :student_id ";
Query<?> query = session.createQuery(hql, Student.class);
query.setParameter("student_id", i);
System.out.println("student_id: " + i);
List<Student> k = (List<Student>) query.getResultList();
System.out.println("List size: " + k.size());
tx.commit();
session.close();
Student st = new Student(1);
for (Iterator<?> iterator = k.iterator(); iterator.hasNext();){
st = (Student) iterator.next();
}
return st;
}
}
部署Web應用程式的步驟
我們使用Tomcat來部署此Web應用程式。安裝Tomcat後,我們假設TOMCAT_HOME環境變量表示Tomcat安裝目錄。
在TOMCAT_HOME/webapps目錄下建立一個名為WebHibernate的目錄。
在此目錄(TOMCAT_HOME/webapps)下,放置index.jsp、showResults.jsp
在同一目錄(TOMCAT_HOME/webapps)中建立一個名為WEB-INF的目錄。
在WEB-INF下,建立一個新檔案web.xml並新增以下內容。
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>在WEB-INF下建立一個名為lib的目錄,並將所有依賴項放在其中。
在WEB-INF/classes/com/tutorialspoint/webhibernate/下建立目錄結構,並將Student.class、StudentDAO.class放在其中
啟動Tomcat
輸出
開啟瀏覽器,輸入URL:https://:8080/WebHibernate。您的索引頁面將如下所示
輸入1000作為學生ID。結果頁面如下所示
Hibernate - 基於層次結構的表
簡介
在 Hibernate 中,Java 類對映到資料庫表。假設我們有一個類,它有幾個子類,Hibernate 提供了三種方法將類對映到表:
基於層次結構的表
基於具體類的表
基於子類的表
讓我們詳細討論一下基於層次結構的表,並舉例說明。
基於層次結構的表
假設我們有一個類 'Shape',它有兩個子類 'Circle' 和 'Rectangle'。在這種型別的對映中,我們只會在資料庫中有一個表。
層次結構中有三個類。Shape 是超類,Circle 和 Rectangle 是子類。考慮type變數。它的值對於 Shape 類為shape,對於 Circle 類為circle,對於 Rectangle 類為rectangle。這是為了讓我們從資料庫中知道儲存的是哪種型別的物件。因此,它是鑑別器。
建立對映類
讓我們建立其資料需要持久化到資料庫中的 POJO 類。
Shape.java
package com.tutorialspoint;
public class Shape {
public int Id;
public String type;
public double area;
public Shape() {
this.type = "Shape";
}
public double calculateArea() {
return 0; // default implementation.
}
public int getId() {
return Id;
}
public void setId(int i) {
this.Id = i;
}
public String getType() {
return this.type;
}
public void setArea(double a) {
this.area = a;
}
public double getArea() {
return this.area;
}
}
Circle.java
package com.tutorialspoint;
import java.math.*;
public class Circle extends Shape {
private double radius;
public Circle(double rad ) {
this.radius = rad;
this.type = "Circle";
}
@Override
public double calculateArea() {
area = Math.PI * radius * radius;
return area;
}
}
Rectangle.java
package com.tutorialspoint;
public class Rectangle extends Shape {
private double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
this.type = "Rectangle";
}
@Override
public double calculateArea() {
return length * width;
}
}
建立資料庫表
讓我們在資料庫中建立一個表。將對應於上述物件的一個表,您願意提供持久化。考慮上述物件需要儲存和檢索到以下 RDBMS 表中:
CREATE TABLE students.shape( id int NOT NULL, type VARCHAR(20), radius int, length int, width int, area float );
建立對映配置檔案
現在建立一個對映檔案,該檔案指示 Hibernate 如何將定義的類對映到資料庫表。
shape.hbm.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 5.3//EN"
"http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.tutorialspoint.Shape" table="shape" discriminator-value="Shape">
<id name="id">
<generator class="increment"></generator>
</id>
<discriminator column="type" type="string"></discriminator>
<property name="area"></property>
<subclass name="com.tutorialspoint.Circle" discriminator-value="Circle">
<property name="radius"></property>
</subclass>
<subclass name="com.tutorialspoint.Rectangle" discriminator-value="Rectangle">
<property name="width"></property>
<property name="length"></property>
</subclass>
</class>
</hibernate-mapping>
建立Hibernate配置檔案
現在為資料庫和其他詳細資訊建立一個Hibernate配置檔案。
hibernate.cfg.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQL8Dialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.cj.jdbc.Driver
</property>
<!—'students' is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql:///students
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
guest123
</property>
<!-- List of XML mapping files -->
<mapping resource = "shape.hbm.xml"/>
</session-factory>
</hibernate-configuration>
建立應用程式類
最後,我們將建立包含main()方法的應用程式類來執行應用程式。我們將使用此應用程式來測試Table per Hiearchy對映。
TestTablePerhierarchy.java
package com.tutorialspoint;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class TestTablePerHierarchy {
public static void main(String[] args) {
// create a hibernate configuration
StandardServiceRegistry ssr=new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
Metadata meta=new MetadataSources(ssr).getMetadataBuilder().build();
// get the sessionfactory and open the session
SessionFactory factory=meta.getSessionFactoryBuilder().build();
Session session=factory.openSession();
// begin the transaction
Transaction t=session.beginTransaction();
// create Shape instance and set details to persist
Shape s1=new Shape();
s1.setType(s1.getType());
s1.setArea(s1.calculateArea());
// create Circle instance and set details to persist
Circle c1=new Circle(2);
c1.setType(c1.getType());
c1.setArea(c1.calculateArea());
// create Rectangle instance and set details to persist
Rectangle r1 = new Rectangle(3,4);
r1.setType(r1.getType());
r1.setArea(r1.calculateArea());
// persist all instances
session.persist(s1);
session.persist(c1);
session.persist(r1);
// commit the transaction and close the session
t.commit();
session.close();
System.out.println(" Successfully persisted 3 classes. Please check your database for results.");
}
}
編譯和執行
執行 TestTablePerHierarchy 二進位制檔案以執行程式。
輸出
您將獲得以下結果,並且記錄將建立在Shape表中。
$java TestTablePerHierarchy Successfully persisted 3 classes. Please check your database for results.
如果您檢查 Shape 表,它應該包含以下記錄:
mysql> select * from shape; +----+-----------+--------+--------+-------+-------+ | id | type | radius | length | width | area | +----+-----------+--------+--------+-------+-------+ | 1 | Shape | NULL | NULL | NULL | 0 | | 2 | Circle | 2 | NULL | NULL | 12.56 | | 3 | Rectangle | NULL | 3 | 4 | 12 | +----+-----------+--------+--------+-------+-------+ 3 rows in set (0.00 sec)
Hibernate - 基於具體類的表
簡介
在 Hibernate 中,Java 類對映到資料庫表。假設我們有一個類,它有幾個子類,Hibernate 提供了三種方法將類對映到表:
基於層次結構的表
基於具體類的表
基於子類的表
讓我們詳細討論一下基於具體類的表,並舉例說明。
基於具體類的表
假設我們有一個類 'Shape',它有兩個子類 'Circle' 和 'Rectangle'。在這種型別的對映中,我們將在資料庫中有三個表。
層次結構中有三個類。Shape 是超類,Circle 和 Rectangle 是子類。子類的表將只包含子類的欄位。為了檢視子類的所有欄位,需要對超類和子類進行連線。
SELECT col1, col2…coln FROM superclass_table UNION SELECT col_a, col_b,..col_n FROM subclass_table;
這將選擇子類的所有屬性(包括父類中的屬性)。UNION 運算子預設只選擇不同的值。要允許重複值,請使用UNION ALL。這就是為什麼基於具體類的表也被稱為連線子類。為了對映類,我們將在 *.hbm.xml 中使用union-subclass元素,如下所示
<class name="com.tutorialspoint.Shape" table="shape">
...
<union-subclass name="com.tutorialspoint.Circle"
table="circle">
<property name="radius"></property>
</union-subclass>
<union-subclass name="com.tutorialspoint.Rectangle"
table="rectangle">
<property name="length"></property>
<property name="width"></property>
</union-subclass>
...
建立對映類
讓我們建立其資料需要持久化到資料庫中的 POJO 類。
Shape.java
package com.tutorialspoint;
public class Shape {
public int Id;
public String type;
public double area;
public Shape() {
this.type = "Shape";
}
public double calculateArea() {
this.area= 0;
setArea(area);
return area;
}
public int getId() {
return Id;
}
public void setId(int i) {
this.Id = i;
}
public String getType() {
return this.type;
}
public double getArea() {
return area;
}
public void setArea(double i) {
area = i;
}
}
Circle.java
package com.tutorialspoint;
import java.math.*;
public class Circle extends Shape {
private double radius;
public Circle(double rad ) {
this.radius = rad;
this.type = "Circle";
}
@Override
public double calculateArea() {
area = Math.PI * radius * radius;
return area;
}
public void setArea() {
this.area = calculateArea();
}
public double getArea() {
return area;
}
}
Rectangle.java
package com.tutorialspoint;
public class Rectangle extends Shape {
private double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
this.type = "Rectangle";
}
@Override
public double calculateArea() {
return length * width;
}
public void setArea() {
this.area = calculateArea();
}
public double getArea() {
return area;
}
}
建立資料庫表
讓我們在資料庫中建立表。將對應於上述物件的一個表,您願意提供持久化。考慮上述物件需要儲存和檢索到以下 RDBMS 表中:
CREATE TABLE students.shape( id int NOT NULL, type VARCHAR(20), area float ); CREATE TABLE students.circle( id int NOT NULL, type VARCHAR(20), area float, radius float ); CREATE TABLE students.rectangle( id int NOT NULL, type VARCHAR(20), area float, length float, width float );
建立對映配置檔案
現在建立一個對映檔案,該檔案指示 Hibernate 如何將定義的類對映到資料庫表。
shape.hbm.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 5.3//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.tutorialspoint.Shape" table="shape">
<id name="id">
<generator class="increment"></generator>
</id>
<property name="type"></property>
<property name="area"></property>
<union-subclass name="com.tutorialspoint.Circle"
table="circle">
<property name="radius"></property>
</union-subclass>
<union-subclass name="com.tutorialspoint.Rectangle"
table="rectangle">
<property name="length"></property>
<property name="width"></property>
</union-subclass>
</class>
</hibernate-mapping>
建立Hibernate配置檔案
現在為資料庫和其他詳細資訊建立一個Hibernate配置檔案。
hibernate.cfg.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQL8Dialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.cj.jdbc.Driver
</property>
<!—'students' is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql:///students
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
guest123
</property>
<!-- List of XML mapping files -->
<mapping resource = "shape.hbm.xml"/>
</session-factory>
</hibernate-configuration>
將屬性hbm2ddlauto設定為update將在程式執行期間建立表(如果尚未建立)。
建立應用程式類
最後,我們將建立包含 main() 方法的應用程式類來執行應用程式。我們將使用此應用程式來測試基於具體類的表對映。
TestTablePerConcrete.java
package com.tutorialspoint;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class TestTablePerConcrete{ {
public static void main(String[] args) {
// create a hibernate configuration
StandardServiceRegistry ssr=new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
Metadata meta=new MetadataSources(ssr).getMetadataBuilder().build();
// get the sessionfactory and open the session
SessionFactory factory=meta.getSessionFactoryBuilder().build();
Session session=factory.openSession();
// begin the transaction
Transaction t=session.beginTransaction();
// create Shape instance and set details to persist
Shape s1=new Shape();
s1.setType(s1.getType());
s1.setArea(s1.calculateArea());
// create Circle instance and set details to persist
Circle c1=new Circle(2);
c1.setType(c1.getType());
c1.setArea();
// create Rectangle instance and set details to persist
Rectangle r1 = new Rectangle(3,4);
r1.setType(r1.getType());
r1.setArea();
// persist all instances
session.persist(s1);
session.persist(c1);
session.persist(r1);
// commit the transaction and close the session
t.commit();
session.close();
System.out.println(" Successfully persisted 3 classes. Please check your database for results.");
}
}
編譯和執行
執行 TestTablePerHierarchy 二進位制檔案以執行程式。
輸出
您將獲得以下結果,並且記錄將建立在Shape表中。
$java TestTablePerHierarchy Successfully persisted 3 classes. Please check your database for results.
如果您檢查您的表,它們應該包含以下記錄:
mysql> select * from shape; +----+-------+------+ | id | type | area | +----+-------+------+ | 1 | Shape | 0 | +----+-------+------+ 1 row in set (0.00 sec) mysql> select * from circle; +----+--------+-------+--------+ | id | type | area | radius | +----+--------+-------+--------+ | 1 | Circle | 12.56 | 2 | +----+--------+-------+--------+ 1 row in set (0.00 sec) mysql> select * from rectangle; +----+-----------+------+--------+-------+ | id | type | area | length | width | +----+-----------+------+--------+-------+ | 1 | Rectangle | 12 | 3 | 4 | +----+-----------+------+--------+-------+ 1 row in set (0.00 sec)
Hibernate - 基於子類的表
簡介
在 Hibernate 中,Java 類對映到資料庫表。假設我們有一個類,它有幾個子類,Hibernate 提供了三種方法將類對映到表:
基於層次結構的表
基於具體類的表
基於子類的表
讓我們詳細討論一下基於子類的表,並舉例說明。
基於子類的表
假設我們有一個類 'Shape',它有兩個子類 'Circle' 和 'Rectangle'。在這種策略中,我們為每個類建立一個新表。此外,在子類表中,添加了父類所有欄位。因此,我們不需要與超類表連線,因為子類表包含超類所有欄位。
層次結構中有三個類。Shape 是超類,Circle 和 Rectangle 是子類。為了對映類,我們將在 *.hbm.xml 中使用joined-subclass元素,如下所示
<class name="com.tutorialspoint.Shape" table="shape">
...
<joined-subclass name="com.tutorialspoint.Circle"
table="circle">
<key column="sid"></key>
<property name="radius"></property>
</joined-subclass>
<joined-subclass name="com.tutorialspoint.Rectangle"
table="rectangle">
<key column="rid"></key>
<property name="length"></property>
<property name="width"></property>
</joined-subclass>
...
建立對映類
讓我們建立其資料需要持久化到資料庫中的 POJO 類。
Shape.java
package com.tutorialspoint;
public class Shape {
public int Id;
public String type;
public double area;
public Shape() {
this.type = "Shape";
}
public double calculateArea() {
this.area= 0;
setArea(area);
return area;
}
public int getId() {
return Id;
}
public void setId(int i) {
this.Id = i;
}
public String getType() {
return this.type;
}
public double getArea() {
return area;
}
public void setArea(double i) {
area = i;
}
}
Circle.java
package com.tutorialspoint;
import java.math.*;
public class Circle extends Shape {
private double radius;
public Circle(double rad ) {
this.radius = rad;
this.type = "Circle";
}
@Override
public double calculateArea() {
area = Math.PI * radius * radius;
return area;
}
public void setArea() {
this.area = calculateArea();
}
public double getArea() {
return area;
}
}
Rectangle.java
package com.tutorialspoint;
public class Rectangle extends Shape {
private double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
this.type = "Rectangle";
}
@Override
public double calculateArea() {
return length * width;
}
public void setArea() {
this.area = calculateArea();
}
public double getArea() {
return area;
}
}
建立資料庫表
讓我們在資料庫中建立表。將對應於上述物件的一個表,您願意提供持久化。考慮上述物件需要儲存和檢索到以下 RDBMS 表中:
CREATE TABLE students.shape( id int NOT NULL, type VARCHAR(20), area float ); CREATE TABLE students.circle( sid int NOT NULL, radius float ); CREATE TABLE students.rectangle( rid int NOT NULL, length float, width float );
建立對映配置檔案
現在建立一個對映檔案,該檔案指示 Hibernate 如何將定義的類對映到資料庫表。
shape.hbm.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 5.3//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.tutorialspoint.Shape" table="shape">
<id name="id">
<generator class="increment"></generator>
</id>
<property name="type"></property>
<property name="area"></property>
<joined-subclass name="com.tutorialspoint.Circle"
table="circle">
<key column="sid"></key>
<property name="radius"></property>
</joined-subclass>
<joined-subclass name="com.tutorialspoint.Rectangle"
table="rectangle">
<key column="rid"></key>
<property name="length"></property>
<property name="width"></property>
</joined-subclass>
</class>
</hibernate-mapping>
建立Hibernate配置檔案
現在為資料庫和其他詳細資訊建立一個Hibernate配置檔案。
hibernate.cfg.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hbm2ddl.auto">update</property>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQL8Dialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.cj.jdbc.Driver
</property>
<!—'students' is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql:///students
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
guest123
</property>
<!-- List of XML mapping files -->
<mapping resource = "shape.hbm.xml"/>
</session-factory>
</hibernate-configuration>
將屬性hbm2ddlauto設定為update將在程式執行期間建立表(如果尚未建立)。
建立應用程式類
最後,我們將建立包含 main() 方法的應用程式類來執行應用程式。我們將使用此應用程式來測試基於具體類的表對映。
TestTablePerSubclass.java
package com.tutorialspoint;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
public class TestTablePerSubclass{ {
public static void main(String[] args) {
// create a hibernate configuration
StandardServiceRegistry ssr=new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();
Metadata meta=new MetadataSources(ssr).getMetadataBuilder().build();
// get the sessionfactory and open the session
SessionFactory factory=meta.getSessionFactoryBuilder().build();
Session session=factory.openSession();
// begin the transaction
Transaction t=session.beginTransaction();
// create Shape instance and set details to persist
Shape s1=new Shape();
s1.setType(s1.getType());
s1.setArea(0);
// create Circle instance and set details to persist
Circle c1=new Circle(2);
c1.setArea();
// create Rectangle instance and set details to persist
Rectangle r1 = new Rectangle(3,4);
r1.setArea();
// persist all instances
session.persist(s1);
session.persist(c1);
session.persist(r1);
// commit the transaction and close the session
t.commit();
session.close();
System.out.println(" Successfully persisted 3 classes. Please check your database for results.");
}
}
編譯和執行
執行 TestTablePerHierarchy 二進位制檔案以執行程式。
輸出
您將獲得以下結果,並且記錄將建立在Shape表中。
$java TestTablePerHierarchy Successfully persisted 3 classes. Please check your database for results.
如果您檢查您的表,它們應該包含以下記錄:
mysql> select * from shape; +----+-----------+-------+ | id | type | area | +----+-----------+-------+ | 1 | Shape | 0 | | 2 | Circle | 12.56 | | 3 | Rectangle | 12 | +----+-----------+-------+ 3 rows in set (0.00 sec) mysql> select * from circle; +-----+--------+ | sid | radius | +-----+--------+ | 2 | 2 | +-----+--------+ 1 row in set (0.00 sec) mysql> select * from rectangle; +-----+--------+--------+ | rid | length | height | +-----+--------+--------+ | 3 | 3 | 4 | +-----+--------+--------+ 1 row in set (0.00 sec)