前言
Hibernate 是一個優秀的持久層的框架,當然,雖然現在説用得比較多的是 MyBaits,但是我工作中也不得不接觸 Hibernate,特別是一些老項目需要你維護的時候。所以,在此寫下這篇文章,方便自己回顧,也方便新手入門體驗 Hibernate。
注:使用的版本是 Hibernate 5.x 的
什麼是 ORM?
ORM(Object Relational Mapping,對象關係映射)可以説是一種理論,或者説是一種設計思想,主要是讓「關係型數據庫」和「面向對象編程語言」之間建立映射關係。目的是將數據庫中的數據轉換為對象,以便開發人員更方便地進行數據庫操作。
那為什麼會出現 ORM 呢?
在我們學習 JDBC 的時候,我們需要編寫大量的 SQL 語句來執行數據庫的 CRUD 操作,而且還需要手動將查詢結果轉換為對象。這樣的操作是比較繁瑣的,還容易出錯,而且對於大型項目來説,數據庫操作的代碼量通常很大,維護起來也是非常困難的。所以,ORM 出現了,幫助我們簡化數據庫操作,提高開發效率。
市面上有很多不同的 ORM 框架可供選擇,在 Java 後端開發的學習路線上,我們需要知道的就有 Hibernate 和 MyBatis。
簡單來説,ORM 就是將數據庫操作封裝成對象操作,通過對象的方式來進行數據庫的增刪改查。
理解 JPA 和 ORM 的關係
JPA(Java Persistence API)是 Java EE(現在稱為 Jakarta EE)規範中定義的一套 API,用於實現對象和關係數據庫之間的映射。JPA 提供了一種標準的方式來進行對象持久化操作。而 ORM 是一個通用的概念,不侷限於特定的編程語言或框架。比如 Python 也有對應的實現 ORM 的框架。
換句話説,JPA 它定義了一系列的接口和註解,開發者可以使用這些接口和註解來描述對象和數據庫表之間的映射關係,並進行數據庫操作。而 ORM 是一個更廣泛的概念,它可以適用於其他編程語言和框架,並不侷限於 Java 和 JPA。
注:JPA 就只定義,沒有具體實現,就是所謂的規範、標準,我 JPA 規定了這些 API 能進行相關操作,具體的實現是交給軟件廠商去實現的,比如 Hibernate 就是 JPA 標準的一種實現。
理解 JPA 和 Hibernate 的關係
Hibernate 是 JPA 的一種具體實現,它使用了 JPA 規範定義的接口和註解,提供了 ORM 功能。
從時間線上來看:JPA(Java Persistence API)是在 Hibernate 之後出現的。
最早由 Gavin King 在2001年創建 Hibernate,目標是簡化開發人員進行數據庫的操作,以面向對象的方式去操作數據庫。
JPA 的第一個版本是在2006年發佈的,其中包含了一系列的接口和註解,用於描述對象和數據庫表之間的映射關係,以及進行數據庫操作。Hibernate 的創始人 Gavin King 是 JPA 規範的主要參與者之一。
JPA 規範的出現是為了標準化 ORM 框架的行為和功能,使開發人員可以在不同的 ORM 實現之間進行切換,而不需要修改大量的代碼。
總結來説,Hibernate 是在 JPA 規範之前出現的 ORM 框架,而 JPA 是在 Hibernate 的基礎上產生的一套標準化的 ORM API。Hibernate 作為 JPA 的一種實現,為開發人員提供了強大的 ORM 功能,併成為了 JPA 規範的主要影響者之一。
正題:Hibernate 簡介
Hibernate 是全自動的對象關係映射的持久層框架,主要通過持久化類(.Java,當然,也習慣説的實體類)、映射文件(.hbm.xml)和配置文件(.cfg.xml)來操作關係型數據庫。
Hibernate 封裝了數據庫的訪問細節,通過配置的屬性文件,來關聯上關係型數據庫和實體類的。
Hibernate 中有 3 個我們需要知道的類,分別是配置類(Configuration)、會話工廠類(SessionFactory)和會話類(Session),注意,此處的 Session 可不是 HttpSession 啊!
- 配置類
主要負責管理 Hibernate 的配置信息以及 Hibernate 的啓動,在運行的時候,配置類會讀取一些底層的基本信息,比如數據庫的 URL、數據庫的用户名、密碼、驅動類、方言(適配器,Dialect)等信息。
- 會話工廠類
主要負責生成 Session,這個工廠類會保存當前數據庫中所有的映射關係。
- 會話類
持久化操作的核心,通過它實現 CRUD,它不是線程安全的,需要注意不要多個線程共享一個 Session 對象。
理解會話二字:顧名思義,實際上就是交流,通信。在網絡中,一次會話可以是一次 HTTP 請求到 HTTP 響應的過程,在數據庫操作中,一次會話,可以是一次新增操作的請求到數據庫中,然後數據庫做出響應。
使用 Maven 構建 Hibernate 項目
最原始引入 Jar 包的方式來創建 Hibernate 項目,可以參考這裏:
http://m.biancheng.net/hibernate/first-example.html
由於我比較懶,所以使用 Maven 來構建一個具有 Hibernate 的 Web 項目。
引入依賴項
分別引入 Hibernate、MySQL 數據庫驅動、單元測試 Junit4(創建 Maven 時自帶的)。
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.14.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
Hibernate 配置文件
在 resource 目錄下創建一個 hibernate.cfg.xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置數據源、連接池等相關信息 -->
<property name="connection.url">jdbc:mysql://localhost:3306/demo_hibernate</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- Hibernate 方言 -->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 打印 SQL 語句-->
<property name="show_sql">true</property>
<!-- 格式化 SQL 語句-->
<property name="format_sql">true</property>
<!-- 映射文件所在位置 -->
<mapping resource="cn/god23bin/demo/domain/mapping/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
以上的配置只是一小部分,還可以配置數據庫連接池、是否自動生成數據庫表等等。
持久化類(實體類)
我們是通過持久化類來操作數據庫表的,也就是 ORM 的體現,即對象映射到數據庫表,數據庫表也映射對象,操作對象就相當於操作數據庫表。所以我們需要編寫持久化類來描述數據庫表,類中的屬性需要與數據庫表中的字段相匹配。
創建一個 User 類,作為一個 JavaBean(只有 getter 和 setter 方法,沒有其他業務方法的對象)
package cn.god23bin.demo.domain.entity;
/**
* @author god23bin
*/
public class User {
private Integer id;
private String name;
private String password;
// 省略 getter 和 setter 方法
}
這種類(JavaBean)在日常開發中是無處不在的,百分之百會用到,也稱它為 POJO(Plain Old Java Object),我們知道這種概念就行,反正這種類就只有屬性和對應的 getter 和 setter 方法。
需要注意的幾點:
- 必須有無參構造方法,便於 Hibernate 通過 Constructor.newInstance() 實例化持久類。
- 提供一個標識屬性,一般這個標識屬性映射的是數據庫表中的主鍵字段,就上面 User 中的 id 屬性。
- 設計實體類,屬性都是聲明為 private 的。
Hibernate 映射
我們單獨寫了一個持久化類,目前是還沒有做映射的,也就是説還不能通過這個類去操作數據庫,那如何去做映射呢?
這就涉及到一個映射文件了,映射文件是 xml 文件,命名規則一般是 持久化類名.hbm.xml,以 User 為例,它的映射文件就是 User.hbm.xml。
我們可以在項目某個包下創建映射文件,我選擇在 cn.god23bin.demo.domain.mapping 包下創建:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- name 屬性:持久化類的全路徑 -->
<!-- table 屬性:表的名稱 -->
<class name="cn.god23bin.demo.domain.entity.User" table="user">
<!-- 主鍵 -->
<id name="id" column="id" type="java.lang.Integer">
<!-- 主鍵生成策略 -->
<generator class="native"/>
</id>
<!-- type 屬性 的三種寫法 -->
<!-- 1. Java類型 :java.lang.String -->
<!-- 2. Hibernate類型:string -->
<!-- 3. SQL類型 :不能直接使用type屬性,需要子標籤<column> -->
<!-- <column name="name" sql-type="varchar(20)"/> -->
<property name="name" column="name" type="string" not-null="true" length="50"/>
<property name="password" column="password" not-null="true" length="50"/>
</class>
</hibernate-mapping>
注意:映射文件的編寫需要按照持久化類來編寫,而不是數據庫表。
cn.god23bin.demo.domain.entity.User 稱為全路徑 | 全限定類名 | 全限定名 | 全包名,反正我是見過多種叫法的,指的都是同個東西。
如果映射文件中沒有配置 column 和 type 屬性,那麼 Hibernate 會默認使用持久化類中的屬性名和屬性類型去匹配數據庫表中的字段。
創建完這個映射文件後,我們需要在配置文件中 <session-factory> 裏指定該映射文件所在的位置,這樣 Hibernate 才知道映射文件在哪裏。
<mapping resource="cn/god23bin/demo/domain/mapping/User.hbm.xml" />
完整的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:mysql://localhost:3306/demo_hibernate</property>
<property name="connection.username">root</property>
<property name="connection.password">123456</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- 映射文件所在位置 -->
<mapping resource="cn/god23bin/demo/domain/mapping/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
配置 pom.xml
由於我是 Maven 來構建項目的,所以需要新增一個配置,便於讓 Hibernate 能夠找到 Maven 工程編譯後的 *.hbm.xml 映射文件。
在 pom.xml 中,找到 build 標籤,在裏面加上如下的配置:
<build>
...
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
Hibernate 工具類
Hibernate 有 3 個需要知道的類,不知道現在你還記不記得,不記得就翻到上面簡介那裏看看。
其中,Session 是持久化操作的核心類,通過它可以實現 CRUD 操作。那麼如何獲取 Session 對象呢?顯而易見,就是通過 Session 工廠,即 SessionFactory 對象,來獲取 Session 對象,那問題又來了,Session 工廠如何獲取?
這裏就得説到 Configuration 配置類了,通過它創建 SessionFactory 對象,進而獲取 Session 對象。
核心代碼是這樣的:
// 讀取 hibernate.cfg.xml 配置文件並創建 SessionFactory
Configuration configure = new Configuration().configure(); // 加載配置文件,configure() 方法可以指定配置文件所在位置,沒有指定的話,默認為項目的 classpath 根目錄下的 hibernate.cfg.xml
SessionFactory sessionFactory = configure.buildSessionFactory(); // 創建 SessionFactory 對象
一般情況下,我們會寫一個工具類來獲取 Session 對象,如下:
package cn.god23bin.demo.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/**
* @author god23bin
*/
public class HibernateUtil {
/**
* 一個 ThreadLocal 變量,用於存儲線程局部變量 Session。
* ThreadLocal 提供了線程局部變量的機制,保證每個線程都有自己的 Session 實例。
*/
private static final ThreadLocal<Session> THREAD_LOCAL = new ThreadLocal<>();
private static SessionFactory sessionFactory;
static {
try{
// 讀取 hibernate.cfg.xml 配置文件並創建 SessionFactory
Configuration configure = new Configuration().configure();
sessionFactory = configure.buildSessionFactory();
} catch (Exception e) {
System.err.println("Hibernate 創建會話工廠失敗!");
e.printStackTrace();
}
}
/**
* 獲取 Session 對象
*/
public static Session getSession() {
Session session = THREAD_LOCAL.get();
if (session == null || session.isOpen()) {
if (sessionFactory == null) {
rebuildSessionFactory();
}
session = (sessionFactory != null) ? sessionFactory.openSession() : null;
THREAD_LOCAL.set(session);
}
return session;
}
/**
* 重新創建會話工廠
*/
private static void rebuildSessionFactory() {
try{
// 讀取 hibernate.cfg.xml 配置文件並創建 SessionFactory
Configuration configure = new Configuration().configure();
sessionFactory = configure.buildSessionFactory();
} catch (Exception e) {
System.err.println("Hibernate 創建會話工廠失敗!");
e.printStackTrace();
}
}
/**
* 返回唯一的會話工廠對象
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
/**
* 關閉 Session 對象
*/
public static void closeSession() {
Session session = THREAD_LOCAL.get();
THREAD_LOCAL.remove();
if (session != null) {
session.close();
}
}
}
測試
創建一張 user 表:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(50) NOT NULL COMMENT '名稱',
`password` varchar(50) NOT NULL COMMENT '密碼',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
編寫一個測試類,由於我這裏是 Maven 項目,按照約定,測試類放在 /src/test/java/ 目錄下,我這裏就把這個測試類 HibernateTest 放在 java 目錄下的 cn.god23bin.demo 包中:
package cn.god23bin.demo;
import cn.god23bin.demo.domain.entity.User;
import cn.god23bin.demo.util.HibernateUtil;
import org.hibernate.Session;
import org.junit.Test;
/**
* @author god23bin
*/
public class HibernateTest {
@Test
public void test() {
// 獲取 Session 對象
Session session = HibernateUtil.getSession();
User user = new User();
user.setName("god23bin");
user.setPassword("123456");
try {
// 開啓事務,即使是執行一次數據庫操作,也是事務
session.beginTransaction();
// 執行插入操作
session.save(user);
// 提交事務
session.getTransaction().commit();
} catch (Exception e) {
// 發生異常,則回滾事務
session.getTransaction().rollback();
System.out.println("插入User數據失敗!");
e.printStackTrace();
} finally{
// 關閉 Session 對象
HibernateUtil.closeSession();
}
}
}
控制枱輸出:
五月 07, 2023 11:52:13 下午 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 5.6.14.Final
五月 07, 2023 11:52:15 下午 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
五月 07, 2023 11:52:17 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!)
五月 07, 2023 11:52:17 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/demo_hibernate]
五月 07, 2023 11:52:17 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {user=root, password=****}
五月 07, 2023 11:52:17 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
五月 07, 2023 11:52:17 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
Sun May 07 23:52:17 CST 2023 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
五月 07, 2023 11:52:18 下午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
五月 07, 2023 11:52:20 下午 org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Hibernate:
insert
into
user
(name, password)
values
(?, ?)
Process finished with exit code 0
我們可以查看數據庫中 User 表中是否存在我們剛剛插入的數據,可以發現是存在的:
總結
我們依次説明了什麼是 ORM,並且梳理了 JPA 和 ORM的關係以及 JPA 和 Hibernate 的關係。
我相信還是有很多人沒有搞清楚它們之間的聯繫的,就只是學了而已,或者説學過而已,當然,也有的人説,知道了它們的關係又能怎樣呢?我不知道我也能用 Hibernate 去操作數據庫。話雖如此,但是我認為明白它們之間的聯繫,是有利於我們後續其他知識的學習的,也能跟其他知識建立起聯繫,而不是單獨的一個知識孤島。
接着介紹了 Hibernate,以及如何使用 Maven 項目去構建一個具有 Hibernate 的 Web 應用,畢竟咱們開發,基本都是 Web 應用程序。
使用 Maven 去構建,就需要引入相關的依賴,Hibernate 的核心依賴以及數據庫驅動的依賴,接着需要編寫配置文件、持久化類、持久化類的映射文件,最後寫一個獲取 Session 對象的工具類,便於我們獲取 Session 對象執行數據庫操作。
以上,就是本篇文章的內容,現在恭喜你已經入門 Hibernate 了!是不是很快上手了!哈哈哈
最後的最後
希望各位屏幕前的靚仔靚女們給個三連!你輕輕地點了個贊,那將在我的心裏世界增添一顆明亮而耀眼的星!
咱們下期再見!