Hibernate 简明教程
Hibernate - ORM Overview
What is JDBC?
JDBC 代表 Java Database Connectivity 。它提供了一组 Java API,用于从 Java 程序访问关系数据库。这些 Java API 使 Java 程序能够执行 SQL 语句和与任何 SQL 兼容数据库进行交互。
JDBC stands for Java Database Connectivity. It provides a set of Java API for accessing the relational databases from Java program. These Java APIs enables Java programs to execute SQL statements and interact with any SQL compliant database.
JDBC 提供了一个灵活的架构来编写一个数据库独立的应用程序,该应用程序可以在不同的平台上运行并与不同的 DBMS 交互,而无需进行任何修改。
JDBC provides a flexible architecture to write a database independent application that can run on different platforms and interact with different DBMS without any modification.
Pros and Cons of JDBC
Pros of JDBC |
Cons of JDBC |
Clean and simple SQL processing Good performance with large data Very good for small applications Simple syntax so easy to learn |
Complex if it is used in large projects Large programming overhead No encapsulation Hard to implement MVC concept Query is DBMS specific |
Why Object Relational Mapping (ORM)?
当我们使用面向对象系统时,对象模型和关系数据库之间存在不匹配。RDBMS 以表格形式表示数据,而面向对象语言(例如 Java 或 C#)则以相互连接的对象图表的形式表示数据。
When we work with an object-oriented system, there is a mismatch between the object model and the relational database. RDBMSs represent data in a tabular format whereas object-oriented languages, such as Java or C# represent it as an interconnected graph of objects.
考虑以下具有适当构造函数和关联公共函数的 Java 类:
Consider the following Java Class with proper constructors and associated public function −
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 表中:
Consider the above objects are to be stored and retrieved into the following RDBMS table −
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)
);
第一个问题是,如果在开发了一些页面或应用程序后需要修改数据库的设计,该怎么办?其次,在关系数据库中加载和存储对象会使我们面临以下五个不匹配问题:
First problem, what if we need to modify the design of our database after having developed a few pages or our application? Second, loading and storing objects in a relational database exposes us to the following five mismatch problems −
Sr.No. |
Mismatch & Description |
1 |
Granularity Sometimes you will have an object model, which has more classes than the number of corresponding tables in the database. |
2 |
Inheritance RDBMSs do not define anything similar to Inheritance, which is a natural paradigm in object-oriented programming languages. |
3 |
Identity An RDBMS defines exactly one notion of 'sameness': the primary key. Java, however, defines both object identity (a==b) and object equality (a.equals(b)). |
4 |
Associations Object-oriented languages represent associations using object references whereas an RDBMS represents an association as a foreign key column. |
5 |
Navigation The ways you access objects in Java and in RDBMS are fundamentally different. |
*O*对象-*R*关系映射 (ORM) 是解决上述所有阻抗失配问题的办法。
The *O*bject-*R*elational *M*apping (ORM) is the solution to handle all the above impedance mismatches.
What is ORM?
ORM 代表 *O*对象-*R*关系映射 (ORM) 是一种用于转换关系数据库和诸如 Java、C# 等面向对象编程语言之间数据的编程技术。
ORM stands for *O*bject-*R*elational *M*apping (ORM) is a programming technique for converting data between relational databases and object oriented programming languages such as Java, C#, etc.
与纯 JDBC 相比,一个 ORM 系统有以下优点:
An ORM system has the following advantages over plain JDBC −
Sr.No. |
Advantages |
1 |
Let’s business code access objects rather than DB tables. |
2 |
Hides details of SQL queries from OO logic. |
3 |
Based on JDBC 'under the hood.' |
4 |
No need to deal with the database implementation. |
5 |
Entities based on business concepts rather than database structure. |
6 |
Transaction management and automatic key generation. |
7 |
Fast development of application. |
一个 ORM 解决方案包含以下四个实体:
An ORM solution consists of the following four entities −
Sr.No. |
Solutions |
1 |
An API to perform basic CRUD operations on objects of persistent classes. |
2 |
A language or API to specify queries that refer to classes and properties of classes. |
3 |
A configurable facility for specifying mapping metadata. |
4 |
A technique to interact with transactional objects to perform dirty checking, lazy association fetching, and other optimization functions. |
Java ORM Frameworks
在 Java 中有几个持久框架和 ORM 选项。持久性框架是一种将对象存储到关系数据库并从关系数据库中检索对象的 ORM 服务。
There are several persistent frameworks and ORM options in Java. A persistent framework is an ORM service that stores and retrieves objects into a relational database.
-
Enterprise JavaBeans Entity Beans
-
Java Data Objects
-
Castor
-
TopLink
-
Spring DAO
-
Hibernate
-
And many more
Hibernate - Overview
Hibernate 是 Java 的 *O*bject-*R*elational *M*apping (ORM) 解决方案。它是一个由 Gavin King 于 2001 年创建的开源持久性框架。它是面向任何 Java 应用程序的强大、高性能对象关系持久性和查询服务。
Hibernate is an *O*bject-*R*elational *M*apping (ORM) solution for JAVA. It is an open source persistent framework created by Gavin King in 2001. It is a powerful, high performance Object-Relational Persistence and Query service for any Java Application.
Hibernate 将 Java 类映射到数据库表和从 Java 数据类型到 SQL 数据类型,免除了 95% 的开发人员与常见的与数据持久性相关编程任务。
Hibernate maps Java classes to database tables and from Java data types to SQL data types and relieves the developer from 95% of common data persistence related programming tasks.
Hibernate 介于传统 Java 对象和数据库服务器之间,可以基于适当的 O/R 机制和模式处理持久化这些对象的所有工作。
Hibernate sits between traditional Java objects and database server to handle all the works in persisting those objects based on the appropriate O/R mechanisms and patterns.
Hibernate Advantages
-
Hibernate takes care of mapping Java classes to database tables using XML files and without writing any line of code.
-
Provides simple APIs for storing and retrieving Java objects directly to and from the database.
-
If there is change in the database or in any table, then you need to change the XML file properties only.
-
Abstracts away the unfamiliar SQL types and provides a way to work around familiar Java Objects.
-
Hibernate does not require an application server to operate.
-
Manipulates Complex associations of objects of your database.
-
Minimizes database access with smart fetching strategies.
-
Provides simple querying of data.
Supported Databases
Hibernate 几乎支持所有主要的 RDBMS。以下是 Hibernate 支持的若干数据库引擎列表:
Hibernate supports almost all the major RDBMS. Following is a list of few of the database engines supported by Hibernate −
-
HSQL Database Engine
-
DB2/NT
-
MySQL
-
PostgreSQL
-
FrontBase
-
Oracle
-
Microsoft SQL Server Database
-
Sybase SQL Server
-
Informix Dynamic Server
Hibernate - Architecture
Hibernate 具有分层架构,它帮助用户在不知道底层 API 的情况下进行操作。Hibernate 利用数据库和配置数据向应用程序提供持久性服务(和持久性对象)。
Hibernate has a layered architecture which helps the user to operate without having to know the underlying APIs. Hibernate makes use of the database and configuration data to provide persistence services (and persistent objects) to the application.
以下是 Hibernate 应用程序架构的高级视图。
Following is a very high level view of the Hibernate Application Architecture.
以下是对 Hibernate 应用程序架构及其重要核心类的详细视图。
Following is a detailed view of the Hibernate Application Architecture with its important core classes.
Hibernate 使用各种现有的 Java API,如 JDBC、Java 事务 API(JTA)、Java 命名和目录接口 (JNDI)。JDBC 提供对关系数据库通用功能的初级抽象,使几乎任何带有 JDBC 驱动的数据库都可以受 Hibernate 支持。JNDI 和 JTA 允许 Hibernate 与 J2EE 应用程序服务器集成。
Hibernate uses various existing Java APIs, like JDBC, Java Transaction API(JTA), and Java Naming and Directory Interface (JNDI). JDBC provides a rudimentary level of abstraction of functionality common to relational databases, allowing almost any database with a JDBC driver to be supported by Hibernate. JNDI and JTA allow Hibernate to be integrated with J2EE application servers.
以下部分简要描述了 Hibernate 应用程序架构中涉及的每个类对象。
Following section gives brief description of each of the class objects involved in Hibernate Application Architecture.
Configuration Object
Configuration 对象是您在任何 Hibernate 应用程序中创建的第一个 Hibernate 对象。它通常仅在应用程序初始化期间创建一次。它表示 Hibernate 所需的配置或属性文件。
The Configuration object is the first Hibernate object you create in any Hibernate application. It is usually created only once during application initialization. It represents a configuration or properties file required by the Hibernate.
Configuration 对象提供两个键组件:
The Configuration object provides two keys components −
-
Database Connection − This is handled through one or more configuration files supported by Hibernate. These files are hibernate.properties and hibernate.cfg.xml.
-
Class Mapping Setup − This component creates the connection between the Java classes and database tables.
SessionFactory Object
配置对象用于创建 SessionFactory 对象,后者使用提供的配置文件为应用程序配置 Hibernate,并允许实例化 Session 对象。SessionFactory 是一个线程安全对象,可用于应用程序的所有线程。
Configuration object is used to create a SessionFactory object which in turn configures Hibernate for the application using the supplied configuration file and allows for a Session object to be instantiated. The SessionFactory is a thread safe object and used by all the threads of an application.
SessionFactory 是一个重量级对象;它通常在应用程序启动期间创建并保留以便以后使用。您需要为使用独立配置文件的每个数据库创建一个 SessionFactory 对象。因此,如果您要使用多个数据库,则需要创建多个 SessionFactory 对象。
The SessionFactory is a heavyweight object; it is usually created during application start up and kept for later use. You would need one SessionFactory object per database using a separate configuration file. So, if you are using multiple databases, then you would have to create multiple SessionFactory objects.
Session Object
使用会话来与数据库获得物理连接。会话对象是轻量的,并且设计为在需要与数据库进行交互时进行实例化。持久性对象通过会话对象保存并检索。
A Session is used to get a physical connection with a database. The Session object is lightweight and designed to be instantiated each time an interaction is needed with the database. Persistent objects are saved and retrieved through a Session object.
会话对象不应长期保持打开状态,因为它们通常不是线程安全的,并且应根据需要创建并销毁它们。
The session objects should not be kept open for a long time because they are not usually thread safe and they should be created and destroyed them as needed.
Transaction Object
事务代表与数据库的工作单元,并且大多数 RDBMS 都支持事务功能。Hibernate 中的事务由底层事务管理器和事务(来自 JDBC 或 JTA)处理。
A Transaction represents a unit of work with the database and most of the RDBMS supports transaction functionality. Transactions in Hibernate are handled by an underlying transaction manager and transaction (from JDBC or JTA).
这是一个可选对象,Hibernate 应用程序可以选择不使用此接口,而是在其自己的应用程序代码中管理事务。
This is an optional object and Hibernate applications may choose not to use this interface, instead managing transactions in their own application code.
Query Object
查询对象使用 SQL 或 Hibernate 查询语言 (HQL) 字符串从数据库中检索数据并创建对象。Query 实例用于绑定查询参数,限制查询返回的结果数量,最终执行查询。
Query objects use SQL or Hibernate Query Language (HQL) string to retrieve data from the database and create objects. A Query instance is used to bind query parameters, limit the number of results returned by the query, and finally to execute the query.
Hibernate - Environment
本章解释如何安装 Hibernate 和其他相关软件包,为 Hibernate 应用程序准备环境。我们将使用 MySQL 数据库试验 Hibernate 示例,所以请确保已经为 MySQL 数据库设置了安装程序。有关 MySQL 的更多详细信息,您可以查看我们的 MySQL Tutorial 。
This chapter explains how to install Hibernate and other associated packages to prepare an environment for the Hibernate applications. We will work with MySQL database to experiment with Hibernate examples, so make sure you already have a setup for MySQL database. For more detail on MySQL, you can check our MySQL Tutorial.
Downloading Hibernate
假设您已经在系统上安装了最新版本的 Java。以下是下载和安装 Hibernate 到系统中的简单步骤:
It is assumed that you already have the latest version of Java installed on your system. Following are the simple steps to download and install Hibernate on your system −
-
Make a choice whether you want to install Hibernate on Windows, or Unix and then proceed to the next step to download .zip file for windows and .tz file for Unix.
-
Download the latest version of Hibernate from http://www.hibernate.org/downloads.
-
At the time of writing this tutorial, I downloaded hibernate-distribution3.6.4.Final and when you unzip the downloaded file, it will give you directory structure as shown in the following image
Installing Hibernate
下载并解压缩最新版本的 Hibernate 安装文件后,您需要执行以下两个简单步骤。确保正确设置 CLASSPATH 变量,否则在编译应用程序时将会遇到问题。
Once you downloaded and unzipped the latest version of the Hibernate Installation file, you need to perform following two simple steps. Make sure you are setting your CLASSPATH variable properly otherwise you will face problem while compiling your application.
-
Now, copy all the library files from /lib into your CLASSPATH, and change your classpath variable to include all the JARs −
-
Finally, copy hibernate3.jar file into your CLASSPATH. This file lies in the root directory of the installation and is the primary JAR that Hibernate needs to do its work.
Hibernate Prerequisites
以下是 Hibernate 需要的包/库列表,您应在开始使用 Hibernate 之前安装它们。要安装这些包,您必须从 /lib 将库文件复制到您的类路径 CLASSPATH 中,并相应地更改您的 CLASSPATH 变量。
Following is the list of the packages/libraries required by Hibernate and you should install them before starting with Hibernate. To install these packages, you will have to copy library files from /lib into your CLASSPATH, and change your CLASSPATH variable accordingly.
Sr.No. |
Packages/Libraries |
1 |
dom4j XML parsing www.dom4j.org/ |
2 |
Xalan XSLT Processor https://xml.apache.org/xalan-j/ |
3 |
Xerces The Xerces Java Parser https://xml.apache.org/xerces-j/ |
4 |
cglib Appropriate changes to Java classes at runtime http://cglib.sourceforge.net/ |
5 |
log4j Logging Faremwork https://logging.apache.org/log4j |
6 |
Commons Logging, Email etc. https://jakarta.apache.org/commons |
7 |
SLF4J Logging Facade for Java https://www.slf4j.org |
Hibernate - Configuration
Hibernate 需要提前知道——在哪里找到映射信息,这些映射信息定义了 Java 类和数据库表之间的相互关系。Hibernate 还需要一组与数据库和其他相关参数相关的配置设置。所有此类信息通常按标准 Java 属性文件 hibernate.properties 提供,或按名为 hibernate.cfg.xml 的 XML 文件提供。
Hibernate requires to know in advance — where to find the mapping information that defines how your Java classes relate to the database tables. Hibernate also requires a set of configuration settings related to database and other related parameters. All such information is usually supplied as a standard Java properties file called hibernate.properties, or as an XML file named hibernate.cfg.xml.
我在示例中将考虑使用 XML 格式的文件 hibernate.cfg.xml 来指定必需的 Hibernate 属性。大多数属性采用其默认值,并且不必在属性文件中指定它们(除非确实需要)。此文件保存在应用程序类路径的根目录中。
I will consider XML formatted file hibernate.cfg.xml to specify required Hibernate properties in my examples. Most of the properties take their default values and it is not required to specify them in the property file unless it is really required. This file is kept in the root directory of your application’s classpath.
Hibernate Properties
以下是一些重要属性,在独立情况下,您需要对其进行数据库配置:
Following is the list of important properties, you will be required to configure for a databases in a standalone situation −
Sr.No. |
Properties & Description |
1 |
hibernate.dialect This property makes Hibernate generate the appropriate SQL for the chosen database. |
2 |
hibernate.connection.driver_class The JDBC driver class. |
3 |
hibernate.connection.url The JDBC URL to the database instance. |
4 |
hibernate.connection.username The database username. |
5 |
hibernate.connection.password The database password. |
6 |
hibernate.connection.pool_size Limits the number of connections waiting in the Hibernate database connection pool. |
7 |
hibernate.connection.autocommit Allows autocommit mode to be used for the JDBC connection. |
如果您使用数据库与应用程序服务器和 JNDI 一起使用,则需要配置以下属性:
If you are using a database along with an application server and JNDI, then you would have to configure the following properties −
Sr.No. |
Properties & Description |
1 |
hibernate.connection.datasource The JNDI name defined in the application server context, which you are using for the application. |
2 |
hibernate.jndi.class The InitialContext class for JNDI. |
3 |
hibernate.jndi.<JNDIpropertyname> Passes any JNDI property you like to the JNDI InitialContext. |
4 |
hibernate.jndi.url Provides the URL for JNDI. |
5 |
hibernate.connection.username The database username. |
6 |
hibernate.connection.password The database password. |
Hibernate with MySQL Database
MySQL 是当今最流行的开源数据库系统之一。我们将创建 hibernate.cfg.xml 配置文件,并将其放入应用程序类路径的根目录中。您必须确保您的 MySQL 数据库中存在 testdb 数据库,并且您有用户 test 可用于访问数据库。
MySQL is one of the most popular open-source database systems available today. Let us create hibernate.cfg.xml configuration file and place it in the root of your application’s classpath. You will have to make sure that you have testdb database available in your MySQL database and you have a user test available to access the database.
XML 配置文件必须符合 Hibernate 3 Configuration DTD,可以在 http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd 中获取该 DTD。
The XML configuration file must conform to the Hibernate 3 Configuration DTD, which is available at http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd.
<?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 test is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
root123
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
上述配置文件包含 <mapping> 标记,这些标记与 Hibernate 映射文件相关,我们将在下一章中看到 Hibernate 映射文件确切是什么以及我们如何以及为什么使用它?
The above configuration file includes <mapping> tags, which are related to hibernatemapping file and we will see in next chapter what exactly a hibernate mapping file is and how and why do we use it?
以下是各种重要的数据库方言属性类型的列表 -
Following is the list of various important databases dialect property type −
Sr.No. |
Database & Dialect Property |
1 |
DB2 org.hibernate.dialect.DB2Dialect |
2 |
HSQLDB org.hibernate.dialect.HSQLDialect |
3 |
HypersonicSQL org.hibernate.dialect.HSQLDialect |
4 |
Informix org.hibernate.dialect.InformixDialect |
5 |
Ingres org.hibernate.dialect.IngresDialect |
6 |
Interbase org.hibernate.dialect.InterbaseDialect |
7 |
Microsoft SQL Server 2000 org.hibernate.dialect.SQLServerDialect |
8 |
Microsoft SQL Server 2005 org.hibernate.dialect.SQLServer2005Dialect |
9 |
Microsoft SQL Server 2008 org.hibernate.dialect.SQLServer2008Dialect |
10 |
MySQL org.hibernate.dialect.MySQLDialect |
11 |
Oracle (any version) org.hibernate.dialect.OracleDialect |
12 |
Oracle 11g org.hibernate.dialect.Oracle10gDialect |
13 |
Oracle 10g org.hibernate.dialect.Oracle10gDialect |
14 |
Oracle 9i org.hibernate.dialect.Oracle9iDialect |
15 |
PostgreSQL org.hibernate.dialect.PostgreSQLDialect |
16 |
Progress org.hibernate.dialect.ProgressDialect |
17 |
SAP DB org.hibernate.dialect.SAPDBDialect |
18 |
Sybase org.hibernate.dialect.SybaseDialect |
19 |
Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect |
Hibernate - Sessions
使用会话来与数据库获得物理连接。会话对象是轻量的,并且设计为在需要与数据库进行交互时进行实例化。持久性对象通过会话对象保存并检索。
A Session is used to get a physical connection with a database. The Session object is lightweight and designed to be instantiated each time an interaction is needed with the database. Persistent objects are saved and retrieved through a Session object.
会话对象不应该长时间保持打开状态,因为它们通常不是线程安全的,并且应该根据需要创建和销毁它们。会话的主要功能是为映射实体类的实例提供、创建、读取和删除操作。
The session objects should not be kept open for a long time because they are not usually thread safe and they should be created and destroyed them as needed. The main function of the Session is to offer, create, read, and delete operations for instances of mapped entity classes.
实例在给定的时间点可以处于以下三个状态之一 -
Instances may exist in one of the following three states at a given point in time −
-
transient − A new instance of a persistent class, which is not associated with a Session and has no representation in the database and no identifier value is considered transient by Hibernate.
-
persistent − You can make a transient instance persistent by associating it with a Session. A persistent instance has a representation in the database, an identifier value and is associated with a Session.
-
detached − Once we close the Hibernate Session, the persistent instance will become a detached instance.
如果其持久类是可序列化的,则会话实例是可序列化的。一个典型的交易应该使用以下习语 -
A Session instance is serializable if its persistent classes are serializable. A typical transaction should use the following idiom −
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();
}
如果会话抛出异常,则必须回滚事务并放弃会话。
If the Session throws an exception, the transaction must be rolled back and the session must be discarded.
Session Interface Methods
Session 接口提供了一些方法,但我只列出几个重要的方法,我们将在本教程中使用它们。您可以检查 Hibernate 文档以获取与 Session 和 SessionFactory 相关联的方法的完整列表。
There are number of methods provided by the Session interface, but I’m going to list down a few important methods only, which we will use in this tutorial. You can check Hibernate documentation for a complete list of methods associated with Session and SessionFactory.
Sr.No. |
Session Methods & Description |
1 |
Transaction beginTransaction() Begin a unit of work and return the associated Transaction object. |
2 |
void cancelQuery() Cancel the execution of the current query. |
3 |
void clear() Completely clear the session. |
4 |
Connection close() End the session by releasing the JDBC connection and cleaning up. |
5 |
Criteria createCriteria(Class persistentClass) Create a new Criteria instance, for the given entity class, or a superclass of an entity class. |
6 |
Criteria createCriteria(String entityName) Create a new Criteria instance, for the given entity name. |
7 |
Serializable getIdentifier(Object object) Return the identifier value of the given entity as associated with this session. |
8 |
Query createFilter(Object collection, String queryString) Create a new instance of Query for the given collection and filter string. |
9 |
Query createQuery(String queryString) Create a new instance of Query for the given HQL query string. |
10 |
SQLQuery createSQLQuery(String queryString) Create a new instance of SQLQuery for the given SQL query string. |
11 |
void delete(Object object) Remove a persistent instance from the datastore. |
12 |
void delete(String entityName, Object object) Remove a persistent instance from the datastore. |
13 |
Session get(String entityName, Serializable id) Return the persistent instance of the given named entity with the given identifier, or null if there is no such persistent instance. |
14 |
SessionFactory getSessionFactory() Get the session factory which created this session. |
15 |
void refresh(Object object) Re-read the state of the given instance from the underlying database. |
16 |
Transaction getTransaction() Get the Transaction instance associated with this session. |
17 |
boolean isConnected() Check if the session is currently connected. |
18 |
boolean isDirty() Does this session contain any changes which must be synchronized with the database? |
19 |
boolean isOpen() Check if the session is still open. |
20 |
Serializable save(Object object) Persist the given transient instance, first assigning a generated identifier. |
21 |
void saveOrUpdate(Object object) Either save(Object) or update(Object) the given instance. |
22 |
void update(Object object) Update the persistent instance with the identifier of the given detached instance. |
23 |
void update(String entityName, Object object) Update the persistent instance with the identifier of the given detached instance. |
Hibernate - Persistent Class
Hibernate 的整个概念是获取 Java 类属性中的值,并将它们持久化到数据库表中。映射文档帮助 Hibernate 确定如何从类中提取值,并将其与表和关联字段进行映射。
The entire concept of Hibernate is to take the values from Java class attributes and persist them to a database table. A mapping document helps Hibernate in determining how to pull the values from the classes and map them with table and associated fields.
在数据库表中存储其对象或实例的 Java 类在 Hibernate 中称为持久类。如果这些类遵循一些简单规则,Hibernate 就能很好地工作,这些规则也称为 Plain Old Java Object (POJO) 编程模型。
Java classes whose objects or instances will be stored in database tables are called persistent classes in Hibernate. Hibernate works best if these classes follow some simple rules, also known as the Plain Old Java Object (POJO) programming model.
以下为持久类的主要规则,但其中没有一项属于硬性要求:
There are following main rules of persistent classes, however, none of these rules are hard requirements −
-
All Java classes that will be persisted need a default constructor.
-
All classes should contain an ID in order to allow easy identification of your objects within Hibernate and the database. This property maps to the primary key column of a database table.
-
All attributes that will be persisted should be declared private and have getXXX and setXXX methods defined in the JavaBean style.
-
A central feature of Hibernate, proxies, depends upon the persistent class being either non-final, or the implementation of an interface that declares all public methods.
-
All classes that do not extend or implement some specialized classes and interfaces required by the EJB framework.
POJO 名称用于强调给定对象是普通的 Java 对象,而不是特殊对象,特别是不是 Enterprise JavaBean。
The POJO name is used to emphasize that a given object is an ordinary Java Object, not a special object, and in particular not an Enterprise JavaBean.
Simple POJO Example
基于以上提到的几个规则,我们可以如下定义一个 POJO 类:
Based on the few rules mentioned above, we can define a POJO class as follows −
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 - Mapping Files
对象/关系映射通常在 XML 文档中定义。此映射文件指示 Hibernate — 如何将定义的类或类映射到数据库表?
An Object/relational mappings are usually defined in an XML document. This mapping file instructs Hibernate — how to map the defined class or classes to the database tables?
尽管许多 Hibernate 用户选择手动编写 XML,但存在许多工具可用于生成映射文档。对于高级 Hibernate 用户,这些工具包括 XDoclet, Middlegen 和 AndroMDA 。
Though many Hibernate users choose to write the XML by hand, but a number of tools exist to generate the mapping document. These include XDoclet, Middlegen and AndroMDA for the advanced Hibernate users.
让我们考虑我们之前定义的 POJO 类,其对象将在下一部分定义的表中持久化。
Let us consider our previously defined POJO class whose objects will persist in the table defined in next section.
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 表中的上述对象 −
There would be one table corresponding to each object you are willing to provide persistence. Consider above objects need to be stored and retrieved into the following RDBMS table −
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 如何将定义的类或类映射到数据库表。
Based on the two above entities, we can define following mapping file, which instructs Hibernate how to map the defined class or classes to the database tables.
<?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 中。
You should save the mapping document in a file with the format <classname>.hbm.xml. We saved our mapping document in the file Employee.hbm.xml.
让我们详细了解映射文件 -
Let us see understand a little detail about the mapping elements used in the mapping file −
-
The mapping document is an XML document having <hibernate-mapping> as the root element, which contains all the <class> elements.
-
The <class> elements are used to define specific mappings from a Java classes to the database tables. The Java class name is specified using the name attribute of the class element and the database table name is specified using the table attribute.
-
The <meta> element is optional element and can be used to create the class description.
-
The <id> element maps the unique ID attribute in class to the primary key of the database table. The name attribute of the id element refers to the property in the class and the column attribute refers to the column in the database table. The type attribute holds the hibernate mapping type, this mapping types will convert from Java to SQL data type.
-
The <generator> element within the id element is used to generate the primary key values automatically. The class attribute of the generator element is set to native to let hibernate pick up either identity, sequence, or hilo algorithm to create primary key depending upon the capabilities of the underlying database.
-
The <property> element is used to map a Java class property to a column in the database table. The name attribute of the element refers to the property in the class and the column attribute refers to the column in the database table. The type attribute holds the hibernate mapping type, this mapping types will convert from Java to SQL data type.
还有其他可在映射文档中使用的属性和元素,在我讨论其他 Hibernate 相关主题时将尝试涵盖尽可能多的内容。
There are other attributes and elements available, which will be used in a mapping document and I would try to cover as many as possible while discussing other Hibernate related topics.
Hibernate - Mapping Types
在准备 Hibernate 映射文档时,你会发现你将 Java 数据类型映射到 RDBMS 数据类型。在映射文件中声明和使用 types 并非 Java 数据类型;它们也不是 SQL 数据库类型。这些类型称为 Hibernate mapping types ,可以从 Java 转换为 SQL 数据类型,反之亦然。
When you prepare a Hibernate mapping document, you find that you map the Java data types into RDBMS data types. The types declared and used in the mapping files are not Java data types; they are not SQL database types either. These types are called Hibernate mapping types, which can translate from Java to SQL data types and vice versa.
本章列出了所有基本数据类型、日期和时间、大型对象以及其他各种内置映射类型。
This chapter lists down all the basic, date and time, large object, and various other builtin mapping types.
Primitive Types
Mapping type |
Java type |
ANSI SQL Type |
integer |
int or java.lang.Integer |
INTEGER |
long |
long or java.lang.Long |
BIGINT |
short |
short or java.lang.Short |
SMALLINT |
float |
float or java.lang.Float |
FLOAT |
double |
double or java.lang.Double |
DOUBLE |
big_decimal |
java.math.BigDecimal |
NUMERIC |
character |
java.lang.String |
CHAR(1) |
string |
java.lang.String |
VARCHAR |
byte |
byte or java.lang.Byte |
TINYINT |
boolean |
boolean or java.lang.Boolean |
BIT |
yes/no |
boolean or java.lang.Boolean |
CHAR(1) ('Y' or 'N') |
true/false |
boolean or java.lang.Boolean |
CHAR(1) ('T' or 'F') |
Date and Time Types
Mapping type |
Java type |
ANSI SQL Type |
date |
java.util.Date or java.sql.Date |
DATE |
time |
java.util.Date or java.sql.Time |
TIME |
timestamp |
java.util.Date or java.sql.Timestamp |
TIMESTAMP |
calendar |
java.util.Calendar |
TIMESTAMP |
calendar_date |
java.util.Calendar |
DATE |
Hibernate - Examples
现在我们举个例子,了解如何使用 Hibernate 在独立的应用程序中提供 Java 持久性。我们将介绍在使用 Hibernate 技术创建 Java 应用程序时涉及到的不同步骤。
Let us now take an example to understand how we can use Hibernate to provide Java persistence in a standalone application. We will go through the different steps involved in creating a Java Application using Hibernate technology.
Create POJO Classes
创建应用程序的第一步是构建 Java POJO 类或类,具体取决于将持久化到数据库的应用程序。考虑我们的 Employee 类,其方法 getXXX 和 setXXX 使其成为 JavaBeans 兼容类。
The first step in creating an application is to build the Java POJO class or classes, depending on the application that will be persisted to the database. Let us consider our Employee class with getXXX and setXXX methods to make it JavaBeans compliant class.
POJO(普通旧 Java 对象)是不扩展或不实现 EJB 框架分别要求的某些专门类和接口的 Java 对象。所有常规 Java 对象都是 POJO。
A POJO (Plain Old Java Object) is a Java object that doesn’t extend or implement some specialized classes and interfaces respectively required by the EJB framework. All normal Java objects are POJO.
在设计要由 Hibernate 持久化的类时,重要的是提供 JavaBeans 兼容代码以及一个属性,该属性可像 Employee 类中的 id 属性一样用作索引。
When you design a class to be persisted by Hibernate, it is important to provide JavaBeans compliant code as well as one attribute, which would work as index like id attribute in the Employee class.
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;
}
}
Create Database Tables
第二步是创建数据库中的表。希望提供持久性的每个对象都会对应一个表。考虑需要将以上对象存储和检索到以下 RDBMS 表中 -
Second step would be creating tables in your database. There would be one table corresponding to each object, you are willing to provide persistence. Consider above objects need to be stored and retrieved into the following RDBMS table −
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)
);
Create Mapping Configuration File
此步骤是创建映射文件,指示 Hibernate 如何将已定义的类或类映射到数据库表。
This step is to create a mapping file that instructs Hibernate how to map the defined class or classes to the database tables.
<?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 文件中。我们详细了解下映射文档 -
You should save the mapping document in a file with the format <classname>.hbm.xml. We saved our mapping document in the file Employee.hbm.xml. Let us see little detail about the mapping document −
-
The mapping document is an XML document having <hibernate-mapping> as the root element which contains all the <class> elements.
-
The <class> elements are used to define specific mappings from a Java classes to the database tables. The Java class name is specified using the name attribute of the class element and the database table name is specified using the table attribute.
-
The <meta> element is optional element and can be used to create the class description.
-
The <id> element maps the unique ID attribute in class to the primary key of the database table. The name attribute of the id element refers to the property in the class and the column attribute refers to the column in the database table. The type attribute holds the hibernate mapping type, this mapping types will convert from Java to SQL data type.
-
The <generator> element within the id element is used to generate the primary key values automatically. The class attribute of the generator element is set to native to let hibernate pick up either identity, sequence or hilo algorithm to create primary key depending upon the capabilities of the underlying database.
-
The <property> element is used to map a Java class property to a column in the database table. The name attribute of the element refers to the property in the class and the column attribute refers to the column in the database table. The type attribute holds the hibernate mapping type, this mapping types will convert from Java to SQL data type.
还有其他可在映射文档中使用的属性和元素,在我讨论其他 Hibernate 相关主题时将尝试涵盖尽可能多的内容。
There are other attributes and elements available, which will be used in a mapping document and I would try to cover as many as possible while discussing other Hibernate related topics.
Create Application Class
最后,我们将使用 main() 方法创建我们的应用程序类来运行应用程序。我们将使用此应用程序来保存一些 Employee 的记录,然后我们将对这些记录应用 CRUD 操作。
Finally, we will create our application class with the main() method to run the application. We will use this application to save few Employee’s records and then we will apply CRUD operations on those records.
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();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
Here are the steps to compile and run the above mentioned application. Make sure, you have set PATH and CLASSPATH appropriately before proceeding for the compilation and execution.
-
Create hibernate.cfg.xml configuration file as explained in configuration chapter.
-
Create Employee.hbm.xml mapping file as shown above.
-
Create Employee.java source file as shown above and compile it.
-
Create ManageEmployee.java source file as shown above and compile it.
-
Execute ManageEmployee binary to run the program.
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
You would get the following result, and records would be created in the EMPLOYEE table.
$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 表,它应包含以下记录 −
If you check your EMPLOYEE table, it should have the following records −
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 Mappings
到目前为止,我们已经看到了使用 hibernate 的非常基本的 O/R 映射,但还有三个非常重要的映射主题,我们必须详细了解。
So far, we have seen very basic O/R mapping using hibernate, but there are three most important mapping topics, which we have to learn in detail.
它们是:
These are −
-
Mapping of collections,
-
Mapping of associations between entity classes, and
-
Component Mappings.
Collections Mappings
如果实体或类具有针对特定变量的值集合,那么我们可以使用 java 中可用的任何一个集合接口来映射这些值。Hibernate 可以保留 java.util.Map, java.util.Set, java.util.SortedMap, java.util.SortedSet, java.util.List 和任何持久实体或值 array 的实例。
If an entity or class has collection of values for a particular variable, then we can map those values using any one of the collection interfaces available in java. Hibernate can persist instances of java.util.Map, java.util.Set, java.util.SortedMap, java.util.SortedSet, java.util.List, and any array of persistent entities or values.
Sr.No. |
Collection type & Mapping Description |
1 |
java.util.SetThis is mapped with a <set> element and initialized with java.util.HashSet |
2 |
java.util.SortedSetThis is mapped with a <set> element and initialized with java.util.TreeSet. The sort attribute can be set to either a comparator or natural ordering. |
3 |
java.util.ListThis is mapped with a <list> element and initialized with java.util.ArrayList |
4 |
java.util.CollectionThis is mapped with a <bag> or <ibag> element and initialized with java.util.ArrayList |
5 |
java.util.MapThis is mapped with a <map> element and initialized with java.util.HashMap |
6 |
java.util.SortedMapThis is mapped with a <map> element and initialized with java.util.TreeMap. The sort attribute can be set to either a comparator or natural ordering. |
Hibernate 使用 <primitive-array>(针对 Java 原始值类型)和 <array>(针对所有其他项)支持数组。但是,它们的使用频率很低,因此我不会在本教程中对它们进行讨论。
Arrays are supported by Hibernate with <primitive-array> for Java primitive value types and <array> for everything else. However, they are rarely used, so I am not going to discuss them in this tutorial.
如果您想映射 Hibernate 不直接支持的用户自定义集合接口,您需要告知 Hibernate 您自定义集合的语义,但要做到这一点并不容易,也不建议使用。
If you want to map a user defined collection interfaces, which is not directly supported by Hibernate, you need to tell Hibernate about the semantics of your custom collections, which is not very easy and not recommend to be used.
Association Mappings
实体类之间的关联映射以及表之间的关系是 ORM 的核心。以下是可以表示对象之间关系基数的四种方式。关联映射可以是单向的,也可以是双向的。
The mapping of associations between entity classes and the relationships between tables is the soul of ORM. Following are the four ways in which the cardinality of the relationship between the objects can be expressed. An association mapping can be unidirectional as well as bidirectional.
Sr.No. |
Mapping type & Description |
1 |
Many-to-OneMapping many-to-one relationship using Hibernate |
2 |
One-to-OneMapping one-to-one relationship using Hibernate |
3 |
One-to-ManyMapping one-to-many relationship using Hibernate |
4 |
Many-to-ManyMapping many-to-many relationship using Hibernate |
Component Mappings
一个实体类很可能可以具有对另一个类的引用作为成员变量。如果被引用类没有自己的生命周期,并且完全依赖于所有实体类生命周期,那么被引用类因此而成为 Component class 。
It is very much possible that an Entity class can have a reference to another class as a member variable. If the referred class does not have its own life cycle and completely depends on the life cycle of the owning entity class, then the referred class hence therefore is called as the Component class.
以类似方式映射集合组件也是可能的,就像映射常规集合,但配置差异很小。我们将详细了解这两个映射,并提供示例。
The mapping of Collection of Components is also possible in a similar way just as the mapping of regular Collections with minor configuration differences. We will see these two mappings in detail with examples.
Sr.No. |
Mapping type & Description |
1 |
Component MappingsMapping for a class having a reference to another class as a member variable. |
Hibernate - Annotations
到目前为止,你已经看到了 Hibernate 如何使用 XML 映射文件将数据从 POJO 转换为数据库表,反之亦然。Hibernate 注释是无需使用 XML 文件便可以定义映射的最新方式。你可以除了或替换 XML 映射元数据以外,还可以使用注释。
So far you have seen how Hibernate uses XML mapping file for the transformation of data from POJO to database tables and vice versa. Hibernate annotations are the newest way to define mappings without the use of XML file. You can use annotations in addition to or as a replacement of XML mapping metadata.
Hibernate 注释是以强大方式为对象和关系表映射提供元数据的方法。所有元数据与代码一起打包到 POJO Java 文件中,这有助于用户在开发期间同时了解表结构和 POJO。
Hibernate Annotations is the powerful way to provide the metadata for the Object and Relational Table mapping. All the metadata is clubbed into the POJO java file along with the code, this helps the user to understand the table structure and POJO simultaneously during the development.
如果你打算让应用程序可移植到其他 EJB 3 兼容的 ORM 应用程序,则必须使用注释来表示映射信息,但如果你仍然想要更大的灵活性,则应该选择基于 XML 的映射。
If you going to make your application portable to other EJB 3 compliant ORM applications, you must use annotations to represent the mapping information, but still if you want greater flexibility, then you should go with XML-based mappings.
Environment Setup for Hibernate Annotation
首先,你必须确保正在使用 JDK 5.0,否则需要将 JDK 升级到 JDK 5.0 以利用对注释的本机支持。
First of all you would have to make sure that you are using JDK 5.0 otherwise you need to upgrade your JDK to JDK 5.0 to take advantage of the native support for annotations.
其次,你需要安装 Hibernate 3.x 注释发行包,该包可从 Sourceforge 获得:( Download Hibernate Annotation ),并将 hibernate-annotations.jar, lib/hibernate-comons-annotations.jar 和 lib/ejb3-persistence.jar 从 Hibernate 注释发行包复制到 CLASSPATH。
Second, you will need to install the Hibernate 3.x annotations distribution package, available from the sourceforge: (Download Hibernate Annotation) and copy hibernate-annotations.jar, lib/hibernate-comons-annotations.jar and lib/ejb3-persistence.jar from the Hibernate Annotations distribution to your CLASSPATH.
Annotated Class Example
如上所述,在使用 Hibernate 注释时,所有元数据与代码一起打包到 POJO Java 文件中,这有助于用户在开发期间同时了解表结构和 POJO。
As I mentioned above while working with Hibernate Annotation, all the metadata is clubbed into the POJO java file along with the code, this helps the user to understand the table structure and POJO simultaneously during the development.
考虑我们准备使用以下 EMPLOYEE 表来存储我们的对象 -
Consider we are going to use the following EMPLOYEE table to store our objects −
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 表中 -
Following is the mapping of Employee class with annotations to map objects with the defined EMPLOYEE table −
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 方法上。
Hibernate detects that the @Id annotation is on a field and assumes that it should access properties of an object directly through fields at runtime. If you placed the @Id annotation on the getId() method, you would enable access to properties through getter and setter methods by default. Hence, all other annotations are also placed on either fields or getter methods, following the selected strategy.
以下部分将解释在上述类中使用的注释。
Following section will explain the annotations used in the above class.
@Entity Annotation
EJB 3 标准注释包含在 javax.persistence 包中,因此我们将此包作为第一步导入。其次,我们在 Employee 类中使用了 @Entity 注释,它将此类标记为实体 bean,因此它必定有一个无参数构造函数,并且至少具有受保护的作用域可见性。
The EJB 3 standard annotations are contained in the javax.persistence package, so we import this package as the first step. Second, we used the @Entity annotation to the Employee class, which marks this class as an entity bean, so it must have a no-argument constructor that is visible with at least protected scope.
@Table Annotation
@Table 注释允许您指定表中的详细信息,这些信息将用于保留数据库中的实体。
The @Table annotation allows you to specify the details of the table that will be used to persist the entity in the database.
@Table 注释提供了四个属性,允许您覆盖表名称、其目录以及其架构,并强制执行表中列的唯一约束。目前,我们仅使用表名称,即 EMPLOYEE。
The @Table annotation provides four attributes, allowing you to override the name of the table, its catalogue, and its schema, and enforce unique constraints on columns in the table. For now, we are using just table name, which is EMPLOYEE.
@Id and @GeneratedValue Annotations
每个实体 bean 都将有一个主键,您使用 @Id 注释在类上为此进行注释。主键可以是单个字段或多个字段的组合,具体取决于您的表结构。
Each entity bean will have a primary key, which you annotate on the class with the @Id annotation. The primary key can be a single field or a combination of multiple fields depending on your table structure.
默认情况下,@Id 注释将自动确定要使用的最合适的主键生成策略,但是您可以通过应用 @GeneratedValue 注释来覆盖该策略,该注释采用两个参数 strategy 和 generator ,我将不会在这里讨论它们,因此让我们仅使用默认的主键生成策略。让 Hibernate 确定要使用哪种生成器类型,可以使您的代码在不同的数据库之间具有可移植性。
By default, the @Id annotation will automatically determine the most appropriate primary key generation strategy to be used but you can override this by applying the @GeneratedValue annotation, which takes two parameters strategy and generator that I’m not going to discuss here, so let us use only the default key generation strategy. Letting Hibernate determine which generator type to use makes your code portable between different databases.
@Column Annotation
@Column 注释用于指定字段或属性将映射到的列的详细信息。您可以将列注释与以下最常用的属性一起使用 -
The @Column annotation is used to specify the details of the column to which a field or property will be mapped. You can use column annotation with the following most commonly used attributes −
-
name attribute permits the name of the column to be explicitly specified.
-
length attribute permits the size of the column used to map a value particularly for a String value.
-
nullable attribute permits the column to be marked NOT NULL when the schema is generated.
-
unique attribute permits the column to be marked as containing only unique values.
Create Application Class
最后,我们将使用 main() 方法创建我们的应用程序类来运行应用程序。我们将使用此应用程序来保存一些 Employee 的记录,然后我们将对这些记录应用 CRUD 操作。
Finally, we will create our application class with the main() method to run the application. We will use this application to save few Employee’s records and then we will apply CRUD operations on those records.
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();
}
}
}
Database Configuration
现在,让我们创建 hibernate.cfg.xml 配置文件来定义与数据库相关的参数。
Now let us create hibernate.cfg.xml configuration file to define database related parameters.
<?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://localhost/test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
cohondob
</property>
</session-factory>
</hibernate-configuration>
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
Here are the steps to compile and run the above mentioned application. Make sure, you have set PATH and CLASSPATH appropriately before proceeding for the compilation and execution.
-
Delete Employee.hbm.xml mapping file from the path.
-
Create Employee.java source file as shown above and compile it.
-
Create ManageEmployee.java source file as shown above and compile it.
-
Execute ManageEmployee binary to run the program.
您将获得以下结果,并且记录将被创建到 EMPLOYEE 表中。
You would get the following result, and records would be created in EMPLOYEE table.
$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 表,它应包含以下记录 −
If you check your EMPLOYEE table, it should have the following records −
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 - Query Language
Hibernate 查询语言 (HQL) 是一种面向对象查询语言,类似于 SQL,但 HQL 不操作表和列,而是处理持久对象及其属性。HQL 查询由 Hibernate 转换为常规 SQL 查询,进而对数据库执行操作。
Hibernate Query Language (HQL) is an object-oriented query language, similar to SQL, but instead of operating on tables and columns, HQL works with persistent objects and their properties. HQL queries are translated by Hibernate into conventional SQL queries, which in turns perform action on database.
虽然可以使用本机 SQL 直接在 Hibernate 中使用 SQL 语句,但我建议尽可能使用 HQL 来避免数据库移植麻烦,并利用 Hibernate 的 SQL 生成和缓存策略。
Although you can use SQL statements directly with Hibernate using Native SQL, but I would recommend to use HQL whenever possible to avoid database portability hassles, and to take advantage of Hibernate’s SQL generation and caching strategies.
SELECT、FROM 和 WHERE 等关键字不区分大小写,但 HQL 中的属性(如表和列名)区分大小写。
Keywords like SELECT, FROM, and WHERE, etc., are not case sensitive, but properties like table and column names are case sensitive in HQL.
FROM Clause
如果要将一个完整的持久对象加载到内存中,可以使用 FROM 子句。以下是使用 FROM 子句的简单语法:
You will use FROM clause if you want to load a complete persistent objects into memory. Following is the simple syntax of using FROM clause −
String hql = "FROM Employee";
Query query = session.createQuery(hql);
List results = query.list();
如果需要在 HQL 中完全限定一个类名,只需按如下所示指定包名和类名:
If you need to fully qualify a class name in HQL, just specify the package and class name as follows −
String hql = "FROM com.hibernatebook.criteria.Employee";
Query query = session.createQuery(hql);
List results = query.list();
AS Clause
AS 子句可用于为 HQL 查询中的类分配别名,特别是在有长查询时。例如,我们之前的简单示例将如下所示:
The AS clause can be used to assign aliases to the classes in your HQL queries, especially when you have the long queries. For instance, our previous simple example would be the following −
String hql = "FROM Employee AS E";
Query query = session.createQuery(hql);
List results = query.list();
AS 关键字是可选的,还可以按如下所示直接在类名后面指定别名:
The AS keyword is optional and you can also specify the alias directly after the class name, as follows −
String hql = "FROM Employee E";
Query query = session.createQuery(hql);
List results = query.list();
SELECT Clause
SELECT 子句比 from 子句可对结果集提供更多控制。如果想要获取对象的部分属性而不是整个对象,可以使用 SELECT 子句。以下是使用 SELECT 子句获取 Employee 对象的 first_name 字段的简单语法:
The SELECT clause provides more control over the result set then the from clause. If you want to obtain few properties of objects instead of the complete object, use the SELECT clause. Following is the simple syntax of using SELECT clause to get just first_name field of the Employee object −
String hql = "SELECT E.firstName FROM Employee E";
Query query = session.createQuery(hql);
List results = query.list();
值得注意的是, Employee.firstName 是 Employee 对象的属性,而不是 EMPLOYEE 表的字段。
It is notable here that Employee.firstName is a property of Employee object rather than a field of the EMPLOYEE table.
WHERE Clause
如果想要缩小从存储中返回的特定对象范围,可以使用 WHERE 子句。以下是使用 WHERE 子句的简单语法:
If you want to narrow the specific objects that are returned from storage, you use the WHERE clause. Following is the simple syntax of using WHERE clause −
String hql = "FROM Employee E WHERE E.id = 10";
Query query = session.createQuery(hql);
List results = query.list();
ORDER BY Clause
要对 HQL 查询的结果进行排序,需要使用 ORDER BY 子句。可以按结果集中对象上的任何属性对结果进行升序 (ASC) 或降序 (DESC) 排序。以下是使用 ORDER BY 子句的简单语法:
To sort your HQL query’s results, you will need to use the ORDER BY clause. You can order the results by any property on the objects in the result set either ascending (ASC) or descending (DESC). Following is the simple syntax of using ORDER BY clause −
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 子句的末尾:
If you wanted to sort by more than one property, you would just add the additional properties to the end of the order by clause, separated by commas as follows −
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 Clause
此子句允许 Hibernate 从数据库中提取信息,并根据属性值对信息进行分组,并且通常使用结果包含聚合值。以下是使用 GROUP BY 子句的简单语法:
This clause lets Hibernate pull information from the database and group it based on a value of an attribute and, typically, use the result to include an aggregate value. Following is the simple syntax of using GROUP BY clause −
String hql = "SELECT SUM(E.salary), E.firtName FROM Employee E " +
"GROUP BY E.firstName";
Query query = session.createQuery(hql);
List results = query.list();
Using Named Parameters
Hibernate 在其 HQL 查询中支持命名参数。这可以轻松编写接受用户输入的 HQL 查询,并且不必抵御 SQL 注入攻击。以下是使用命名参数的简单语法:
Hibernate supports named parameters in its HQL queries. This makes writing HQL queries that accept input from the user easy and you do not have to defend against SQL injection attacks. Following is the simple syntax of using named parameters −
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 Clause
批量更新在 Hibernate 3 中是 HQL 的新增内容,并且 Hibernate 3 中的删除工作方式与 Hibernate 2 中不同。Query 界面对象现在包含一个名为 executeUpdate() 的方法,用于执行 HQL UPDATE 或 DELETE 语句。
Bulk updates are new to HQL with Hibernate 3, and delete work differently in Hibernate 3 than they did in Hibernate 2. The Query interface now contains a method called executeUpdate() for executing HQL UPDATE or DELETE statements.
UPDATE 子句可用于更新一个或多个对象的多个属性。以下是使用 UPDATE 子句的简单语法:
The UPDATE clause can be used to update one or more properties of an one or more objects. Following is the simple syntax of using UPDATE clause −
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 Clause
DELETE 子句可用于删除一个或多个对象。以下是使用 DELETE 子句的简单语法:
The DELETE clause can be used to delete one or more objects. Following is the simple syntax of using DELETE clause −
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 Clause
HQL 支持 INSERT INTO 子句,仅在可以将记录从一个对象插入到另一个对象时使用。以下是使用 INSERT INTO 子句的简单语法:
HQL supports INSERT INTO clause only where records can be inserted from one object to another object. Following is the simple syntax of using INSERT INTO clause −
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);
Aggregate Methods
HQL 支持一系列聚合方法,与 SQL 类似。它们在 HQL 中的工作方式与在 SQL 中相同,以下是可用函数的列表:
HQL supports a range of aggregate methods, similar to SQL. They work the same way in HQL as in SQL and following is the list of the available functions −
Sr.No. |
Functions & Description |
1 |
avg(property name) The average of a property’s value |
2 |
count(property name or *) The number of times a property occurs in the results |
3 |
max(property name) The maximum value of the property values |
4 |
min(property name) The minimum value of the property values |
5 |
sum(property name) The sum total of the property values |
distinct 关键词仅对行集中唯一的值进行计数。以下查询将仅返回唯一计数 −
The distinct keyword only counts the unique values in the row set. The following query will return only unique count −
String hql = "SELECT count(distinct E.firstName) FROM Employee E";
Query query = session.createQuery(hql);
List results = query.list();
Pagination using Query
查询接口有两个方法用于分页。
There are two methods of the Query interface for pagination.
Sr.No. |
Method & Description |
1 |
Query setFirstResult(int startPosition) This method takes an integer that represents the first row in your result set, starting with row 0. |
2 |
Query setMaxResults(int maxResult) This method tells Hibernate to retrieve a fixed number maxResults of objects. |
将以上两个方法结合使用,我们可以在 Web 或 Swing 应用程序中构建一个分页组件。以下就是其示例,您可以对其进行扩展以一次获取 10 行 −
Using above two methods together, we can construct a paging component in our web or Swing application. Following is the example, which you can extend to fetch 10 rows at a time −
String hql = "FROM Employee";
Query query = session.createQuery(hql);
query.setFirstResult(1);
query.setMaxResults(10);
List results = query.list();
Hibernate - Criteria Queries
Hibernate 提供了处理对象以及反过来提供 RDBMS 表中数据的替代方法。其中一种方法是 Criteria API,它允许您以编程方式构建一个 criteria 查询对象,您可以在其中应用过滤规则和逻辑条件。
Hibernate provides alternate ways of manipulating objects and in turn data available in RDBMS tables. One of the methods is Criteria API, which allows you to build up a criteria query object programmatically where you can apply filtration rules and logical conditions.
Hibernate Session 接口提供 createCriteria() 方法,它可用于创建 Criteria 对象,当您的应用程序执行 criteria 查询时,该对象会返回持久对象类的实例。
The Hibernate Session interface provides createCriteria() method, which can be used to create a Criteria object that returns instances of the persistence object’s class when your application executes a criteria query.
以下是 criteria 查询的最简单示例,它将简单地返回与 Employee 类对应的每个对象。
Following is the simplest example of a criteria query is one, which will simply return every object that corresponds to the Employee class.
Criteria cr = session.createCriteria(Employee.class);
List results = cr.list();
Restrictions with Criteria
您可以使用 Criteria 对象提供的 add() 方法为 criteria 查询添加限制。以下是添加一个限制的示例,以返回工资等于 2000 的记录 −
You can use add() method available for Criteria object to add restriction for a criteria query. Following is the example to add a restriction to return the records with salary is equal to 2000 −
Criteria cr = session.createCriteria(Employee.class);
cr.add(Restrictions.eq("salary", 2000));
List results = cr.list();
以下是涵盖不同场景的更多示例,可根据要求使用 −
Following are the few more examples covering different scenarios and can be used as per the requirement −
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 条件,如下所示 −
You can create AND or OR conditions using LogicalExpression restrictions as follows −
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 直接一起使用,如前一个教程中所述。
Though all the above conditions can be used directly with HQL as explained in previous tutorial.
Pagination Using Criteria
查询接口有两个方法用于分页。
There are two methods of the Criteria interface for pagination.
Sr.No. |
Method & Description |
1 |
public Criteria setFirstResult(int firstResult) This method takes an integer that represents the first row in your result set, starting with row 0. |
2 |
public Criteria setMaxResults(int maxResults) This method tells Hibernate to retrieve a fixed number maxResults of objects. |
将以上两个方法结合使用,我们可以在 Web 或 Swing 应用程序中构建一个分页组件。以下就是其示例,您可以对其进行扩展以一次获取 10 行 −
Using above two methods together, we can construct a paging component in our web or Swing application. Following is the example, which you can extend to fetch 10 rows at a time −
Criteria cr = session.createCriteria(Employee.class);
cr.setFirstResult(1);
cr.setMaxResults(10);
List results = cr.list();
Sorting the Results
Criteria API 会提供 org.hibernate.criterion.Order 类按照对象的某个属性对结果集进行升序或降序排列。示例展示了如何使用 Order
类对结果集进行排序——
The Criteria API provides the org.hibernate.criterion.Order class to sort your result set in either ascending or descending order, according to one of your object’s properties. This example demonstrates how you would use the Order class to sort the result set −
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();
Projections & Aggregations
Criteria API 会提供 org.hibernate.criterion.Projections 类,该类可以用于获取某个属性值的平均值、最大值或最小值。Projections
类类似于 Restrictions
类,因为它会提供几个用于获取 Projection 实例的静态工厂方法。
The Criteria API provides the org.hibernate.criterion.Projections class, which can be used to get average, maximum, or minimum of the property values. The Projections class is similar to the Restrictions class, in that it provides several static factory methods for obtaining Projection instances.
接下来是几个涉及不同场景的示例,并且能根据需求来使用——
Following are the few examples covering different scenarios and can be used as per requirement −
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 Queries Example
考虑以下 POJO 类——
Consider the following POJO class −
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 对象——
Let us create the following EMPLOYEE table to store Employee objects −
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)
);
下面是映射文件。
Following will be the mapping file.
<?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 查询——
Finally, we will create our application class with the main() method to run the application where we will use Criteria queries −
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();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
Here are the steps to compile and run the above mentioned application. Make sure, you have set PATH and CLASSPATH appropriately before proceeding for the compilation and execution.
-
Create hibernate.cfg.xml configuration file as explained in configuration chapter.
-
Create Employee.hbm.xml mapping file as shown above.
-
Create Employee.java source file as shown above and compile it.
-
Create ManageEmployee.java source file as shown above and compile it.
-
Execute ManageEmployee binary to run the program.
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
You would get the following result, and records would be created in the EMPLOYEE table.
$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 表,它应当有以下记录——
If you check your EMPLOYEE table, it should have the following records−
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 - Native SQL
如果你希望利用数据库的特定功能(例如 Oracle 中的查询提示或 CONNECT 关键字),你可以使用原生 SQL 来表达数据库查询。Hibernate 3.x 让你可以指定手工编写的 SQL(包括存储过程)用于所有创建、更新、删除和加载操作。
You can use native SQL to express database queries if you want to utilize database-specific features such as query hints or the CONNECT keyword in Oracle. Hibernate 3.x allows you to specify handwritten SQL, including stored procedures, for all create, update, delete, and load operations.
你的应用程序会利用 Session 接口的 createSQLQuery() 方法从会话中创建一个原生 SQL 查询——
Your application will create a native SQL query from the session with the createSQLQuery() method on the Session interface −
public SQLQuery createSQLQuery(String sqlString) throws HibernateException
在你向 createSQLQuery()
方法传递包含 SQL 查询的字符串之后,你可以使用 addEntity()
、addJoin()
和 addScalar()
方法分别将 SQL 结果与现有的 Hibernate 实体、联接或标量结果关联起来。
After you pass a string containing the SQL query to the createSQLQuery() method, you can associate the SQL result with either an existing Hibernate entity, a join, or a scalar result using addEntity(), addJoin(), and addScalar() methods respectively.
Scalar Queries
最基本的 SQL 查询是从一张或多张表中获取标量(值)的列表。以下是使用原生 SQL 查询标量值的语法——
The most basic SQL query is to get a list of scalars (values) from one or more tables. Following is the syntax for using native SQL for scalar values −
String sql = "SELECT first_name, salary FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
List results = query.list();
Entity Queries
以上所有查询都涉及返回标量值,基本上从结果集中返回“原始”值。以下是使用 addEntity()
从原生 SQL 查询中整体获取实体对象的语法。
The above queries were all about returning scalar values, basically returning the "raw" values from the result set. Following is the syntax to get entity objects as a whole from a native sql query via addEntity().
String sql = "SELECT * FROM EMPLOYEE";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Employee.class);
List results = query.list();
Named SQL Queries
以下是使用 addEntity()
和命名 SQL 查询从原生 SQL 查询中获取实体对象的语法。
Following is the syntax to get entity objects from a native sql query via addEntity() and using named SQL query.
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();
Native SQL Example
考虑以下 POJO 类——
Consider the following POJO class −
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 对象——
Let us create the following EMPLOYEE table to store Employee objects −
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)
);
以下是映射文件——
Following will be mapping file −
<?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()
方法创建我们的应用程序类以运行应用程序,在其中我们将使用 Native SQL 查询——
Finally, we will create our application class with the main() method to run the application where we will use Native SQL queries −
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();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
Here are the steps to compile and run the above mentioned application. Make sure, you have set PATH and CLASSPATH appropriately before proceeding for the compilation and execution.
-
Create hibernate.cfg.xml configuration file as explained in configuration chapter.
-
Create Employee.hbm.xml mapping file as shown above.
-
Create Employee.java source file as shown above and compile it.
-
Create ManageEmployee.java source file as shown above and compile it.
-
Execute ManageEmployee binary to run the program.
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
You would get the following result, and records would be created in the EMPLOYEE table.
$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 表,它应包含以下记录 −
If you check your EMPLOYEE table, it should have the following records −
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 - Caching
高速缓存是一种增强系统性能的机制。它是一种位于应用程序和数据库之间的缓冲存储器。高速缓存存储最近使用过的数据项,尽可能减少数据库命中次数。
Caching is a mechanism to enhance the performance of a system. It is a buffer memorythat lies between the application and the database. Cache memory stores recently used data items in order to reduce the number of database hits as much as possible.
高速缓存对 Hibernate 也很重要。它利用了如下解释的多级高速缓存方案——
Caching is important to Hibernate as well. It utilizes a multilevel caching scheme as explained below −
First-level Cache
一级高速缓存即 Session 高速缓存,这是一个所有请求都必须经过的强制高速缓存。Session 对象在将对象提交到数据库之前,在自己的能力范围内保留该对象。
The first-level cache is the Session cache and is a mandatory cache through which all requests must pass. The Session object keeps an object under its own power before committing it to the database.
如果对对象发出多次更新,Hibernate 尝试尽可能长时间延迟更新,以减少发出的更新 SQL 语句的次数。如果关闭 Session,所有被缓存的对象都会丢失并且被保留或更新到数据库中。
If you issue multiple updates to an object, Hibernate tries to delay doing the update as long as possible to reduce the number of update SQL statements issued. If you close the session, all the objects being cached are lost and either persisted or updated in the database.
Second-level Cache
二级高速缓存是一个可选的高速缓存,而且在尝试定位二级高速缓存中的对象之前,通常会咨询一级高速缓存。二级高速缓存可以在每个类和每个集合的基础上配置,并且主要负责跨 Session 缓存对象。
Second level cache is an optional cache and first-level cache will always be consulted before any attempt is made to locate an object in the second-level cache. The second level cache can be configured on a per-class and per-collection basis and mainly responsible for caching objects across sessions.
Hibernate 可以使用任何第三方缓存。提供了一个 org.hibernate.cache.CacheProvider 接口,必须实现该接口才能为 Hibernate 提供一个对缓存实现的句柄。
Any third-party cache can be used with Hibernate. An org.hibernate.cache.CacheProvider interface is provided, which must be implemented to provide Hibernate with a handle to the cache implementation.
Query-level Cache
Hibernate 还为查询结果集实现了与二级缓存紧密集成在一起的缓存。
Hibernate also implements a cache for query resultsets that integrates closely with the second-level cache.
这是一个可选特性,需要两个附加的物理缓存区域来保存缓存的查询结果以及表上次更新的时间戳。这仅适用于频繁使用相同参数运行的查询。
This is an optional feature and requires two additional physical cache regions that hold the cached query results and the timestamps when a table was last updated. This is only useful for queries that are run frequently with the same parameters.
The Second Level Cache
Hibernate 默认使用一级缓存,并且您无需执行任何操作即可使用一级缓存。我们直接进入可选的二级缓存。并不是所有类都受益于缓存,因此能够禁用二级缓存非常重要。
Hibernate uses first-level cache by default and you have nothing to do to use first-level cache. Let’s go straight to the optional second-level cache. Not all classes benefit from caching, so it’s important to be able to disable the second-level cache.
Hibernate 二级缓存分两步设置。首先,您必须决定使用哪种并发策略。然后,使用缓存提供程序配置缓存到期时间和物理缓存属性。
The Hibernate second-level cache is set up in two steps. First, you have to decide which concurrency strategy to use. After that, you configure cache expiration and physical cache attributes using the cache provider.
Concurrency Strategies
并发策略是一个调节器,它负责将数据项存储在缓存中并从缓存中检索数据项。如果您要启用二级缓存,则需要为每个持久类和集合决定使用哪种缓存并发性策略。
A concurrency strategy is a mediator, which is responsible for storing items of data in the cache and retrieving them from the cache. If you are going to enable a second-level cache, you will have to decide, for each persistent class and collection, which cache concurrency strategy to use.
-
Transactional − Use this strategy for read-mostly data where it is critical to prevent stale data in concurrent transactions, in the rare case of an update.
-
Read-write − Again use this strategy for read-mostly data where it is critical to prevent stale data in concurrent transactions, in the rare case of an update.
-
Nonstrict-read-write − This strategy makes no guarantee of consistency between the cache and the database. Use this strategy if data hardly ever changes and a small likelihood of stale data is not of critical concern.
-
Read-only − A concurrency strategy suitable for data, which never changes. Use it for reference data only.
如果我们准备为 Employee 类使用二级缓存,那么我们添加必需的映射元素来告诉 Hibernate 使用读写策略来缓存 Employee 实例。
If we are going to use second-level caching for our Employee class, let us add the mapping element required to tell Hibernate to cache Employee instances using read-write strategy.
<?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 为定义的缓存使用读写并发策略。
The usage="read-write" attribute tells Hibernate to use a read-write concurrency strategy for the defined cache.
Cache Provider
在考虑了并发策略后,您的下一步是用您的缓存候选类来选择缓存提供程序。Hibernate 强制您为整个应用程序选择单一缓存提供程序。
Your next step after considering the concurrency strategies, you will use your cache candidate classes to pick a cache provider. Hibernate forces you to choose a single cache provider for the whole application.
Sr.No. |
Cache Name & Description |
1 |
EHCache It can cache in memory or on disk and clustered caching and it supports the optional Hibernate query result cache. |
2 |
OSCache Supports caching to memory and disk in a single JVM with a rich set of expiration policies and query cache support. |
3 |
warmCache A cluster cache based on JGroups. It uses clustered invalidation, but doesn’t support the Hibernate query cache. |
4 |
JBoss Cache A fully transactional replicated clustered cache also based on the JGroups multicast library. It supports replication or invalidation, synchronous or asynchronous communication, and optimistic and pessimistic locking. The Hibernate query cache is supported. |
并非每个缓存提供程序都与每个并发策略兼容。以下兼容性矩阵将帮助您选择合适的组合。
Every cache provider is not compatible with every concurrency strategy. The following compatibility matrix will help you choose an appropriate combination.
Strategy/Provider |
Read-only |
Nonstrictread-write |
Read-write |
Transactional |
EHCache |
X |
X |
X |
|
OSCache |
X |
X |
X |
|
SwarmCache |
X |
X |
||
JBoss Cache |
X |
X |
您将在 hibernate.cfg.xml 配置文件中指定一个缓存提供程序。我们选择 EHCache 作为我们的二级缓存提供程序 −
You will specify a cache provider in hibernate.cfg.xml configuration file. We choose EHCache as our second-level cache provider −
<?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://localhost/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 ,它应该在应用程序的类路径中。Employee 类的 ehcache.xml 中的缓存配置可能如下所示:
Now, you need to specify the properties of the cache regions. EHCache has its own configuration file, ehcache.xml, which should be in the CLASSPATH of the application. A cache configuration in ehcache.xml for the Employee class may look like this −
<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 时,都会命中二级缓存。
That’s it, now we have second-level caching enabled for the Employee class and Hibernate, now hits the second-level cache whenever you navigate to an Employee or when you load an Employee by identifier.
你应分析你的所有类,并为每个类选择适当的缓存策略。有时,二级缓存可能会降低应用程序的性能。因此,建议先对你的应用程序进行基准测试,在未启用缓存的情况下进行,然后启用适合你的缓存,并检查性能。如果缓存未提高系统性能,则无需启用任何类型的缓存。
You should analyze your all the classes and choose appropriate caching strategy for each of the classes. Sometime, second-level caching may downgrade the performance of the application. So, it is recommended to benchmark your application first, without enabling caching and later on enable your well suited caching and check the performance. If caching is not improving system performance, then there is no point in enabling any type of caching.
The Query-level Cache
要使用查询缓存,你必须首先使用配置文件中的 hibernate.cache.use_query_cache="true" 属性激活它。通过将此属性设置为 true,使 Hibernate 在内存中创建必需的缓存以保存查询和标识符集。
To use the query cache, you must first activate it using the hibernate.cache.use_query_cache="true" property in the configuration file. By setting this property to true, you make Hibernate create the necessary caches in memory to hold the query and identifier sets.
然后,要使用查询缓存,使用 Query 类的 setCacheable(Boolean) 方法。例如:
Next, to use the query cache, you use the setCacheable(Boolean) method of the Query class. For example −
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
List users = query.list();
SessionFactory.closeSession();
Hibernate 还通过缓存区域的概念支持非常细粒度的缓存支持。缓存区域是缓存的一部分,它被赋予了一个名称。
Hibernate also supports very fine-grained cache support through the concept of a cache region. A cache region is part of the cache that’s given a name.
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
query.setCacheRegion("employee");
List users = query.list();
SessionFactory.closeSession();
此代码使用该方法告知 Hibernate 将查询存储在缓存的 employee 区域中并查找该查询。
This code uses the method to tell Hibernate to store and look for the query in the employee area of the cache.
Hibernate - Batch Processing
设想一下这种情况:你需要使用 Hibernate 上传大量记录到你的数据库。以下是使用 Hibernate 实现此目标的代码段:
Consider a situation when you need to upload a large number of records into your database using Hibernate. Following is the code snippet to achieve this using 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 而崩溃。如果你将 batch processing 与 Hibernate 一起使用,则可以解决此问题。
By default, Hibernate will cache all the persisted objects in the session-level cache and ultimately your application would fall over with an OutOfMemoryException somewhere around the 50,000th row. You can resolve this problem, if you are using batch processing with Hibernate.
要使用批处理功能,首先将 hibernate.jdbc.batch_size 设置为批量大小,以 20 或 50 的数字作为对象大小。这将告知 Hibernate 容器将每 X 行插入为一批。要在你的代码中实现此功能,我们需要进行如下小修改:
To use the batch processing feature, first set hibernate.jdbc.batch_size as batch size to a number either at 20 or 50 depending on object size. This will tell the hibernate container that every X rows to be inserted as batch. To implement this in your code, we would need to do little modification as follows −
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 操作,则可以使用以下代码来实现:
Above code will work fine for the INSERT operation, but if you are willing to make UPDATE operation, then you can achieve using the following code −
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();
Batch Processing Example
让我们修改配置文件以添加 hibernate.jdbc.batch_size 属性:
Let us modify the configuration file to add hibernate.jdbc.batch_size property −
<?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://localhost/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 类:
Consider the following POJO Employee class −
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 对象:
Let us create the following EMPLOYEE table to store the Employee objects −
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 表映射的映射文件:
Following will be the mapping file to map the Employee objects with EMPLOYEE table −
<?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 继续将这些记录写入数据库,而不是将它们缓存在内存中。
Finally, we will create our application class with the main() method to run the application where we will use flush() and clear() methods available with Session object so that Hibernate keeps writing these records into the database instead of caching them in the memory.
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 ;
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
Here are the steps to compile and run the above mentioned application. Make sure, you have set PATH and CLASSPATH appropriately before proceeding for the compilation and execution.
-
Create hibernate.cfg.xml configuration file as explained above.
-
Create Employee.hbm.xml mapping file as shown above.
-
Create Employee.java source file as shown above and compile it.
-
Create ManageEmployee.java source file as shown above and compile it.
-
Execute ManageEmployee binary to run the program, which will create 100000 records in EMPLOYEE table.
Hibernate - Interceptors
您已经了解到,在 Hibernate 中,将创建一个对象并将其持久化。一旦对象发生更改,就必须将其保存回数据库。该过程会持续到下次需要该对象为止,该对象将从持久化储存中加载。
As you have learnt that in Hibernate, an object will be created and persisted. Once the object has been changed, it must be saved back to the database. This process continues until the next time the object is needed, and it will be loaded from the persistent store.
对象在其生命周期中会经过不同的阶段,而 Interceptor Interface 提供可于不同阶段调用以执行一些所需任务的方法。这些方法是会话到应用程序的回调,它允许应用程序在持久对象被保存、更新、删除或加载之前检查和/或操作其属性。以下列出拦截器接口中提供的所有方法 −
Thus an object passes through different stages in its life cycle and Interceptor Interface provides methods, which can be called at different stages to perform some required tasks. These methods are callbacks from the session to the application, allowing the application to inspect and/or manipulate properties of a persistent object before it is saved, updated, deleted or loaded. Following is the list of all the methods available within the Interceptor interface −
Sr.No. |
Method & Description |
1 |
findDirty() This method is be called when the flush() method is called on a Session object. |
2 |
instantiate() This method is called when a persisted class is instantiated. |
3 |
isUnsaved() This method is called when an object is passed to the saveOrUpdate() method/ |
4 |
onDelete() This method is called before an object is deleted. |
5 |
onFlushDirty() This method is called when Hibernate detects that an object is dirty (i.e. have been changed) during a flush i.e. update operation. |
6 |
onLoad() This method is called before an object is initialized. |
7 |
onSave() This method is called before an object is saved. |
8 |
postFlush() This method is called after a flush has occurred and an object has been updated in memory. |
9 |
preFlush() This method is called before a flush. |
Hibernate Interceptor 让我们全面控制对象在应用程序和数据库中的呈现方式。
Hibernate Interceptor gives us total control over how an object will look to both the application and the database.
How to Use Interceptors?
要构建拦截器,你可以直接实现 Interceptor 类或扩展 EmptyInterceptor 类。以下是使用 Hibernate Interceptor 功能的简单步骤。
To build an interceptor, you can either implement Interceptor class directly or extend EmptyInterceptor class. Following will be the simple steps to use Hibernate Interceptor functionality.
Create Interceptors
在我们的示例中,我们将扩展 EmptyInterceptor,在 Employee 对象被创建和更新时,Interceptor 的方法将自动调用。你可以根据要求实现更多方法。
We will extend EmptyInterceptor in our example where Interceptor’s method will be called automatically when Employee object is created and updated. You can implement more methods as per your requirements.
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");
}
}
Create POJO Classes
现在,让我们稍微修改第一个示例,在示例中我们将使用 EMPLOYEE 表和 Employee 类 −
Now, let us modify a little bit our first example where we used EMPLOYEE table and Employee class to play with −
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;
}
}
Create Database Tables
第二步是在你的数据库中创建表。将有一个表对应一个对象,你愿意提供持久性。考虑上面解释的对象,需要存储并检索到以下 RDBMS 表中 −
Second step would be creating tables in your database. There would be one table corresponding to each object, you are willing to provide persistence. Consider the objects explained above, need to be stored and retrieved into the following RDBMS table −
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)
);
Create Mapping Configuration File
此步骤是创建映射文件,以指导 Hibernate — 如何将定义的类或类映射到数据库表。
This step is to create a mapping file that instructs Hibernate — how to map the defined class or classes to the database tables.
<?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>
Create Application Class
最后,我们将创建具有 main() 方法的应用程序类来运行应用程序。此处应该注意的是,在创建会话对象时,我们使用了拦截器类作为参数。
Finally, we will create our application class with the main() method to run the application. Here, it should be noted that while creating session object, we used our Interceptor class as an argument.
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();
}
}
}
Compilation and Execution
以下是编译和运行上述应用程序的步骤。请确保在继续编译和执行之前,已适当地设置 PATH 和 CLASSPATH。
Here are the steps to compile and run the above mentioned application. Make sure, you have set PATH and CLASSPATH appropriately before proceeding for the compilation and execution.
-
Create hibernate.cfg.xml configuration file as explained in configuration chapter.
-
Create Employee.hbm.xml mapping file as shown above.
-
Create Employee.java source file as shown above and compile it.
-
Create MyInterceptor.java source file as shown above and compile it.
-
Create ManageEmployee.java source file as shown above and compile it.
-
Execute ManageEmployee binary to run the program.
您将获得以下结果,并且将在 EMPLOYEE 表中创建记录。
You would get the following result, and records would be created in the EMPLOYEE table.
$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 表,它应包含以下记录 −
If you check your EMPLOYEE table, it should have the following records −
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>