-
1. Re: adding more information to revision entity
vyacheslav86 Dec 15, 2011 3:42 AM (in response to vyacheslav86)I need at least functionalitity of https://hibernate.onjira.com/browse/HHH-5580
but indeed i need to access entityId(ids) or better the object(objects) saved itself in a listener to build and save set of properties changes of entity in a table. Instead of quering for previous revision all the time.
How to get access to saved entity data? Will this possibility be implemented?
And one more question about REVCHANGES table?
REV ENTITYNAME
2
----------------------------------------
3
54 org.hibernate.envers.demo.Person
4
54 org.hibernate.envers.demo.Address
What if we save 2-10 Persons entities saved in one transaction? How will REVCHANGES look like?
54 org.hibernate.envers.demo.Person
54 org.hibernate.envers.demo.Person
54 org.hibernate.envers.demo.Person
54 org.hibernate.envers.demo.Person
54 org.hibernate.envers.demo.Person
54 org.hibernate.envers.demo.Person
54 org.hibernate.envers.demo.Person
54 org.hibernate.envers.demo.Person
?
-
2. Re: adding more information to revision entity
adamw Dec 17, 2011 7:02 AM (in response to vyacheslav86)Well you can't get that info using the current public APIs. But why would you need this functionality?
As in the issue you linked (which is already implemented and released), you can store the names of modified entities. Then you will have to query for the objects changed in each entity type separately anyway. So I don't see what a list of ids would additionaly give you?
Adam
-
3. Re: adding more information to revision entity
vyacheslav86 Dec 19, 2011 5:56 AM (in response to adamw)It is not about only id of entities.
It is about perfomance issue and goal of getting list of property changes for each revision of object. Now for this i need to 1) query revision, +2) query previous revision 3) generate changeset in memory for each object change. +4) apply order by. I want to store property changes(from value, to value) for each object changed in revision, but now i need to use hibernate interceptor to store them in db simultaneously with Envers audit framework which seems strange.
Imho auditing is about "property changes" so it would be nice to have at least previous revision linked to revision somehow without additional query to database. It is about number of queries to get info now, to make joins of tables in database instead of hibernate.
I also received access to both entityName and entityId using EntityTrackingRevisionListener.
And now using additional queries to database to build property changes set.
Storing ids of entities won't help much for join here, but i need to store group entity name and group entity id. I have graph of objects with root object calles group entity. When looking for root object property changes i want to see list of all changes not only in object but in @OneToOne relations to the object, so i store groupObjectName and groupObjectId in EntityChange and use hibernate query to find changes of the whole group.
@Entity
@Table(name = "ENTITY_CHANGED_IN_REVISION")
public class EntityChange {
@Id
@NotNull
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name="GROUP_NAME")
@Size(max = 50)
private String groupName;
@Column(name="GROUP_ENTITY_ID")
@Size(max = 50)
private String groupEntityId;
@Column(name="ENTITY_ID")
@Size(max = 50)
private String entityId;
@Column(name="ENTITY_NAME")
private String entityName;
@Column(name="REVISION_TYPE")
@Size(min = 0, max = 2)
private Integer revisionType;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "REV_ID", updatable = false)
private Revision revision;
// @OneToMany(fetch=FetchType.LAZY, mappedBy="logRecord", cascade = {CascadeType.ALL}, orphanRemoval = true)
@Transient
private List<AuditLogChangeset> propertyChangesSet = new ArrayList<AuditLogChangeset>(0);
-
4. Re: adding more information to revision entity
stevemac Dec 21, 2011 3:12 AM (in response to vyacheslav86)I'm trying to do the same thing.. Envers is really great at showing me the revisions of a given entity, but it doesn't easily let me know what is changed in each revision.
I have used the EntityTrackingRevisionListener to partially get my solution, while now I can track the table and ID of the change, I really need to intercept the actual entity so I can log other information to make my Revision reporting much faster and more reliable.
I have attempted to override some of the logic inside the Audit processing listeners, but there are no simple methods to override, or the elements are final so is hard to replace.
Any suggestions on how to achieve this outcome?
-
5. Re: adding more information to revision entity
vyacheslav86 Dec 21, 2011 3:35 AM (in response to stevemac)i'm quering for entity by entityName-entityClass and entityId now.
package ru.csbi.registry.services.impl.envers;
import java.io.Serializable;
import org.hibernate.envers.EntityTrackingRevisionListener;
import org.hibernate.envers.RevisionType;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import ru.csbi.registry.domain.envers.Revision;
import ru.csbi.registry.domain.envers.EntityChange;
import ru.csbi.registry.utils.PersistenceManagerHibernate;
import ru.csbi.registry.utils.audit.Auditable;
@Service
public class CustomRevisionListener implements EntityTrackingRevisionListener, ApplicationContextAware {
private static ApplicationContext applicationContext;
private PersistenceManagerHibernate persistenceManagerHibernate;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
CustomRevisionListener.applicationContext = applicationContext;
}
public PersistenceManagerHibernate getBean() {
if (persistenceManagerHibernate == null) {
persistenceManagerHibernate = applicationContext.getBean(PersistenceManagerHibernate.class);
}
return persistenceManagerHibernate;
}
public void newRevision(Object revisionEntity) {
Revision revision = (Revision) revisionEntity;
revision.setUsername(getUsername());
}
public String getUsername() {
final SecurityContext context = SecurityContextHolder.getContext();
if (context != null) {
if (context.getAuthentication() != null) {
return context.getAuthentication().getName();
} else {
return "anonymous";
}
}
return "anonymous";
}
@Override
public void entityChanged(@SuppressWarnings("rawtypes") Class entityClass, String entityName, Serializable entityId, RevisionType revisionType, Object revisionEntity) {
Revision revision = (Revision) revisionEntity;
EntityChange entityChangedInRevision = create(entityClass, entityName, entityId, revisionType, revision);
getBean().save(entityChangedInRevision);
revision.getEntitiesChangedInRevision().add(entityChangedInRevision);
getBean().save(revision);
}
@SuppressWarnings("unchecked")
public EntityChange create(@SuppressWarnings("rawtypes") Class entityClass, String entityName, Serializable entityId, RevisionType revisionType, Revision revision) {
EntityChange entityChange = new EntityChange();
entityChange.setEntityId(entityId.toString());
entityChange.setEntityName(entityName);
entityChange.setRevisionType((int)revisionType.getRepresentation());
entityChange.setRevision(revision);
// query here
Auditable auditable = null;
if (entityId instanceof Long) {
auditable = (Auditable) getBean().findById(entityClass, (Long)entityId);
} else {
auditable = (Auditable) getBean().findByPk(entityClass, entityId);
}
entityChange.setGroupName(auditable.getAuditGroupName());
entityChange.setGroupEntityId(auditable.getAuditGroupId());
return entityChange;
}
}
-
6. Re: adding more information to revision entity
stevemac Dec 21, 2011 7:04 AM (in response to vyacheslav86)I agree that would work, but it seems silly to re-query the entity when it actually exists only 2 stack hops up from the entityChanged method..
I will try and work on some simple changes to the current Audit classes that will allow overriding of the current methods to support what I think would be required.
-
7. Re: adding more information to revision entity
adamw Dec 21, 2011 10:23 AM (in response to vyacheslav86)Ah, the problem of "entity groups" is a well-known one.
Well, I can only say Envers is far from being complete
Adam
-
8. Re: adding more information to revision entity
adamw Dec 21, 2011 10:24 AM (in response to stevemac)If you have some ideas on making Envers more flexible, let me know - plus a pull request will make it much faster to introduce
Adam
-
9. Re: adding more information to revision entity
stevemac Dec 21, 2011 10:36 PM (in response to adamw)I've forked the current codebase and have started looking at what mods I would like to make to get the actual entity, or at least the changed properties during the audit event.
But when I attempt to run my application against this codebase in Eclipse I get:
java.lang.ExceptionInInitializerError
at org.hibernate.envers.configuration.EntitiesConfigurator.configure(EntitiesConfigurator.java:69)
at org.hibernate.envers.configuration.AuditConfiguration.<init>(AuditConfiguration.java:103)
at org.hibernate.envers.configuration.AuditConfiguration.getFor(AuditConfiguration.java:135)
at org.hibernate.envers.event.EnversIntegrator.integrate(EnversIntegrator.java:63)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:295)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1775)
at au.com.clarity.dbstate.CompanyConnectionManager.initSessionFactory(CompanyConnectionManager.java:129)
at au.com.clarity.dbstate.CompanyConnectionManager.<init>(CompanyConnectionManager.java:45)
at au.com.clarity.dbstate.impl.BaseDBManagementProvider.buildCompanyDataSource(BaseDBManagementProvider.java:25)
at au.com.clarity.dbstate.CompanyDatabaseManager.getCompanyConnectionManager(CompanyDatabaseManager.java:189)
at au.com.clarity.dbstate.CompanyDatabaseManager.sessionFactory(CompanyDatabaseManager.java:324)
at au.com.clarity.dbstate.CompanyDatabaseManager.sessionFactory(CompanyDatabaseManager.java:311)
at au.com.clarity.data.dao.HibernateUtil.getCurrentSession(HibernateUtil.java:30)
at au.com.clarity.data.test.transaction.TransactionDocumentVoidTest.setup(TransactionDocumentVoidTest.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.IllegalArgumentException: Invalid logger interface org.hibernate.envers.internal.EnversMessageLogger (implementation not found)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2250)
at org.jboss.logging.Logger.getMessageLogger(Logger.java:2214)
at org.hibernate.envers.configuration.ClassesAuditingData.<clinit>(ClassesAuditingData.java:22)
... 30 more
I've googled but can't find anything that points me in the right direction of why this is happening.
-
10. Re: adding more information to revision entity
stevemac Dec 22, 2011 2:21 AM (in response to stevemac)I've hacked together something that works for me, but I know it probably breaks some rules of API changes.
I wanted to use this code to start a discussion around this sort of change.
https://github.com/stevemac007/hibernate-core/commit/7cdb4c829f28c5b029a3d43f50a54d4c89fc9665
I'm not sure if this forum or somewhere else is the best for this review, please let me know where (if anywhere) is more appropriate.
-
11. Re: adding more information to revision entity
adamw Dec 23, 2011 7:36 AM (in response to stevemac)Did you build Hibernate with the provided Gradle script? If so, you shuld get the correct logger classes.
I'm using Intellij, so I won't help much with Eclipse
Adam
-
12. Re: adding more information to revision entity
vyacheslav86 Dec 26, 2011 7:20 AM (in response to stevemac)I actually need that functionality too. Can you create hibernate-envers issue for that with this patch in hibernate jira? https://hibernate.onjira.com
-
13. Re: adding more information to revision entity
stevemac Jan 3, 2012 12:52 AM (in response to adamw)I did get it compiling, and have been succesfully using my own patched version built from the supplied github commit.
How can we proceed from here? I still don't feel this code is ready to be supplied as a patch via JIRA, does anyone have any comments on how they would like to see this change.
My reasoning for this being not ready is that there is an API breaking change in CustomTrackingRevisionListener
I could refactor this to a new listener, would that be suitable?
-
14. Re: adding more information to revision entity
adamw Jan 3, 2012 9:21 AM (in response to stevemac)It's fine to change the API at "big enough" versions. But what should be done here in fact is rather wrapping all these parameters into an object, so such problems would be avoided in the future (adding more fields to an object shouldn't be a problem).
Maybe we could also then have one listener, but I'm not entirerly sure right now.
Adam