hibernate-release-5.4.3.Final快速开始教程---中文版

汪鸿波
2023-12-01

目录

 

序言

1.获取hibernate

1.1.hibernate模块

1.2.版本包下载

1.3.Maven库工具

2.使用本地Hibernate API和hbm.xml映射的教程

2.1.Hibernate的配置文件

2.2.实体java类

2.3.映射文件

例一:类映射元素

例二:id元素

例三:属性映射元素

2.4.示例代码

例五:保存实体

例六:获取实例集合

3.使用本地APIs和注解映射

3.1.HIbernate配置文件

3.2.注解实体java类

例7:将类标识为实体

例8:标识标识符属性

例9:标识基础成员变量

3.3.示例代码,和上面2.4的示例代码完全一样

4.使用JPA的教程

4.1. persistence.xml

例十:persistence.xml

4.2.注解实体类

4.3.示例代码

例11:获得javax.persistence.EntityManagerFactory

例12:保存(持久化)实体

例13:获得实体list

5.使用Envers的教程

5.1.persistence.xml

5.2.注解实体类

5.3.示例代码

例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.htmlhttp://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

1.获取hibernate

1.1.hibernate模块

为了隔离依赖项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 库作为二级缓存提供者

1.2.版本包下载

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提供的各种连接池和二级缓存及其所需的依赖项

1.3.Maven库工具

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下发布。

2.使用本地Hibernate API和hbm.xml映射的教程

注意:这个教程在刚才下载的hibernate-tutorials.zip的basic/.路径下

目标:

  • 启动一个hibernate会话工程
  • 使用Hibernate映射(hbm.xml)文件提供映射信息
  • 使用Hibernate本地APIs

2.1.Hibernate的配置文件

在这个教程里,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.

2.2.实体java类

教程的实体类在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.(这句实在不懂,翻译不来。。。)

2.3.映射文件

这个教程映射文件的类资源路径是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元素

<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在处理映射文件时用反射决定匹配类型,这个过程会增加时间和资源方面的开销,如果程序的启动性能很重要的话,考虑明确指定数据类型。

 

2.4.示例代码

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();

 

3.使用本地APIs和注解映射

 

目标:

  • 启动一个hibernate会话工程
  • 使用注解提供映射信息
  • 使用Hibernate本地APIs

3.1.HIbernate配置文件

除了<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>

3.2.注解实体java类

这个教程里的实体类是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;
    }
}

例7:将类标识为实体

@Entity@Table( name = "EVENTS" )

public class Event {

...

}

注解@javax.persistence.Entity被用来将类标识为实体,注解@javax.persistence.Table显式指定表名。,如果没有显示指明,默认表名将和类名一致。

例8:标识标识符属性

@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的增量生成策略来生成此实体的标识符值。

例9:标识基础成员变量

@Temporal(TemporalType.TIMESTAMP)

@Column(name = "EVENT_DATE")

public Date getDate() {

return date;

}

和在映射文件中一样,日期变量需要特殊处理以说明其特殊的命名和特殊的SQL数据类型。

 

在使用注释进行映射时,默认情况下,实体的属性被认为是持久的,这就是为什么我们看不到任何与title相关联的映射信息的原因。

3.3.示例代码,和上面2.4的示例代码完全一样

4.使用JPA的教程

目标:

  • 启动一个JPAEntityManagerFactory
  • 使用Hibernate映射(hbm.xml)文件提供映射信息
  • 使用JPA API调用

4.1. persistence.xml

<!--

  ~ 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.xml

<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配置文件的功能是一样的。

4.2.注解实体类

这个实体类和3.2的注解实体类完全一致。

4.3.示例代码

前面的教程使用本地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();

}

}

例11:获得javax.persistence.EntityManagerFactory

protected void setUp() throws Exception {

sessionFactory = Persistence.createEntityManagerFactory( "org.hibernate.tutorial.jpa" );}

再次注意这里的persistence unit name是org.hibernate.tutorial.jpa,与配置文件persistence.xml里的相匹配。

例12:保存(持久化)实体

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。

 

例13:获得实体list

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的代码也很相似。

 

5.使用Envers的教程

目标:

  • 向前面一样使用数据实体类
  • 配置Envers
  • 使用Envers APIs 查看和分析历史数据。

5.1.persistence.xml

这种配置文件在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>

5.2.注解实体类

和上面使用的注解实体类大部分相似,主要不同点是添加了注解@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;

}

}

5.3.示例代码

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 收回初始化版本和更新版本。一个修订是指这个实体的一个历史快照

例14:使用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 );

...

}

以javax.persistence.EntityManager为参数从org.hibernate.envers.AuditReaderFactory获得一个org.hibernate.envers.AuditReader。然后,fine方法检索entity的特定版本,第一个调用检索版本号为1并且id为2的实体,第二个调用版本号为2并且id为2 的实体

 类似资料: