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 Position

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 High Level View

以下是 Hibernate 应用程序架构及其重要核心类的详细视图。

Hibernate Architecture

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 Directories

安装Hibernate

下载并解压缩最新版本的Hibernate安装文件后,您需要执行以下两个简单的步骤。确保您正确设置了CLASSPATH变量,否则在编译应用程序时会遇到问题。

  • 现在,将/lib中的所有库文件复制到您的CLASSPATH中,并更改您的classpath变量以包含所有JAR文件 -

  • 最后,将hibernate3.jar文件复制到您的CLASSPATH中。此文件位于安装的根目录中,是Hibernate执行其工作所需的主要JAR文件。

Hibernate先决条件

以下是Hibernate所需软件包/库的列表,您应该在开始使用Hibernate之前安装它们。要安装这些软件包,您需要将库文件从/lib复制到您的CLASSPATH中,并相应地更改您的CLASSPATH变量。

序号 软件包/库
1

MySQL Connector/J

MySQL驱动程序 https://dev.mysqlserver.cn/downloads/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

日志框架 https://logging.apache.org/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"  
   "https://hibernate.com.cn/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://127.0.0.1/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文档以获取与SessionSessionFactory相关的所有方法的完整列表。

序号 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.jarlib/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注解来覆盖此策略,该注解有两个参数strategygenerator,我不会在这里讨论它们,因此让我们只使用默认密钥生成策略。让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://127.0.0.1/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 也很重要。它利用如下所述的多级缓存方案:

Hibernate Caching

一级缓存

一级缓存是 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://127.0.0.1/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关联。对象可以通过调用savepersistsaveOrUpdate()方法进入持久化状态。

示例

Employee employee = new Employee();
employee.setName("John Doe");
employee.setSalary(50000.0);

在上面的示例中,Employee对象处于瞬时状态,因为它尚未与Hibernate会话关联。

持久化状态

持久化状态指的是与会话的关联。一旦我们调用savepersist,对象就处于持久化状态。当调用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://127.0.0.1/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://127.0.0.1/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.xmlXML文件

  • 一个名为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);

所需库

您还需要将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"
   "https://hibernate.com.cn/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://127.0.0.1/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.jspshowResults.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"
   "https://hibernate.com.cn/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://127.0.0.1/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"
   "https://hibernate.com.cn/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; 
   }
}

应用程序流程

  1. 用户在index.jsp中输入学生ID

  2. index.jsp中,表单被设置为处理'DisplayResults'操作。'DisplayResults'struts.xml中映射到Student.java,它是Action类。调用Student.javaexecute()方法,该方法返回"success"

  3. 在返回"success"后,如struts.xml中声明的那样,控制权传递到"showResults.jsp"

部署Web应用程序的步骤

我们使用Tomcat来部署此Web应用程序。安装Tomcat后,我们假设TOMCAT_HOME环境变量表示Tomcat安装目录。

  • TOMCAT_HOME/webapps目录下创建一个名为WebHibernate的目录。

  • 在此目录(TOMCAT_HOME/webapps)下,放置index.jspshowResults.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://127.0.0.1:8080/WebHibernate。您的索引页面将如下所示

Student Id in Web Application

输入1000作为学生ID。结果页面如下所示

Student Details in Web Application

Hibernate - 示例Web应用程序

在本教程中,我们将构建一个示例Web应用程序。这是一个简单的应用程序,包含2个jsp文件,index.jspshowResults.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"
   "https://hibernate.com.cn/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://127.0.0.1/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"
   "https://hibernate.com.cn/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.jspshowResults.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://127.0.0.1:8080/WebHibernate。您的索引页面将如下所示

Student Id in Web Application

输入1000作为学生ID。结果页面如下所示

Student Details in Web Application

Hibernate - 基于层次结构的表

简介

在 Hibernate 中,Java 类映射到数据库表。假设我们有一个类,它有几个子类,Hibernate 提供了三种方法将类映射到表:

  • 基于层次结构的表

  • 基于具体类的表

  • 基于子类的表

让我们详细讨论一下基于层次结构的表,并举例说明。

基于层次结构的表

假设我们有一个类 'Shape',它有两个子类 'Circle' 和 'Rectangle'。在这种类型的映射中,我们只会在数据库中有一个表。

Class Hierachy

层次结构中有三个类。Shape 是超类,CircleRectangle 是子类。考虑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"  
   "https://hibernate.com.cn/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://127.0.0.1/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'。在这种类型的映射中,我们将在数据库中有三个表。

Class Hierachy

层次结构中有三个类。Shape 是超类,CircleRectangle 是子类。子类的表将只包含子类的字段。为了查看子类的所有字段,需要对超类和子类进行连接。

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://127.0.0.1/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'。在这种策略中,我们为每个类创建一个新表。此外,在子类表中,添加了父类所有字段。因此,我们不需要与超类表连接,因为子类表包含超类所有字段。

Class Hierachy

层次结构中有三个类。Shape 是超类,CircleRectangle 是子类。为了映射类,我们将在 *.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://127.0.0.1/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)
广告