DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Implement Hibernate Second-Level Cache With NCache
  • Providing Enum Consistency Between Application and Data
  • Non-blocking Database Migrations
  • Building a Database Written in Node.js From the Ground Up

Trending

  • AI’s Role in Everyday Development
  • Unmasking Entity-Based Data Masking: Best Practices 2025
  • AI-Based Threat Detection in Cloud Security
  • How Trustworthy Is Big Data?
  1. DZone
  2. Coding
  3. Java
  4. Hibernate Envers: Simple Implementations (Part 1)

Hibernate Envers: Simple Implementations (Part 1)

Look at an introduction to Hibernate Envers and see a simple implementation.

By 
Sanjoy Kumer Deb user avatar
Sanjoy Kumer Deb
DZone Core CORE ·
May. 23, 19 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
28.7K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

Auditing is an important part of software applications. Almost every business domain requires an audit log to manage the changes of acquired data. More than that, auditing is also required to keep applications safe from fraudulent and unethical access. Many applications also check the changelog of data for their internal processes. Many Java-based software implement triggers on the database layer for auditing, but Hibernate gives a more convenient way to implement auditing.

Image title

Hibernate Envers: Features

Hibernate Envers is a framework for auditing. Though Hibernate is an ORM technology, auditing tasks based on Hibernate entities means changes on the entity is audited and saved on the database. Auditing of all mappings is defined by the JPA specification. Revision of each entity log is saved by Hibernate Envers. Hibernate Envers gives the way to read historical data log. 

Implementation

It's built on top of Hibernate and Hibernate JPA. As Hibernate is an ORM technology and Hibernate Envers also entity based, we must focus on the entity. As we are using Hibernate Envers with a spring maven project, we have to specify this library on pom.xml file. Others Spring and Hibernate jars are specified in pom.xml. 

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-envers</artifactId>
            <version>5.4.0.Final</version>
        </dependency>

An entity is an object that represents the tables of a database. And Envers is using Hibernate's event system. Entity transitions are intercepted by the Hibernate audit system and audits them afterward. So, we will create two entities for our implementation; Employee entity and Department entity. 

Employee Entity:

@Entity
@Audited
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String name;
    String address;
    String email;
    @ManyToOne
    Department department;

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    @Version
    private Integer version;

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    @CreatedBy
    private String createdBy;

    public String getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    @LastModifiedBy
    private String modifiedBy;

    public String getModifiedBy() {
        return modifiedBy;
    }

    public void setModifiedBy(String modifiedBy) {
        this.modifiedBy = modifiedBy;
    }
}

Department Entity:

@Entity
@Audited
public class Department {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String name;
    String responsibility;
    @LastModifiedBy
    private String modifiedBy;
    @CreatedBy
    private String createdBy;
    @Version
    private Integer version;
    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getResponsibility() {
        return responsibility;
    }

    public void setResponsibility(String responsibility) {
        this.responsibility = responsibility;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    public String getModifiedBy() {
        return modifiedBy;
    }

    public void setModifiedBy(String modifiedBy) {
        this.modifiedBy = modifiedBy;
    }
}

Here, we just added an @Audited annotation at the top of the entity, which will identify the entities to be audited or not. Though we are using a spring maven project with a java-based configuration, we have to define audit-related properties on the application config file. 

@Configuration
@ComponentScan(basePackages = {"com.sanju.envers","com.sanju.envers.repository"})
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "emf",basePackages = {"com.sanju.envers.repository"})
@EnableAsync
@EnableJpaAuditing
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.mariadb.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/envers_practice?autoReconnect=true");
        dataSource.setUsername("root");
        dataSource.setPassword("");
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean emf(){
        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setDatabasePlatform("org.hibernate.dialect.MariaDB10Dialect"); //you can change this if you have a different DB
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();

        Properties properties = new Properties();
        properties.put("hibernate.dialect", MariaDB10Dialect.class);
        properties.put(AvailableSettings.HBM2DDL_AUTO, SchemaAutoTooling.UPDATE.name().toLowerCase());
        properties.put(AvailableSettings.SHOW_SQL,"true");
        properties.put("org.hibernate.envers.audit_table_suffix",
                "_aud");
        properties.put("hibernate.listeners.envers.autoRegister",true);
        properties.put("hibernate.envers.autoRegisterListeners",true);
        factory.setJpaProperties(properties);
        factory.setJpaVendorAdapter(adapter);
        factory.setDataSource(dataSource());
        factory.setPackagesToScan("com.sanju.envers");
        factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
        factory.setValidationMode(ValidationMode.NONE);
        return factory;
    }

    @Bean
    public PlatformTransactionManager transactionManager()
    {
        return new JpaTransactionManager(
                this.emf().getObject());
    }

    @Bean
    public TaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(6);
        executor.setThreadNamePrefix("default_task_executor_thread");
        executor.initialize();
        return executor;
    }

}

Here, we annotated this class with an @EnableJpaAuditing for enabling JPA Hibernate auditing and defined datasources, transactional properties, and threadpool properties. We configured EntityManager, and here, we define some Hibernate Envers properties.

properties.put("org.hibernate.envers.audit_table_suffix","_aud");
properties.put("hibernate.listeners.envers.autoRegister",true);
properties.put("hibernate.envers.autoRegisterListeners",true);

The first one defines that when we run this project, it will create or update an audit table for auditing with suffix _aud. And the last two properties define that the Envers listener will be used by a default listener or custom listener.  Revinfo default table contains two properties (rev, revtstmp), which will define the revision of the entity in audit tables. 

Please clone the project from here and change the properties hibernate.listeners.envers.autoRegister and hibernate.envers.autoRegisterListeners to true at AppConfig.java. Resolve the maven dependency and then run Application.java. Before that, change your database configuration to AppConfig.java.

@Beanpublic DataSource dataSource() {    
  DriverManagerDataSource dataSource = new DriverManagerDataSource();
  dataSource.setDriverClassName("org.mariadb.jdbc.Driver");  
  dataSource.setUrl("jdbc:mysql://localhost:3306/envers_practice?autoReconnect=true");   
  dataSource.setUsername("root");    
  dataSource.setPassword("");    
  return dataSource;
}

I am using MariaDB. You can choose your favourite database, then you have to change driver classes. Also, change on pom.xml.

      <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</artifactId>
            <version>2.4.0</version>
        </dependency>

Now create an empty database named by envers_practice. Now run Application.java file. Go to the database and you can see that the audit table and revision table are created.

Now we can think about the custom listener. Sometimes, we need to make some custom operations during auditing. Then, we need to add a Custom listener to the application. Before that, we will change the properties hibernate.listeners.envers.autoRegister and hibernate.envers.autoRegisterListeners to false at AppConfig.java. And then look at the Custom listener configuration:

@Component
public class EnversListenerConfiguration {

    @Autowired
    EntityManagerFactory entityManagerFactory;

    @PostConstruct
    protected void init() {
        SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
        EnversService enversService = sessionFactory.getServiceRegistry().getService(EnversService.class);

        registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(new CustomEnversPreInsertEventListenerImpl());
        registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(new CustomEnversPreUpdateEventListenerImpl(enversService));
        registry.getEventListenerGroup(EventType.POST_UPDATE).appendListener(new CustomPostUpdateEventListener(enversService));
        registry.getEventListenerGroup(EventType.PRE_COLLECTION_UPDATE).appendListener(new CustomPreUpdateCollectionListener(enversService));
        registry.getEventListenerGroup(EventType.POST_COLLECTION_UPDATE).appendListener(new CustomPostUpdateCollectionListener());
    }
}

Then the program can perform its necessary operations during different auditing events.

Read Audited Data

Hibernate Envers has some interfaces to read audit data. AuditReader reads audit logs of entities. We get AuditReader from AuditReaderFactory by using entitymanager. AuditQuery is also an interface that can read all audited data with revision information for a specific entity. Here, Object[] contains three pieces of audit information. First, the array element contains original tables or entities changes, second, the element of the array contains DefaultRevisionEntity, and the third one contains RevisionType, which means operation type.

   private static <T> void auditDataRead(Class<T> tClass, AnnotationConfigApplicationContext configApplicationContext) {
        System.out.println("Reading auditing data...");
        EntityManagerFactory emf = configApplicationContext.getBean(EntityManagerFactory.class);
        AuditReader auditReader = AuditReaderFactory.get(emf.createEntityManager());

        AuditQuery query = auditReader.createQuery()
                .forRevisionsOfEntity(tClass, false, true);

        List<Object[]> resultList = query.getResultList();

        resultList.forEach(objects -> {
            System.out.println("------------------------------------------------------------------------\n");
            if (Employee.class.equals(tClass)) {
                Employee employeeRev = (Employee) objects[0];
                System.out.println("Employee info  : ");
                System.out.println("Id             : " + employeeRev.getId());
                System.out.println("Name           : " + employeeRev.getName());
                System.out.println("Email          : " + employeeRev.getEmail());
                System.out.println("Address        : " + employeeRev.getAddress());
            } else if (Department.class.equals(tClass)) {
                System.out.println("Department info: ");
                Department department = (Department) objects[0];
                System.out.println("Id             : "+department.getId());
                System.out.println("Responsibility : "+department.getResponsibility());
                System.out.println("Name           : "+department.getName());
            }
            DefaultRevisionEntity revisionEntity = (DefaultRevisionEntity) objects[1];
            System.out.println("Revision       : " + revisionEntity.getId());
            System.out.println("Date           : " + revisionEntity.getRevisionDate());

            RevisionType revisionType = (RevisionType) objects[2];
            System.out.println("Operation      : " + revisionType.name());
        });
    }

This was an overview of Hibernate Envers. I will discuss it more in my next article.

Thanks. You can find the code from this article here.

Hibernate Database Implementation Property (programming) application Data (computing) IT Apache Maven

Published at DZone with permission of Sanjoy Kumer Deb. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Implement Hibernate Second-Level Cache With NCache
  • Providing Enum Consistency Between Application and Data
  • Non-blocking Database Migrations
  • Building a Database Written in Node.js From the Ground Up

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!