6 Hibernate:Envers 入门

Hibernate Envers 记录实体对象的历史版本,主要用于审计数据,如意外丢失数据的找回、审计数据合法性等。

Hibernate Envers 使用 @Audited 注解实体对象,Hibernate 为每一个注解对象映射的数据表创建一个对应的审计数据存储表,借助 Listener 机制将数据更新操作(增删改)记录到审计表中。

以下展示 Hibernate Envers 开发的完整步骤:

(1) 使用 Maven 管理 Hibernate 依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>lesson</groupId>
  <artifactId>hibernate</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.40</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-envers</artifactId>
      <version>${hibernate.version}</version>
    </dependency>
  </dependencies>
  <properties>
    <hibernate.version>5.2.10.Final</hibernate.version>
  </properties>
</project>

(2) 创建 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="hibernate.envers">
    <description>
      Persistence unit for the JPA tutorial of the Hibernate Getting Started Guide
    </description>

    <class>hibernate.Person</class>

    <properties>
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test" />
      <property name="javax.persistence.jdbc.user" value="root" />
      <property name="javax.persistence.jdbc.password" value="123456" />

      <property name="hibernate.show_sql" value="true" />
      <property name="hibernate.hbm2ddl.auto" value="create" />
    </properties>
  </persistence-unit>

</persistence>

(3) 使用注解创建持久化类

package hibernate;

import java.util.Date;

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 = "person")
@Audited
public class Person {

    private int id;

    private String account;

    private String name;

    private Date birth;

    public Person() {}

    public Person(String account, String name, Date birth) {
        this.account = account;
        this.name = name;
        this.birth = birth;
    }

    @Id // 实体唯一标识
    @GeneratedValue(generator = "increment") // 使用名为“increment”的生成器
    @GenericGenerator(name = "increment", strategy = "increment") // 定义名为“increment”的生成器,使用Hibernate的"increment"生成策略,即自增
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Temporal(TemporalType.TIMESTAMP)
    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Person [id=" + id + ", account=" + account + ", name=" + name + ", birth=" + birth + "]";
    }

}

注意 @Audited 注解,启用 Envers 自动跟踪此实体对象的数据变化。

(4) 通过 org.hibernate.envers.AuditReader 查询实体对象历史记录

package hibernate;

import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class EnversIllustrationTest {

    private EntityManagerFactory entityManagerFactory;

    @Before
    public void setUp() throws Exception {
        entityManagerFactory = Persistence.createEntityManagerFactory("hibernate.envers");
    }

    @After
    public void tearDown() throws Exception {
        entityManagerFactory.close();
    }

    @Test
    public void test() {
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        entityManager.persist(new Person("tony", "Tony Stark", new Date()));
        entityManager.persist(new Person("hulk", "Banner", new Date()));
        entityManager.getTransaction().commit();
        entityManager.close();

        entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        List<Person> persons = entityManager.createQuery("FROM Person", Person.class).getResultList();
        System.out.println("------------------------------------------------------------------------------------------------------------------");
        System.out.println("INITIAL DATA : ");
        for (Person person : persons) {
            System.out.println(person);
        }
        System.out.println("------------------------------------------------------------------------------------------------------------------");
        entityManager.getTransaction().commit();
        entityManager.close();

        entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        Person person = entityManager.find(Person.class, 2);
        person.setBirth(new Date());
        person.setName("Bruce Banner");
        entityManager.getTransaction().commit();
        entityManager.close();

        entityManager = entityManagerFactory.createEntityManager();
        entityManager.getTransaction().begin();
        person = entityManager.find(Person.class, 2);
        System.out.println("------------------------------------------------------------------------------------------------------------------");
        System.out.print("UPDATED DATA : ");
        System.out.println(person);
        System.out.println("------------------------------------------------------------------------------------------------------------------");
        AuditReader reader = AuditReaderFactory.get(entityManager);
        Person original = reader.find(Person.class, 2, 1);
        Person current = reader.find(Person.class, 2, 2);
        System.out.println("------------------------------------------------------------------------------------------------------------------");
        System.out.print("First Version : ");
        System.out.println(original);
        System.out.print("Second Version : ");
        System.out.println(current);
        System.out.println("------------------------------------------------------------------------------------------------------------------");
        entityManager.getTransaction().commit();
        entityManager.close();
    }

}

单元测试日志:

六月 26, 2017 10:14:16 下午 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
    name: hibernate.envers
    ...]
六月 26, 2017 10:14:16 下午 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {5.2.10.Final}
六月 26, 2017 10:14:16 下午 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
六月 26, 2017 10:14:16 下午 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
六月 26, 2017 10:14:16 下午 org.hibernate.boot.jaxb.internal.stax.LocalXmlResourceResolver resolveEntity
WARN: HHH90000012: Recognized obsolete hibernate namespace http://hibernate.sourceforge.net/hibernate-mapping. Use namespace http://www.hibernate.org/dtd/hibernate-mapping instead.  Support for obsolete DTD/XSD namespaces may be removed at any time.
六月 26, 2017 10:14:19 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!)
六月 26, 2017 10:14:19 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [com.mysql.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/test]
六月 26, 2017 10:14:19 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {user=root, password=****}
六月 26, 2017 10:14:19 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
六月 26, 2017 10:14:19 下午 org.hibernate.engine.jdbc.connections.internal.PooledConnections <init>
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
Mon Jun 26 22:14:19 CST 2017 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
六月 26, 2017 10:14:20 下午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
六月 26, 2017 10:14:20 下午 org.hibernate.envers.boot.internal.EnversServiceImpl configure
INFO: Envers integration enabled? : true
Hibernate: drop table if exists PERSON
六月 26, 2017 10:14:23 下午 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@425d5d46] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate: drop table if exists PERSON_AUD
Hibernate: drop table if exists REVINFO
Hibernate: create table PERSON (ID integer not null auto_increment, ACCOUNT varchar(255), NAME varchar(255), BIRTH datetime, primary key (ID)) engine=MyISAM
六月 26, 2017 10:14:23 下午 org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@47f08b81] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate: create table PERSON_AUD (ID integer not null, REV integer not null, REVTYPE tinyint, ACCOUNT varchar(255), NAME varchar(255), BIRTH datetime, primary key (ID, REV)) engine=MyISAM
Hibernate: create table REVINFO (REV integer not null auto_increment, REVTSTMP bigint, primary key (REV)) engine=MyISAM
Hibernate: alter table PERSON_AUD add constraint FK3lr1agcm4oxkno1t09jp2m936 foreign key (REV) references REVINFO (REV)
六月 26, 2017 10:14:23 下午 org.hibernate.tool.schema.internal.SchemaCreatorImpl applyImportSources
INFO: HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl@56ba8773'
Hibernate: insert into PERSON (ACCOUNT, NAME, BIRTH) values (?, ?, ?)
Hibernate: insert into PERSON (ACCOUNT, NAME, BIRTH) values (?, ?, ?)
Hibernate: insert into REVINFO (REVTSTMP) values (?)
Hibernate: insert into PERSON_AUD (REVTYPE, ACCOUNT, NAME, BIRTH, ID, REV) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into PERSON_AUD (REVTYPE, ACCOUNT, NAME, BIRTH, ID, REV) values (?, ?, ?, ?, ?, ?)
六月 26, 2017 10:14:23 下午 org.hibernate.hql.internal.QueryTranslatorFactoryInitiator initiateService
INFO: HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select person0_.ID as ID1_0_, person0_.ACCOUNT as ACCOUNT2_0_, person0_.NAME as NAME3_0_, person0_.BIRTH as BIRTH4_0_ from PERSON person0_
------------------------------------------------------------------------------------------------------------------
INITIAL DATA : 
Person [id=1, account=tony, name=Tony Stark, birth=2017-06-26 22:14:23.0]
Person [id=2, account=hulk, name=Banner, birth=2017-06-26 22:14:24.0]
------------------------------------------------------------------------------------------------------------------
Hibernate: select person0_.ID as ID1_0_0_, person0_.ACCOUNT as ACCOUNT2_0_0_, person0_.NAME as NAME3_0_0_, person0_.BIRTH as BIRTH4_0_0_ from PERSON person0_ where person0_.ID=?
Hibernate: update PERSON set ACCOUNT=?, NAME=?, BIRTH=? where ID=?
Hibernate: insert into REVINFO (REVTSTMP) values (?)
Hibernate: insert into PERSON_AUD (REVTYPE, ACCOUNT, NAME, BIRTH, ID, REV) values (?, ?, ?, ?, ?, ?)
Hibernate: select person0_.ID as ID1_0_0_, person0_.ACCOUNT as ACCOUNT2_0_0_, person0_.NAME as NAME3_0_0_, person0_.BIRTH as BIRTH4_0_0_ from PERSON person0_ where person0_.ID=?
------------------------------------------------------------------------------------------------------------------
UPDATED DATA : Person [id=2, account=hulk, name=Bruce Banner, birth=2017-06-26 22:14:24.0]
------------------------------------------------------------------------------------------------------------------
Hibernate: select person_aud0_.ID as ID1_1_, person_aud0_.REV as REV2_1_, person_aud0_.REVTYPE as REVTYPE3_1_, person_aud0_.ACCOUNT as ACCOUNT4_1_, person_aud0_.NAME as NAME5_1_, person_aud0_.BIRTH as BIRTH6_1_ from PERSON_AUD person_aud0_ where person_aud0_.REV=(select max(person_aud1_.REV) from PERSON_AUD person_aud1_ where person_aud1_.REV<=? and person_aud0_.ID=person_aud1_.ID) and person_aud0_.REVTYPE<>? and person_aud0_.ID=?
Hibernate: select person_aud0_.ID as ID1_1_, person_aud0_.REV as REV2_1_, person_aud0_.REVTYPE as REVTYPE3_1_, person_aud0_.ACCOUNT as ACCOUNT4_1_, person_aud0_.NAME as NAME5_1_, person_aud0_.BIRTH as BIRTH6_1_ from PERSON_AUD person_aud0_ where person_aud0_.REV=(select max(person_aud1_.REV) from PERSON_AUD person_aud1_ where person_aud1_.REV<=? and person_aud0_.ID=person_aud1_.ID) and person_aud0_.REVTYPE<>? and person_aud0_.ID=?
------------------------------------------------------------------------------------------------------------------
First Version : Person [id=2, account=hulk, name=Banner, birth=2017-06-26 22:14:24.0]
Second Version : Person [id=2, account=hulk, name=Bruce Banner, birth=2017-06-26 22:14:24.0]
------------------------------------------------------------------------------------------------------------------
六月 26, 2017 10:14:24 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH10001008: Cleaning up connection pool [jdbc:mysql://localhost:3306/test]

org.hibernate.envers.AuditReaderfind 方法用于查询实体对象的历史数据,第一个参数是实体类,第二个参数为实体类对象的唯一标识,第三个参数是版本号,从1开始。

Envers 自动生成的数据库审计表及记录如下:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
对于将 P6Spy 集成到 Spring Boot 项目中,你可以按照以下步骤进行操作: 1. 在项目的 Maven 或 Gradle 构建文件中,添加 P6Spy 依赖。你可以在 Maven 中的 pom.xml 文件中添加以下内容: ```xml <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency> ``` 2. 在 Spring Boot 的配置文件(application.properties 或 application.yml)中,配置数据库连接信息。 3. 创建一个 `p6spy.properties` 文件,并将其放置在项目的 `src/main/resources` 目录下。在该文件中,可以配置 P6Spy 的相关属性,例如: ``` modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory logMessageFormat=com.p6spy.engine.spy.option.P6MessageFormat outageDetection=true jdbcUrl=jdbc:p6spy:mysql://localhost:3306/db_name driverlist=org.mariadb.jdbc.Driver ``` 注意替换 `jdbcUrl` 中的 `mysql://localhost:3306/db_name` 为你的数据库连接信息。 4. 在 Spring Boot 的配置文件中,启用 P6Spy。如果是使用 application.properties 配置文件,添加以下配置: ``` spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true spring.jpa.properties.hibernate.listeners.envers.autoRegister=false spring.jpa.properties.hibernate.jdbc.use_get_generated_keys=true spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect spring.jpa.show-sql=false spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/db_name ``` 其中,注意替换 `spring.datasource.url` 中的 `mysql://localhost:3306/db_name` 为你的数据库连接信息。 这样,当你的 Spring Boot 应用程序启动时,P6Spy 将会拦截并打印出所有的 SQL 语句和参数。 希望这些步骤能帮助到你!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又言又语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值