前言
最近的精力主要集中在Hibernate上,在意识到Hibernate 5 的中文资料并不多的时候,我不得不把目光转向Hibernate的官方doc,学习之余简要翻一下入门文档。
原文地址:https://docs.jboss.org/hibernate/orm/5.4/quickstart/html_single/,勘误请在评论区留言
原文序
使用面向对象软件和关系型数据库可能既麻烦且耗时。开发成本往往会因数据在软件和数据库中的数据范式(paradigm)表现不一而显著地增长,Hibernate就是一种Java语言环境下的一个对象/关系映射 解决方案(ORM框架),对象/关系映射(ORM)的概念,则代指一种将数据的对象模型形式,即在软件中的表现形式,和数据模型形式,即数据库中的表现形式,这两种形式的数据相映射的技术。可以参照Wikipedia上的ORM词条以获得更详细的了解。
虽然Hibernate并不要求用户需要有非常丰富的SQL编程经验,但是对于一些概念的基本理解着实可以让你更快更全面的理解Hibernate,特别是对数据建模原理的理解就显得尤为重要,这两篇文章可以帮助你快速开始理解这些原理dataModeling101 、DataModeling(wiki)。
Hibernate负责Java class和数据库表的映射工作,同时也会处理两者间数据格式的映射。另外,它还提供了数据查询和获取功能。它可以节省开发者原本需要的手动数据处理时间(如果你使用SQL和JDBC这些原生手段的话)Hibernate的设计目标是将开发者从95%的日常持久化编程步骤中解放出来,开发者再无需手册来通过SQL和JDBC手动处理数据。同时,不像其他持久化方案,Hibernate并不会将原生SQL的优点隐藏起来,你依旧可以使用它,意味着你之前所付出的学习和相关知识依然十分有价值。
HIbernate可能不是那些以数据为中心的,重度依赖存储过程的应用的最佳解决方案。它更适合于对于数据相关的应用中间层,这种情况下,它的确可以去除掉那些针对特定数据库而写的SQL,流水化查询的数据集转译到对象表(对于编程语言而言)。
See http://hibernate.org/orm/contribute/ for information on getting involved.
本文章中涉及的项目代码可以从这里下载: hibernate-tutorials.zip
1. 获取 Hibernate
1.1. Hibernate Modules/Artifacts
Hibernate’s 的功能被分成一些不同的 modules/artifacts 用来隔离依赖关系(模块化).
- hibernate-core
-
Hibernate 核心module. 定义了ORM功能和API,还有不同的集成SPIs(方便第三方进行自定义扩展).
- hibernate-envers
-
Hibernate历史实体版本控制功能
- hibernate-spatial
-
Hibernate空间数据集和GIS(地理信息系统)数据类型支持
- hibernate-osgi
-
Hibernate对在OSGi容器中运行的支持。
- hibernate-agroal
-
Hibernate Agroal 连接池支持
- hibernate-c3p0
-
Hibernate C3P0 连接池支持
- hibernate-hikaricp
-
Hibernate HikariCP 连接池支持
- hibernate-vibur
-
Hibernate Vibur DBCP 连接池支持
- hibernate-proxool
-
Hibernate Proxool 连接池支持
- hibernate-jcache
-
Hibernate JCache 缓存支持 , 使得任何兼容的组件成为二级缓存的提供者。
- hibernate-ehcache
-
Hibernate Ehcache 缓存支持。使得Ehache成为二级缓存提供者。
1.2. 发行包下载
Hibernate发行包存放在 SourceForge File Release System, 有 TGZ
和 ZIP
两种格式,两种都包含了JAR文件、文档、源代码和其他一些东西。
你可以从这里下载:https://sourceforge.net/projects/hibernate/files/hibernate-orm/. 以下是发行包目录结构:
-
lib/required/
目录包含hibernate-core
jar包和它的依赖。所有的jar包都需要在在classpath下,不论那些功能有没有被用到。 -
lib/envers
目录包含hibernate-envers
jar包和它的依赖 (先包含lib/required/
和lib/jpa/
). -
lib/spatial/
目录包含hibernate-spatial
jar包和它的依赖 (先包含lib/required/
) -
lib/osgi/
目录包含hibernate-osgi
jar包和它的依赖 (先包含lib/required/
和lib/jpa/
) -
lib/jpa-metamodel-generator/
目录包含 生成Criteria API类型安全的元模型(Criteria API type-safe Metamodel)所需的jar。 -
lib/optional/
目录包含 Hibernate提供的各种连接池和第二级缓存集成所需的jar及其依赖。
1.3. Maven Repository Artifacts
Hibernate artifacts的权威仓库为the JBoss Maven repository. Hibernate artifacts会自动同步到这上面(可能会延迟发布).
负责JBoss Maven仓库的团队维护着许多包含重要信息的Wiki页面:
-
http://community.jboss.org/docs/DOC-14900 - 有关仓库的常规信息。
-
http://community.jboss.org/docs/DOC-15170 - 有关设置JBoss信息库以便对JBoss项目本身进行开发工作的信息。
-
http://community.jboss.org/docs/DOC-15169 - 有关设置访问仓库以将JBoss项目用作您自己的软件的一部分的信息。
Hibernate ORM artifacts发布在org.hibernate groupId下。
2. 使用Hibernate原生 API 和 hbm.xml 映射文件
本示例位于您下载的压缩包中的basic/ . |
-
启动 Hibernate
SessionFactory
-
使用 Hibernate 映射文件(
hbm.xml
)来提供映射信息 -
使用Hibernate原生API
2.1. Hibernate配置文件
在本示例中, hibernate.cfg.xml
文件定义了Hibernate配置信息
connection.driver_class
, connection.url
, connection.username
和connection.password
<property/>
元素定义了JDBC连接信息这些示例使用内置的H2 in-memory 数据库,所以所有的属性被设置成H2数据库以在内存中运行connection.pool_size
用来配置内置连接池的大小。
内置的Hibernate连接池并不能用于实际应用中,对比实际应用中的的连接池它缺乏一些基本功能 |
dialect元素定义了Hibernate会使用的特定SQL类型
绝大多数情况下, Hibernate 能够自动选择数据库的方言(dialect),当你使用多种数据库时这点尤其有用(注:3.x版本不清楚) |
hbm2ddl.auto
属性开启自动生成数据库schema,也即自动建表功能。
最后,添加映射文件到配置文件中, 位于 <mapping/>
元素中的resource
使得Hibernate会尝试去使用用java.lang.ClassLoader
在classpath下进行查找。
有许多方式可以用于启动SessionFactory,详见the Native Bootstrapping topical guide.
2.2. Java类
本示例中的实体类为示例代码中的: org.hibernate.tutorial.hbm.Event
-
实体类使用标准JavaBean的命名规范,包括getter/setter和私有的变量,虽然这是推荐的用法,但并不强制。
-
无参构造函数,同样作为JavaBean的规范,则要求必须实现,Hibernate需要通过反射机制(Java Reflection)为你创建实体类(即查询结果时),构造函数不能是私有的,同时,包可见性也要保证,这样才能通过java运行时代理机制(runtime proxy)生成以保证无需通过 字节码检测(bytecode instrumentation)进行代理。
2.3. 映射文件
本示例中的映射文件位于 org/hibernate/tutorial/hbm/Event.hbm.xml
(同上文).
Hibernate 使用映射元数据来决定如何加载和存储持久化类,Hibernate 映射文件就是提供元数据的一种方式。
<class name="Event" table="EVENTS"> ... </class>
关于 <varname>class</varname> 映射元素:
-
name
属性 (可在<hibernate-mapping/>
中添加package属性来定义包名) 定义了类名 -
table
属性定义表名
到此为止,Event类和EVENTS数据库表被关联起来。
<id name="id" column="EVENT_ID"> ... </id>
Hibernate 使用 <id/>
元素来确保行唯一性。
id元素不需要映射到表的实际主键列,但这是常规约定。在Hibernate中映射的表甚至不需要定义主键。但是,强烈建议所有schema都定义适当的参照完整性。如此一来,id和主键在整个Hibernate中可以互换使用 。 |
此处的<id/>
元素将EVENT_ID列命名为EVENTS表的主键。它还将Event类的id属性标识为包含标识符值的属性。
generator
元素通知Hibernate使用哪种策略为该实体生成主键值。本示例使用简单的递增计数。(注,在其他相关性能调优指南文章中,有提及不要使用id生成器,会那样会阻止批量插入)
<property name="date" type="timestamp" column="EVENT_DATE"/> <property name="title"/>
这两个 <property/>
元素声明了Event
类的其余两个持久属性:date
和title
。date
属性映射包括列属性,但title
不包含。在没有列属性的情况下,Hibernate默认使用属性名称作为列名称。这适用于title,但是由于date
是大多数数据库中的保留关键字,因此您需要为列名指定一个非保留字。
title映射也缺少类型属性。映射文件中声明和使用的类型既不是Java原生数据类型也不是SQL数据库类型。相反,它们是Hibernate映射类型,它们是在Java和SQL数据类型之间转换的转换器。如果未在映射中指定type属性,则Hibernate尝试自动确定正确的转换和映射类型,方法是使用Java反射来确定已声明属性的Java类型,并使用该Java类型的默认映射类型(注:如,BigInteger->Integer)。
在某些情况下,如date
属性所示,此自动检测可能未选择您期望或需要的默认值。 Hibernate无法知道类型为java.util.Date的属性是否应映射到SQL DATE,TIME或TIMESTAMP数据类型。通过将属性映射到timestamp转换器(timestamp converter)来保存完整的日期和时间信息,该转换器标识转换器类org.hibernate.type.TimestampType。
处理映射文件时,Hibernate使用反射来确定映射类型。此过程增加了时间和资源方面的开销。如果启动性能( startup performance)很重要,请考虑明确定义要使用的类型。 |
2.4. 示例代码
org.hibernate.tutorial.hbm.NativeApiIllustrationTest
类说明了如何使用Hibernate原生API。
为了易于使用,这些教程中的示例以JUnit测试的形式呈现。这种方法的优点之一是setUp() 和tearDown() 大致说明了如何在应用程序启动时创建org.hibernate.SessionFactory并在应用程序生命周期结束时将其关闭。 |
org.hibernate.SessionFactory
protected void setUp() throws Exception { // A SessionFactory is set up once for an application! final StandardServiceRegistry registry = new StandardServiceRegistryBuilder() .configure() // configures settings from hibernate.cfg.xml .build(); try { sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory(); } catch (Exception e) { // The registry would be destroyed by the SessionFactory, but we had trouble building the SessionFactory // so destroy it manually. StandardServiceRegistryBuilder.destroy( registry ); } }
setUp方法首先创建org.hibernate.boot.registry.StandardServiceRegistry实例,该实例将配置信息合并到服务(Services,有特指)的工作集中以供SessionFactory使用。在本教程中,我们在hibernate.cfg.xml中定义了所有配置信息。
使用StandardServiceRegistry我们创建org.hibernate.boot.MetadataSources,这是向Hibernate告知您的Domain模型的起点。再一次,因为我们在hibernate.cfg.xml中定义了它,所以这边没什么东西好讲的。 org.hibernate.boot.Metadata表示SessionFactory将基于的应用程序域模型的完整,部分验证的视图。
引导程序的最后一步是构建SessionFactory。 SessionFactory是一个线程安全的对象,实例化一次即可服务整个应用程序。
SessionFactory充当org.hibernate.Session实例的工厂,应将其视为“工作单元”的必然(corollary )结果。
示例 5. 保存实体
Session session = sessionFactory.openSession(); session.beginTransaction(); session.save( new Event( "Our very first event!", new Date() ) ); session.save( new Event( "A follow up event", new Date() ) ); session.getTransaction().commit(); session.close();
testBasicUsage() 首先使用save() 方法创建一些新的Event对象并将其交给Hibernate进行管理。 Hibernate现在负责为每个Event在数据库上执行INSERT。
session = sessionFactory.openSession(); session.beginTransaction(); List result = session.createQuery( "from Event" ).list(); for ( Event event : (List<Event>) result ) { System.out.println( "Event (" + event.getDate() + ") : " + event.getTitle() ); } session.getTransaction().commit(); session.close();
在这里,我们看到一个Hibernate查询语言(HQL)的示例,该示例通过生成适当的SELECT SQL,将其发送到数据库,并使用结果集数据填充Event对象来从数据库加载所有现有的Event对象。
2.5. 下一步!
-
重新配置示例以连接到您自己的持久关系数据库。
-
Add an association to the
Event
entity to model a message thread.
3. 使用 Hibernate 原生API 和 注解式映射
本示例位于您下载的压缩包中的 annotations/ . |
-
启动 Hibernate
SessionFactory
-
使用 Java注解来提供映射信息
-
使用Hibernate原生API
3.1. Hibernate配置文件
内容与上文的Hibernate配置文件相同,但有一个重要区别...最后的<mapping />元素使用class属性命名带注释的实体类。
3.2. 注解的Java类
本示例中的实体类是遵循JavaBean约定的org.hibernate.tutorial.annotations.Event。实际上,该类本身与实体Java类中的类相同,只不过注释用于提供元数据而不是单独的映射文件。
@Entity @Table( name = "EVENTS" ) public class Event { ... }
@ javax.persistence.Entity注释用于将类标记为实体。它的功能与映射文件中讨论的<class />映射元素相同。此外,@ javax.persistence.Table注释显式指定了表名。如果没有此规范,默认表名称将为EVENT。
示例8. 标记id属性
@Id @GeneratedValue(generator="increment") @GenericGenerator(name="increment", strategy = "increment") public Long getId() { return id; }
@ javax.persistence.Id标记定义实体标识符的属性。
@ javax.persistence.GeneratedValue和@ org.hibernate.annotations.GenericGenerator协同工作以指定Hibernate应该对该实体的标识符值使用Hibernate的增量生成策略。
public String getTitle() { return title; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "EVENT_DATE") public Date getDate() { return date; }
与映射文件中一样,日期属性需要进行特殊处理以考虑其特殊命名和SQL类型。 带有注释的映射默认情况下将实体的属性视为持久化属性(与数据库字段相同),这就是为什么我们看不到任何与title相关的映射信息的原因。
3.3. 示例代码
org.hibernate.tutorial.annotations.AnnotationsIllustrationTest本质上与示例代码中讨论的org.hibernate.tutorial.hbm.NativeApiIllustrationTest相同。
3.4. 下一步!
-
向事件实体添加关联以对消息线程进行建模。有关更多详细信息,请使用《用户指南》。
- 添加回调以在创建,更新或删除事件时接收通知。使用事件监听器尝试相同的操作。有关更多详细信息,请使用《用户指南》。
4. 使用 Java Persistence API (JPA)
本示例位于您下载的压缩包中的 entitymanager/ . |
-
启动JPA EntityManagerFactory
-
使用 Java注解来提供映射信息
-
使用JPA API
4.1. persistence.xml
之前的教程使用了特定于Hibernate的hibernate.cfg.xml配置文件。但是,JPA定义了一个不同的引导过程,该过程使用其自己的名为persistence.xml的配置文件。该引导过程由JPA规范定义。在Java SE环境中,需要持久性提供程序(在这种情况下为Hibernate)通过META-INF / persistence.xml资源名称的类路径查找来定位所有JPA配置文件。
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="org.hibernate.tutorial.jpa"> ... </persistence-unit> </persistence>
persistence.xml文件应为每个“持久性单元”提供唯一的名称。在获取javax.persistence.EntityManagerFactory引用时,应用程序使用此名称来引用配置。
Hibernate配置文件中讨论了<properties />元素中定义的设置。这里尽可能使用javax.persistence前缀的变体。请注意,其余特定于Hibernate的配置设置名称现在以hibernate前缀。
此外,<class />元素的功能与我们在Hibernate配置文件中看到的相同。
4.2. 注解的Java类
实体与上节注解的的Java类中的完全相同。
4.3. 示例代码
先前的教程使用了Hibernate原生API。本教程使用JPA API
protected void setUp() throws Exception { sessionFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" ); }
再次注意,持久化单元名称是org.hibernate.tutorial.jpa,与persistence.xml相匹配。
EntityManager entityManager = sessionFactory.createEntityManager(); entityManager.getTransaction().begin(); entityManager.persist( new Event( "Our very first event!", new Date() ) ); entityManager.persist( new Event( "A follow up event", new Date() ) ); entityManager.getTransaction().commit(); entityManager.close();
该代码类似于上文的保存实体类。使用javax.persistence.EntityManager接口代替org.hibernate.Session接口。 JPA将此操作称为“持久化”(persistence)而不是“保存”(save)。
entityManager = sessionFactory.createEntityManager(); entityManager.getTransaction().begin(); List<Event> result = entityManager.createQuery( "from Event", Event.class ).getResultList(); for ( Event event : result ) { System.out.println( "Event (" + event.getDate() + ") : " + event.getTitle() ); } entityManager.getTransaction().commit(); entityManager.close();
同样,该代码与我们在获取上文的 获取List结果集看到的非常相似。
4.4. 下一步!
-
开发一个EJB Session bean来研究使用容器管理的持久性上下文的含义。尝试无状态(stateless )和有状态(stateful )两种情况。
-
将事件监听器(listeners)与基于CDI的注入(CDI-based injection)结合使用,以开发基于JMS的事件消息中心
5. 使用 Envers
本示例位于您下载的压缩包中的 envers/ . |
- 将实体注释为历史对象
-
配置 Envers
- 使用Envers API查看和分析历史数据
5.1. persistence.xml
这个文件在JPA 示例中已提过 persistence.xml, 而且基本是一样的
5.2. 注解的Java类
同样,该实体与上文:注释的Java类中的相同。主要区别在于添加了@ org.hibernate.envers.Audted注释,该注释告诉Envers自动跟踪对此实体的更改。
5.3. 示例代码
该代码保存了一些实体,对其中一个实体进行了更改,然后使用Envers API来查询初始修订(revision)以及更新的修订。修订是指实体的历史快照(snapshot )。
org.hibernate.envers.AuditReader
public void testBasicUsage() { ... AuditReader reader = AuditReaderFactory.get( entityManager ); Event firstRevision = reader.find( Event.class, 2L, 1 ); ... Event secondRevision = reader.find( Event.class, 2L, 2 ); ... }
可以看到:我们从org.hibernate.envers.AuditReaderFactory获得了一个org.hibernate.envers.AuditReader,它包装了javax.persistence.EntityManager。
接下来,find
方法检索实体的特定修订版。第一次调用找到ID为2的事件的修订版本1。第二次调用找到ID为2的事件的修订版本2。
5.4.下一步!
-
提供自定义修订版本实体,以额外获取进行更改的人员。
-
写一个只取回符合条件历史数据的查询。 使用 User Guide 查看 Envers 查询如何创建。(Envers用于在自动生成记录更改信息的数据表,用于审计)
- 尝试使用具有各种关系(多对一,多对多等)的审计实体(auditing entities)。尝试查找此类实体的历史版本(修订版)并浏览对象树。