目录
2.使用本地Hibernate API和hbm.xml映射的教程
例11:获得javax.persistence.EntityManagerFactory
例14:使用org.hibernate.envers.AuditReader
同时用面向对象技术和关系型数据库编程繁琐且耗时。因为在对象和在数据库中表示数据方式(paradigm)的不同导致开发成本明显更高。Hibernate是java环境下的Object/Relational Mapping(ORM)处理手段。术语ORM可以理解为是一种将对象模型表示和关系模型表现进行映射(转换)的技术,更高水平的解释请看http://en.wikipedia.org/wiki/Object-relational_mapping。另外,Martin Fowler的文章OrmHate (https://martinfowler.com/bliki/OrmHate.html)也介绍了很多不匹配的问题(ps:大概就是介绍ORM的缺点吧)。
虽然使用Hibernate不要求熟练掌握SQL,但了解SQL的基础概念会让你更快且更全面的理解Hibernate.其中理解数据模型原理尤为重要。http://www.agiledata.org/essays/dataModeling101.html和http://en.wikipedia.org/wiki/Data_modeling是个不错的了解数据模型原理的起点。
Hibernate负责将java类映射到数据库表、将java数据类映射为SQL数据类型。另它提供查询和检索功能。它可以省去在SQL和JDBC中手工处理数据的时间,明显降低开发时间,解放开发者。然而,不像很多其他的同类型的解决方案,Hibernate并没有限制你使用SQL功能,所有你所掌握的相关技术和知识在Hibernate中依旧可用。
由于它只使用存储过程来实现数据库中的业务逻辑,对以数据为中心的应用程序来说Hibernate可能不是最佳解决方案,但它对于面向对象领域模型和基于java的中间业务逻辑层是非常有用的。Hibernate可以帮你移除或封装特定功能的SQL语句。也简化了将结果集从表格表示转换为对象(集合)的常见任务。
想参与到Hibernate开发中详见http://hibernate.org/orm/contribute/。
注意:此教程里用到的项目和代码可用在这里获得:hibernate-tutorials.zip
为了隔离依赖项hibernate将功能分为若干模块
hibernate-core:hibernate的主模块,定义了ORM的特性和API,以及各种集成SPI。
hibernate-envers:Hibernate的历史实体版本控制功能
hibernate-spatial:Hibernate的空间/地理信息系统数据类型支持
hibernate-osgi:对OSGI容器的运行支持
hibernate-agroal:集成Agroal connection pooling
hibernate-c3p0:集成C3P0 connection poolinge
hibernate-hikaricp:集成HikariCP connection pooling
hibernate-vibur:集成Vibur DBCP connection pooling
hibernate-proxool:集成Proxool connection pooling
hibernate-jcache:集成JCache caching specification,允许任何兼容的实现成为二级缓存提供者
hibernate-ehcache:集成 Ehcache caching 库作为二级缓存提供者
hibernate团队将发布包托管在SourceForge文件发布平台,TGZ和ZIP格式都有,每个发布包都包含jar文件,文档。源代码和其他好东西。
你可以在https://sourceforge.net/projects/hibernate/files/hibernate-orm/.下载你所需要的。文件里面的结构是这样的:
lib/required/路径下包含hibernate-core功能模块,在程序设计中必须保证这些jar包都可用。(ps: 再加上连接数据的JDBC,程序就可以跑起来了)
lib/envers/路径下是hibernate-envers功能模块
lib/spatial/路径下是hibernate-spatial功能模块
lib/osgi/路径下是hibernate-spatial功能模块
lib/jpa-metamodel-generator/路径下包含生成the Criteria API type-safe Metamodel.所需的JAR。
lib/optional/路径下包含hibernate提供的各种连接池和二级缓存及其所需的依赖项
hibernate的权威库是JBoss Maven repository,Hibernate工件已经自动同步到Maven中心(一些小地方可能会有延迟)。
负责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工件在org.hibernate groupid下发布。
注意:这个教程在刚才下载的hibernate-tutorials.zip的basic/.路径下
目标:
在这个教程里,hibernate.cfg.xml文件定义了Hibernate的配置信息
<?xml version='1.0' encoding='utf-8'?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ 许可证: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库连接设置 -->
<property name="connection.driver_class">org.h2.Driver</property>
<property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
<property name="connection.username">sa</property>
<property name="connection.password"/>
<!-- JDBC 连接池 (用的内置的) -->
<property name="connection.pool_size">1</property>
<!-- SQL 方言 -->
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
<!-- 禁用二级缓存 -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- 输出执行的SQL到标准输出 -->
<property name="show_sql">true</property>
<!-- 启动时删除并重新创建数据库架构 -->
<property name="hbm2ddl.auto">create</property>
<!-- 设置hbm.xml的位置 -->
<mapping resource="org/hibernate/tutorial/hbm/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>
connection.driver_class, connection.url, connection.username 和 connection.password <property/> 元素定义了JDBC连接信息。
这些教程使用内存中的h2数据库,用户请根据自己的数据库填写属性值。
connection.pool_size被用来定义连接到Hibernate内置连接池的数量
注意:内置连接池不能用于实际生产使用,他缺少几个生产就绪连接池的功能。
方言属性指定Hibernate将与之进行转换的特定SQL变量。
注意:在大多数情况下,Hibernate都能够正确地确定要使用的方言。这个属性在应用程序以多个数据库为目标时尤为有用。
hbm2ddl.auto属性允许直接将数据库模式自动生成到数据库中。
最后,添加映射文件,<mapping/>元素的resource属性让hibernate尝试使用java.lang.classloade像定位类路径资源一样查找该映射。(ps:所以cfg.xml要放到Classpath下)
有很多方法启动SessionFactory,更多细节请看Native Bootstrapping topical guide.
教程的实体类在org.hibernate.tutorial.hbm.Event路径下
import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
public Event() {
// this form used by Hibernate
}
public Event(String title, Date date) {
// for application use, to create new events
this.title = title;
this.date = date;
}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
这个类用标准的javaBean命名习惯命名getter和setter方法,成员变量用private修饰。这是推荐设计,但不是强制要求。
有无参的构造函数也是JavaBean的习惯,这Hibernate里是强制要求的。因为Hibernate需要用java的反射技术创建对象。这个构造方法可以是私有的。However, package or public visibility is required for runtime proxy generation and efficient data retrieval without bytecode instrumentation.(这句实在不懂,翻译不来。。。)
这个教程映射文件的类资源路径是org/hibernate/tutorial/hbm/Event.hbm.xml
<?xml version="1.0"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.tutorial.hbm">
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>
</hibernate-mapping>
Hibernate通过使用映射元数据决定如何加载和存储持久类的对象。Hibernate映射文件只是提供Hibernate这些元数据的一种选择。
<class name="Event" table="EVENTS">
...
</class>
name元素指定实体类,table元素指定数据库中存储该实体类的表。
现在实体类的实例映射到了数据库中表的一行。
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
HIbernate使用<id/>元素来唯一确定表中的一行。一般来说id元素对应table的主码,虽然不要求,但是建议这样做。Hibernate甚至不要求定义主键,但强烈要求数据库设计有适当的完整性约束。因此在Hibernate文档中id和主码是可以互换的。现在实体类里的id变量对应表里的id字段。
generator元素通知Hibernate用哪种策略产生实体的主码,这个例子采用简单的增长计数。
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
name属性对应实体类的成员变量,column属性对应数据库表的字段。当省略column属性时,Hibernate用实体类变量名作为数据库的字段名。
在mapping文件里数据类型type的取值既不是java的数据类型也不是SQL的数据类型,而是Hibernate mapping types。这是在java数据类型和SQL数据类型之间进行相互转换的转换器。如果在映射文件中没有明确指定type,Hibernate就尝试自动决定如何正确转换。通过java的反射技术确定声明成员变量用的数据类型,然后使用默认的映射类型。
在一些情况下自动匹配的或许不是你想要的。例如date成员变量,Hibernate不知道java.util.Date是匹配SQL中的DATE,TIME,还是TIMESTAMP。为了保护完整的日期和时间信息,只能将成员变量和timestamp转换器匹配。
Hibernate在处理映射文件时用反射决定匹配类型,这个过程会增加时间和资源方面的开销,如果程序的启动性能很重要的话,考虑明确指定数据类型。
org.hibernate.tutorial.hbm.NativeApiIllustrationTest类说明如何使用Hibernate本地API。
(ps: 示例代码分了三个方法,我合并为一个方便观察。
@Test
public void test()
{
User user=new User();
user.setName("test5");
user.setPassword("123456");
//创建会话工厂
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure() // configures settings from hibernate.cfg.xml
.build();
SessionFactory sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
//实现业务逻辑
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
session.close();
session = sessionFactory.openSession();
session.beginTransaction();
@SuppressWarnings("unchecked")
List<User> result = session.createQuery( "from User" ).list();
for ( User userGet : result ) {
System.out.println(userGet.getName()+"\t"+userGet.getPassword());
}
session.getTransaction().commit();
session.close();
//关闭会话工厂
sessionFactory.close();
}
首先获取StandardServiceRegistry实例,该实例将配置文件合并到工作集合供sessionFactory使用,因为本例我们将所有的配置信息定义在hibernate.cfg.xml,所以我们并没有看到它更多有趣的地方。
用StandardServiceRegistry我们可以创建一个 org.hibernate.boot.MetadataSources,它是告诉Hibernate你的域模型的起点。因为本例我们将所有的配置信息定义在hibernate.cfg.xml,所以我们又一次没有看到它更多有趣的地方。
引导程序过程的最后一步是构建SessionFactory,它是线程安全的对象,实例一次为整个应用服务。
SessionFactory扮演实例化org.hibernate.Session的工厂角色。
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();
在这段代码中Hibernate通过save()方法向数据库插入数据。
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();
目标:
除了<mapping/>元素不同,这里的内容和2.1的内容是完全相同的。
<?xml version='1.0' encoding='utf-8'?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">org.h2.Driver</property>
<property name="connection.url">jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property>
<!-- Names the annotated entity class -->
<mapping class="org.hibernate.tutorial.annotations.Event"/>
</session-factory>
</hibernate-configuration>
这个教程里的实体类是org.hibernate.tutorial.annotations.Event,也是符合javaBean习惯的。事实上这个类上面用到的实体类是完全一样的,除了这里用注解替代映射文件提供元数据。
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table( name = "EVENTS" )
public class Event {
private Long id;
private String title;
private Date date;
public Event() {
// this form used by Hibernate
}
public Event(String title, Date date) {
// for application use, to create new events
this.title = title;
this.date = date;
}
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "EVENT_DATE")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
@Entity@Table( name = "EVENTS" )
public class Event {
...
}
注解@javax.persistence.Entity被用来将类标识为实体,注解@javax.persistence.Table显式指定表名。,如果没有显示指明,默认表名将和类名一致。
@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的增量生成策略来生成此实体的标识符值。
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "EVENT_DATE")
public Date getDate() {
return date;
}
和在映射文件中一样,日期变量需要特殊处理以说明其特殊的命名和特殊的SQL数据类型。
在使用注释进行映射时,默认情况下,实体的属性被认为是持久的,这就是为什么我们看不到任何与title相关联的映射信息的原因。
目标:
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<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">
<description>
Persistence unit for the JPA tutorial of the Hibernate Getting Started Guide
</description>
<class>org.hibernate.tutorial.em.Event</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
</persistence>
前面的教程用HIbernate具体的hibernate.cfg.xml配置文件。然而,JPA用它自己的配置文件persistence.xml来定义不同的bootstrap过程。这个bootstrap过程符合JPA规范。在javaSE环境下运行的持久提供者(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文件应该为每个persistence unit提供独一无二的name。在获得javax.persistence.EntityManagerFactory引用时应用程序通过这些name查找配置文件。
<properties/>元素中定义的设置在hibernate配置文件中讨论过了。在这里,尽可能使用以javax.persistence为前缀的变体。 请注意,剩余的Hibernate特定的配置设置名称现在以hibernate.作为前缀。
另外,<class/>元素的功能和3.1配置文件的功能是一样的。
这个实体类和3.2的注解实体类完全一致。
前面的教程使用本地APIs,这个教程使用JPA APIs。
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import junit.framework.TestCase;
public class EntityManagerIllustrationTest extends TestCase {
private EntityManagerFactory entityManagerFactory;
@Override
protected void setUp() throws Exception {
// like discussed with regards to SessionFactory, an EntityManagerFactory is set up once for an application
// IMPORTANT: notice how the name here matches the name we gave the persistence-unit in persistence.xml!
entityManagerFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );
}
@Override
protected void tearDown() throws Exception {
entityManagerFactory.close();
}
public void testBasicUsage() {
// create a couple of events...
EntityManager entityManager = entityManagerFactory.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();
// now lets pull events from the database and list them
entityManager = entityManagerFactory.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();
}
}
protected void setUp() throws Exception {
sessionFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );}
再次注意这里的persistence unit name是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调用persist操作替代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的代码也很相似。
目标:
这种配置文件在JPA教程里已经讨论过了,这里用的的与上面的基本相似。
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<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.envers">
<description>
Persistence unit for the Envers tutorial of the Hibernate Getting Started Guide
</description>
<class>org.hibernate.tutorial.envers.Event</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
</persistence-unit>
</persistence>
和上面使用的注解实体类大部分相似,主要不同点是添加了注解@org.hibernate.envers.Audited,它告诉Envers自动跟踪这个实体的变化。
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.envers.Audited;
@Entity
@Table( name = "EVENTS" )
@Audited // <--- this tell Envers to audit (track changes to) this entity
public class Event {
private Long id;
private String title;
private Date date;
public Event() {
// this form used by Hibernate
}
public Event(String title, Date date) {
// for application use, to create new events
this.title = title;
this.date = date;
}
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "EVENT_DATE")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public int hashCode() {
int result = title.hashCode();
result = 31 * result + date.hashCode();
return result;
}
}
public class EnversIllustrationTest extends TestCase {
private EntityManagerFactory entityManagerFactory;
@Override
protected void setUp() throws Exception {
// like discussed with regards to SessionFactory, an EntityManagerFactory is set up once for an application
// IMPORTANT: notice how the name here matches the name we gave the persistence-unit in persistence.xml!
entityManagerFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.envers" );
}
@Override
protected void tearDown() throws Exception {
entityManagerFactory.close();
}
public void testBasicUsage() {
// create a couple of events
EntityManager entityManager = entityManagerFactory.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();
// now lets pull events from the database and list them
entityManager = entityManagerFactory.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();
// so far the code is the same as we have seen in previous tutorials. Now lets leverage Envers...
// first lets create some revisions
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
Event myEvent = entityManager.find( Event.class, 2L ); // we are using the increment generator, so we know 2 is a valid id
myEvent.setDate( new Date() );
myEvent.setTitle( myEvent.getTitle() + " (rescheduled)" );
entityManager.getTransaction().commit();
entityManager.close();
// and then use an AuditReader to look back through history
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
myEvent = entityManager.find( Event.class, 2L );
assertEquals( "A follow up event (rescheduled)", myEvent.getTitle() );
AuditReader reader = AuditReaderFactory.get( entityManager );
Event firstRevision = reader.find( Event.class, 2L, 1 );
assertFalse( firstRevision.getTitle().equals( myEvent.getTitle() ) );
assertFalse( firstRevision.getDate().equals( myEvent.getDate() ) );
Event secondRevision = reader.find( Event.class, 2L, 2 );
assertTrue( secondRevision.getTitle().equals( myEvent.getTitle() ) );
assertTrue( secondRevision.getDate().equals( myEvent.getDate() ) );
entityManager.getTransaction().commit();
entityManager.close();
}
}
这段代码保存了一些实体,然后改变一个并用Envers API 收回初始化版本和更新版本。一个修订是指这个实体的一个历史快照
public void testBasicUsage() {
...
AuditReader reader = AuditReaderFactory.get( entityManager );
Event firstRevision = reader.find( Event.class, 2L, 1 );
...
Event secondRevision = reader.find( Event.class, 2L, 2 );
...
}
以javax.persistence.EntityManager为参数从org.hibernate.envers.AuditReaderFactory获得一个org.hibernate.envers.AuditReader。然后,fine方法检索entity的特定版本,第一个调用检索版本号为1并且id为2的实体,第二个调用版本号为2并且id为2 的实体