Hibernate Envers目的是根据对实体的设置,提供记录执行数据变更历史的功能(数据变更版本)。它实现原理是通过对Hibernate的操作事件监听并根据基于Annoatation的配置来记录修改数据的内容。
比较entitymanager项目的异同。
主要是实体bean上增加一个@Audited注解。
package org.hibernate.tutorial.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 {
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();
}
}
运行结果如下:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running org.hibernate.tutorial.envers.EnversIllustrationTest
七月 16, 2016 8:04:54 下午 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
七月 16, 2016 8:04:54 下午 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
七月 16, 2016 8:04:54 下午 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
七月 16, 2016 8:04:55 下午 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
name: org.hibernate.tutorial.envers
...]
七月 16, 2016 8:04:55 下午 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.3.11.Final}
七月 16, 2016 8:04:55 下午 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
七月 16, 2016 8:04:55 下午 org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
七月 16, 2016 8:04:55 下午 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
七月 16, 2016 8:04:55 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH000402: Using Hibernate built-in connection pool (not for production use!)
七月 16, 2016 8:04:55 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000401: using driver [org.h2.Driver] at URL [jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE]
七月 16, 2016 8:04:55 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000046: Connection properties: {user=sa}
七月 16, 2016 8:04:55 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH000006: Autocommit mode: false
七月 16, 2016 8:04:55 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
七月 16, 2016 8:04:55 下午 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
七月 16, 2016 8:04:55 下午 org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [3] less than 4
七月 16, 2016 8:04:56 下午 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
七月 16, 2016 8:04:57 下午 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000227: Running hbm2ddl schema export
Hibernate: drop table EVENTS if exists
Hibernate: drop table EVENTS_AUD if exists
Hibernate: drop table REVINFO if exists
Hibernate: create table EVENTS (id bigint not null, EVENT_DATE timestamp, title varchar(255), primary key (id))
Hibernate: create table EVENTS_AUD (id bigint not null, REV integer not null, REVTYPE tinyint, EVENT_DATE timestamp, title varchar(255), primary key (id, REV))
Hibernate: create table REVINFO (REV integer generated by default as identity, REVTSTMP bigint, primary key (REV))
Hibernate: alter table EVENTS_AUD add constraint FK_3hegaqrrpmx0jj0c8qacjtira foreign key (REV) references REVINFO
七月 16, 2016 8:04:57 下午 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Hibernate: select max(id) from EVENTS
Hibernate: insert into EVENTS (EVENT_DATE, title, id) values (?, ?, ?)
Hibernate: insert into EVENTS (EVENT_DATE, title, id) values (?, ?, ?)
Hibernate: insert into REVINFO (REV, REVTSTMP) values (null, ?)
Hibernate: insert into EVENTS_AUD (REVTYPE, EVENT_DATE, title, id, REV) values (?, ?, ?, ?, ?)
Hibernate: insert into EVENTS_AUD (REVTYPE, EVENT_DATE, title, id, REV) values (?, ?, ?, ?, ?)
Hibernate: select event0_.id as id1_0_, event0_.EVENT_DATE as EVENT_DA2_0_, event0_.title as title3_0_ from EVENTS event0_
Event (2016-07-16 20:04:57.655) : Our very first event!
Event (2016-07-16 20:04:57.671) : A follow up event
Hibernate: select event0_.id as id1_0_0_, event0_.EVENT_DATE as EVENT_DA2_0_0_, event0_.title as title3_0_0_ from EVENTS event0_ where event0_.id=?
Hibernate: update EVENTS set EVENT_DATE=?, title=? where id=?
Hibernate: insert into REVINFO (REV, REVTSTMP) values (null, ?)
Hibernate: insert into EVENTS_AUD (REVTYPE, EVENT_DATE, title, id, REV) values (?, ?, ?, ?, ?)
Hibernate: select event0_.id as id1_0_0_, event0_.EVENT_DATE as EVENT_DA2_0_0_, event0_.title as title3_0_0_ from EVENTS event0_ where event0_.id=?
Hibernate: select event_aud0_.id as id1_1_, event_aud0_.REV as REV2_1_, event_aud0_.REVTYPE as REVTYPE3_1_, event_aud0_.EVENT_DATE as EVENT_DA4_1_, event_aud0_.title as title5_1_ from EVENTS_AUD event_aud0_ where event_aud0_.REV=(select max(event_aud1_.REV) from EVENTS_AUD event_aud1_ where event_aud1_.REV<=? and event_aud0_.id=event_aud1_.id) and event_aud0_.REVTYPE<>? and event_aud0_.id=?
Hibernate: select event_aud0_.id as id1_1_, event_aud0_.REV as REV2_1_, event_aud0_.REVTYPE as REVTYPE3_1_, event_aud0_.EVENT_DATE as EVENT_DA4_1_, event_aud0_.title as title5_1_ from EVENTS_AUD event_aud0_ where event_aud0_.REV=(select max(event_aud1_.REV) from EVENTS_AUD event_aud1_ where event_aud1_.REV<=? and event_aud0_.id=event_aud1_.id) and event_aud0_.REVTYPE<>? and event_aud0_.id=?
七月 16, 2016 8:04:57 下午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.112 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0