HHH-11886 - Elaborate Envers documentation and switch to actual source code examples

Provide two examples for the entity type change revision tracking
This commit is contained in:
Vlad Mihalcea 2017-07-28 09:43:30 +03:00
parent fa81aafb28
commit e56ecc24ff
3 changed files with 426 additions and 20 deletions

View File

@ -505,7 +505,7 @@ The method accepts a `persist` parameter indicating whether the revision entity
[[envers-tracking-modified-entities-revchanges]]
=== Tracking entity names modified during revisions
By default entity types that have been changed in each revision are not being tracked.
By default, entity types that have been changed in each revision are not being tracked.
This implies the necessity to query all tables storing audited data in order to retrieve changes made during specified revision.
Envers provides a simple mechanism that creates `REVCHANGES` table which stores entity names of modified persistent objects.
Single record encapsulates the revision identifier (foreign key to `REVINFO` table) and a string value.
@ -516,33 +516,18 @@ Tracking of modified entity names can be enabled in three different ways:
In this case `org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity` will be implicitly used as the revision log entity.
. Create a custom revision entity that extends `org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity` class.
+
[source,java]
[source, JAVA, indent=0]
----
@RevisionEntity
public class ExtendedRevisionEntity extends DefaultTrackingModifiedEntitiesRevisionEntity {
...
}
include::{sourcedir}/EntityTypeChangeAuditDefaultTrackingTest.java[tags=envers-tracking-modified-entities-revchanges-example]
----
+
. Mark an appropriate field of a custom revision entity with `@org.hibernate.envers.ModifiedEntityNames` annotation.
The property is required to be of `Set<String>` type.
+
[source,java]
[source, JAVA, indent=0]
----
@RevisionEntity
public class AnnotatedTrackingRevisionEntity {
...
@ElementCollection
@JoinTable( name = "REVCHANGES", joinColumns = @JoinColumn( name = "REV" ) )
@Column( name = "ENTITYNAME" )
@ModifiedEntityNames
private Set<String> modifiedEntityNames;
...
}
include::{sourcedir}/EntityTypeChangeAuditTest.java[tags=envers-tracking-modified-entities-revchanges-example]
----
+
Users, that have chosen one of the approaches listed above,
can retrieve all entities modified in a specified revision by utilizing API described in <<envers-tracking-modified-entities-queries>>.

View File

@ -0,0 +1,202 @@
/*
* 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>.
*/
package org.hibernate.userguide.envers;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.Audited;
import org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class EntityTypeChangeAuditDefaultTrackingTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Customer.class,
AnnotatedTrackingRevisionEntity.class
};
}
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = new Customer();
customer.setId( 1L );
customer.setFirstName( "John" );
customer.setLastName( "Doe" );
entityManager.persist( customer );
} );
EntityManagerFactory entityManagerFactory = null;
try {
Map settings = buildSettings();
settings.put(
org.hibernate.jpa.AvailableSettings.LOADED_CLASSES,
Arrays.asList(
ApplicationCustomer.class,
AnnotatedTrackingRevisionEntity.class
)
);
settings.put(
AvailableSettings.HBM2DDL_AUTO,
"update"
);
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
).build().unwrap( SessionFactoryImplementor.class );
final EntityManagerFactory emf = entityManagerFactory;
doInJPA( () -> emf, entityManager -> {
ApplicationCustomer customer = new ApplicationCustomer();
customer.setId( 2L );
customer.setFirstName( "John" );
customer.setLastName( "Doe Jr." );
entityManager.persist( customer );
} );
}
finally {
if ( entityManagerFactory != null ) {
entityManagerFactory.close();
}
}
}
@Audited
@Entity(name = "Customer")
public static class Customer {
@Id
private Long id;
private String firstName;
private String lastName;
@Temporal( TemporalType.TIMESTAMP )
@Column(name = "created_on")
@CreationTimestamp
private Date createdOn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
}
@Audited
@Entity(name = "Customer")
public static class ApplicationCustomer {
@Id
private Long id;
private String firstName;
private String lastName;
@Temporal( TemporalType.TIMESTAMP )
@Column(name = "created_on")
@CreationTimestamp
private Date createdOn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
}
//tag::envers-tracking-modified-entities-revchanges-example[]
@Entity(name = "AnnotatedTrackingRevisionEntityListener")
@Table(name = "TRACKING_REV_INFO")
@RevisionEntity
public static class AnnotatedTrackingRevisionEntity
extends DefaultTrackingModifiedEntitiesRevisionEntity {
}
//end::envers-tracking-modified-entities-revchanges-example[]
}

View File

@ -0,0 +1,219 @@
/*
* 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>.
*/
package org.hibernate.userguide.envers;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.envers.Audited;
import org.hibernate.envers.DefaultRevisionEntity;
import org.hibernate.envers.ModifiedEntityNames;
import org.hibernate.envers.RevisionEntity;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class EntityTypeChangeAuditTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Customer.class,
AnnotatedTrackingRevisionEntity.class
};
}
@Test
public void testLifecycle() {
doInJPA( this::entityManagerFactory, entityManager -> {
Customer customer = new Customer();
customer.setId( 1L );
customer.setFirstName( "John" );
customer.setLastName( "Doe" );
entityManager.persist( customer );
} );
EntityManagerFactory entityManagerFactory = null;
try {
Map settings = buildSettings();
settings.put(
org.hibernate.jpa.AvailableSettings.LOADED_CLASSES,
Arrays.asList(
ApplicationCustomer.class,
AnnotatedTrackingRevisionEntity.class
)
);
settings.put(
AvailableSettings.HBM2DDL_AUTO,
"update"
);
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
new TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
settings
).build().unwrap( SessionFactoryImplementor.class );
final EntityManagerFactory emf = entityManagerFactory;
doInJPA( () -> emf, entityManager -> {
ApplicationCustomer customer = new ApplicationCustomer();
customer.setId( 2L );
customer.setFirstName( "John" );
customer.setLastName( "Doe Jr." );
entityManager.persist( customer );
} );
}
finally {
if ( entityManagerFactory != null ) {
entityManagerFactory.close();
}
}
}
@Audited
@Entity(name = "Customer")
public static class Customer {
@Id
private Long id;
private String firstName;
private String lastName;
@Temporal( TemporalType.TIMESTAMP )
@Column(name = "created_on")
@CreationTimestamp
private Date createdOn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
}
@Audited
@Entity(name = "Customer")
public static class ApplicationCustomer {
@Id
private Long id;
private String firstName;
private String lastName;
@Temporal( TemporalType.TIMESTAMP )
@Column(name = "created_on")
@CreationTimestamp
private Date createdOn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
}
//tag::envers-tracking-modified-entities-revchanges-example[]
@Entity(name = "AnnotatedTrackingRevisionEntityListener")
@Table(name = "TRACKING_REV_INFO")
@RevisionEntity
public static class AnnotatedTrackingRevisionEntity extends DefaultRevisionEntity {
@ElementCollection
@JoinTable(
name = "REVCHANGES",
joinColumns = @JoinColumn( name = "REV" )
)
@Column( name = "ENTITYNAME" )
@ModifiedEntityNames
private Set<String> modifiedEntityNames = new HashSet<>();
public Set<String> getModifiedEntityNames() {
return modifiedEntityNames;
}
}
//end::envers-tracking-modified-entities-revchanges-example[]
}