Reputation: 2144
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???
Upvotes: 12
Views: 11346
Reputation: 1632
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.
Upvotes: 0
Reputation: 649
I was facing the same issue, to resolve it, I followed the next steps:
change :
<prop key="hibernate.hbm2ddl.auto">create</prop>
to:
<prop key="hibernate.hbm2ddl.auto">update</prop>
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>
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()) + ")";
}
}
Add the mapping of this new entity in your application-context.xml as :
<value>mypackage.MyRevisionEntity</value>
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()));
}
}
Clean, install and run your application.
If the problem persists try to upgrade your Envers version (Hibrenate-envers and Hibernate-core)
Hope this help.
Upvotes: 3
Reputation: 2152
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
Upvotes: 3
Reputation: 153780
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.
Upvotes: 1