12

We need to audit the existing table using envers. we don't have hibernate.xml instead of we are using application-context.xml. And we are creating schema through "liquibase-changeset", then how do I create through annotations like @Entity and @Audited.

How do I solve this issue?

I have added hibernate configuration likes

<property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
                <prop key="hibernate.ejb.event.post-insert">org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener</prop>
                <prop key="hibernate.ejb.event.post-update">org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener</prop>
                <prop key="hibernate.ejb.event.post-delete">org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener</prop>
                <prop key="hibernate.ejb.event.pre-collection-update">org.hibernate.envers.event.AuditEventListener</prop>
                <!-- <prop key="hibernate.ejb.event.pre-collection-remove">org.hibernate.envers.event.AuditEventListener</prop>
                <prop key="hibernate.ejb.event.post-collection-recreate">org.hibernate.envers.event.AuditEventListener</prop> -->
                <prop key="org.hibernate.envers.revision_field_name">REV</prop>
                <prop key="org.hibernate.envers.revision_type_field_name">REVTYPE</prop>
                <prop key="org.hibernate.envers.auditTablePrefix"></prop>
                <prop key="org.hibernate.envers.auditTableSuffix">_HISTORY</prop>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>

Added @Audited annotation in my domain class

@Entity
@Audited
@Table(name="user")
public class User implements Serializable {

But this configuration deleted my existing tables

e.g

Mydatabase
-----------

user
product
order_details
user_role
login

I have 5 tables in my database. After running my application it displays 3 tables. Instead of creating "audit" table, it deletes the existing table.

 Mydatabase
  -----------

  user
  product
  order_details

How to create audit(_HISTORY) table without touching existing tables???

SST
  • 2,054
  • 5
  • 35
  • 65

4 Answers4

3

In the Liquibase changeset define the audit table definition like you would for any other table.

Skip the hibernate.hbm2ddl.auto property in spring-hibernate ocnfiguration.That will instruct hibernate to not do anything to the schema.

Keeping rest of your configuartion as it is, this should work.

Just ensure the audit tables names in schema and in configuration match.

Link to doc detailing how its done in case schema is generated using ant

Rohit
  • 2,132
  • 1
  • 15
  • 24
  • Agreed. Don't use hbm2ddl for anything except maybe tests. If you start off with an empty schema, and have Envers enabled properly, hbm2ddl.auto=create should generate the audit tables. This works for me, but I'm using JPA persistence.xml (a separate one for testing). I take the generated DDL and create Liquibase change sets from it for the real database. – Joshua Davis Apr 05 '15 at 17:29
3

I was facing the same issue, to resolve it, I followed the next steps:

  1. change :

    <prop key="hibernate.hbm2ddl.auto">create</prop>
    

to:

    <prop key="hibernate.hbm2ddl.auto">update</prop>
  1. If you work with ENVERS Hibernet-envers 3.5.5 or + you should have this configuration in your application-context:

    <property name="eventListeners">
    <map>
    <entry key="post-insert" >
    <bean class="org.hibernate.envers.event.AuditEventListener" />
    </entry>
    <entry key="post-update">
    <bean class="org.hibernate.envers.event.AuditEventListener" />
    </entry>
    <entry key="post-delete">
    <bean class="org.hibernate.envers.event.AuditEventListener" />
    </entry>
    <entry key="pre-collection-update">
    <bean class="org.hibernate.envers.event.AuditEventListener" />
    </entry>
    <entry key="pre-collection-remove">
    <bean class="org.hibernate.envers.event.AuditEventListener" />
    </entry>
    <entry key="post-collection-recreate">
    <bean class="org.hibernate.envers.event.AuditEventListener" />
    </entry>  
    </map>
    </property>
    
  2. You have to define a revision entity like this one:

    @Entity    
    @Table(name = "MY_REVINFO")    
    @RevisionEntity(MyRevisionListener.class)//@see next class   
    public class MyRevisionEntity {            
        private static final long serialVersionUID =1L;
    
        @Id
        @GeneratedValue
        @RevisionNumber
        private int id;
    
        @RevisionTimestamp
        private long timestamp;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        @Transient
        public Date getRevisionDate() {
            return new Date(timestamp);
        }
    
        @Column(name = "USER_NAME")
        private String userName;
    
        @Column(name = "DATE_OPER")
        private Date dateOperation;
    
        public long getTimestamp() {
            return timestamp;
        }
    
        public void setTimestamp(long timestamp) {
            this.timestamp = timestamp;
        }
    
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof DefaultRevisionEntity)) return false;
    
            DefaultRevisionEntity that = (DefaultRevisionEntity) o;
    
            if (id != that.getId()) return false;
            if (timestamp != that.getTimestamp()) return false;
    
            return true;
        }
    
        public int hashCode() {
            int result;
            result = id;
            result = 31 * result + (int) (timestamp ^ (timestamp >>> 32));
            return result;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public Date getDateOperation() {
            return dateOperation;
        }
    
        public void setDateOperation(Date dateOperation) {
            this.dateOperation = dateOperation;
        }
    
        public String toString() {
            return "DefaultRevisionEntity(id = " + id + ", revisionDate = " + DateFormat.getDateTimeInstance().format(getRevisionDate()) + ")";
        }
    }
    
  3. Add the mapping of this new entity in your application-context.xml as :

    <value>mypackage.MyRevisionEntity</value>
    
  4. Create the listener (it's very helpfull if you want to save the user name and the operation time):

    public class MyRevisionListener implements RevisionListener {       
    public void newRevision(Object revisionEntity) {
    
        MyRevisionEntity revision = (MyRevisionEntity) revisionEntity;
    
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
    
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        String userName="---";
        if (userDetails != null) {
            userName=userDetails.getUsername();
        } else {
            userName="UNKNOWN";
        }
    
        revision.setUserName(userName);
        revision.setDateOperation(new Date(revision.getTimestamp()));
    }
    }
    
  5. Clean, install and run your application.

  6. If the problem persists try to upgrade your Envers version (Hibrenate-envers and Hibernate-core)

Hope this help.

jmj
  • 649
  • 8
  • 17
1

Try changing the DDL strategy from:

<prop key="hibernate.hbm2ddl.auto">create</prop>

to:

<prop key="hibernate.hbm2ddl.auto">update</prop>

The update DDL generation strategy shouldn't delete any existing table.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • Thanks for your reply!!! I have changed like this "create" to "update" but still the "Audit" table was not created... :( How to create the audit table?? hibernate version: 3.6.3.Final, hibernate-envers version: 3.6.3.Final What do I missed? – SST Jan 19 '15 at 10:16
  • try to add EnversIntegrator.AUTO_REGISTER to false – Nassim MOUALEK Jan 20 '15 at 11:19
  • Where should I add this "EnversIntegrator.AUTO_REGISTER" set to false? my configuration file doesn't have this line... – SST Jan 20 '15 at 12:14
  • you must hibernate.listeners.envers.autoRegister to false with other properties – Nassim MOUALEK Jan 20 '15 at 13:05
  • Please read https://docs.jboss.org/hibernate/core/4.2/devguide/en-US/html/ch15.html 15.8. Conditional auditing – Nassim MOUALEK Jan 20 '15 at 13:09
  • I have to disagree with this kind of answer. It is not a good idea to rely on hbm2ddl for anything but testing and development. A schema management tool like Liquibase should be used for the real schema. It does does generate audit tables for envers, and I didn't have to do anything special. I use the ouptut of hbm2ddl in development to look at the schema and I create change sets in liquibase from that. – Joshua Davis Apr 05 '15 at 17:33
0

I'm working on a project using JPA with Hibernate implementation and we managed to make envers work without many problems using Spring context xml based configuration only (we neither use persistence.xml).

Our configuration is bassed on Spring JPA support but it's possible that you can find a similar solution:

    <bean id="projectEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
      </bean>
    </property>
    <property name="packagesToScan">
      <list>
        <value>com.teimas.myproject.bo</value>
        <value>com.teimas.myproject.bo.commons</value>
        <value>com.teimas.myproject.bo.util</value>
      </list>
    </property>
    <property name="persistenceUnitName" value="projectPU" />
    <property name="jtaDataSource" ref="projectDataSourceTarget" />
    <!-- Other ptops for hibernate config -->
    <property name="jpaProperties" ref="jpaHibernateProperties" />
  </bean>
  <util:properties id="jpaHibernateProperties">
    <prop key="hibernate.transaction.jta.platform">
      org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform
    </prop>
    <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
    <!-- validate | update | create | create-drop -->
    <prop key="hibernate.hbm2ddl.auto">validate</prop>
    <prop key="hibernate.show_sql">false</prop>
    <prop key="hibernate.format_sql">false</prop>
    <prop key="javax.persistence.transactionType">JTA</prop>
    <prop key="javax.persistence.validation.mode">AUTO</prop>
  </util:properties>

The key is that we use as JPA provider a hibernate object: org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter and we add packagesToScan property to tell hibernate to scan for annotations in those packages. So Hibernate find Envers and Validation anotations, and all work fine.

Hope this helps.

alexbt
  • 16,415
  • 6
  • 78
  • 87
Ricardo Vila
  • 1,626
  • 1
  • 18
  • 34