Welcome to JiKe DevOps Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
482 views
in Technique[技术] by (71.8m points)

hibernate - How to retrieve the audited revision of relations?

Here is my use case

I have two entities : Personn and Email (a @OneToMany relation). Both of them are audited.

First I create a new Personn, with an Email (=> Both of them have a revision 1), then I modify the Email (=> The Email has a revision 2, but Personn has only a revision 1)

In the web application the end-user has only one view to show the Personn's attributs and also his Emails attributs. In this view I want to show all existing revisions for this Personn. But when I query the audit system, it doesn't show me revision 2 as the Personn has not been modified.

I understand the technical problem, but in the end-user point of view, he wants to see a revision 2 because he modified the personn's email ! He doesn't know (and doesn't have to know) that we deciced to divide these information into 2 Java objects. Of course this problem is not only for Personn-Email relation (I've got a lot of relation between Personn and other objects which are shown in the same view - Adress, Job, Site, Card, and many more)

I thought about 2 solutions:

1- Querying all relations to know if a Revision exist (but I suppose it will generate a big request or multiple requests - I've got a lot of relations).

2- Setting "hibernate.listeners.envers.autoRegister" to false, writing my own EnversIntegrator and event implementations. In event implementations (which override default Envers implementations), I will create a ModWorkUnit for Personn when Email's attributs have been modified (it will not been hard coded of course: perharps a custom annotation like @AuditedPropagation on the personn field). The flaw of this solution is to create a lot a row for Personn even if it was not modified.

What do you think about these solutions ? Do you know a better way to solve that kind of use-case ?

Thanks for your advices.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

Please log in or register to answer this question.

1 Answer

0 votes
by (71.8m points)

I tried to implement the second solution:

  1. First my integrator which adds a new post update listener (RevisionOnCollectionPostUpdateEventListenerImpl)

    public class RevisionOnCollectionUpdateIntegrator implements Integrator {
    private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, RevisionOnCollectionUpdateIntegrator.class.getName());
    
    public static final String REGISTER_ON_UPDATE = "org.hibernate.envers.revision_on_collection_update";
    
    @Override
    public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
    
        final boolean autoRegister = ConfigurationHelper.getBoolean(REGISTER_ON_UPDATE, configuration.getProperties(), true);
        if (!autoRegister) {
            LOG.debug("Skipping 'revision_on_collection_update' listener auto registration");
            return;
        }
    
        EventListenerRegistry listenerRegistry = serviceRegistry.getService(EventListenerRegistry.class);
        listenerRegistry.addDuplicationStrategy(EnversListenerDuplicationStrategy.INSTANCE);
    
        final AuditConfiguration enversConfiguration = AuditConfiguration.getFor(configuration, serviceRegistry.getService(ClassLoaderService.class));
        if (enversConfiguration.getEntCfg().hasAuditedEntities()) {
            listenerRegistry.appendListeners(EventType.POST_UPDATE, new RevisionOnCollectionPostUpdateEventListenerImpl(enversConfiguration));
        }
    }
    
  2. And then the post update listener (which extends):

    public class RevisionOnCollectionPostUpdateEventListenerImpl extends EnversPostUpdateEventListenerImpl {
    protected final void generateBidirectionalWorkUnits(AuditProcess auditProcess, EntityPersister entityPersister, String entityName, Object[] newState,
            Object[] oldState, SessionImplementor session) {
        // Checking if this is enabled in configuration ...
        if (!getAuditConfiguration().getGlobalCfg().isGenerateRevisionsForCollections()) {
            return;
        }
    
        // Checks every property of the entity, if it is an "owned" to-one relation to another entity.
        // If the value of that property changed, and the relation is bi-directional, a new revision
        // for the related entity is generated.
        String[] propertyNames = entityPersister.getPropertyNames();
    
        for (int i = 0; i < propertyNames.length; i++) {
            String propertyName = propertyNames[i];
            RelationDescription relDesc = getAuditConfiguration().getEntCfg().getRelationDescription(entityName, propertyName);
            if (relDesc != null && relDesc.isBidirectional() && relDesc.getRelationType() == RelationType.TO_ONE && relDesc.isInsertable()) {
                // Checking for changes
                Object oldValue = oldState == null ? null : oldState[i];
                Object newValue = newState == null ? null : newState[i];
    
                        // Here is the magic part !!!!!!!!!
                        // The super class verify if old and new value (of the owner value) are equals or not
                        // If different (add or delete) then an audit entry is also added for the owned entity
                        // When commented, an audit row for the owned entity is added when a related entity is updated
            //  if (!Tools.entitiesEqual(session, relDesc.getToEntityName(), oldValue, newValue)) {
                    // We have to generate changes both in the old collection (size decreses) and new collection
                    // (size increases).
                    if (newValue != null) {
                        addCollectionChangeWorkUnit(auditProcess, session, entityName, relDesc, newValue);
                    }
    
                    if (oldValue != null) {
                        addCollectionChangeWorkUnit(auditProcess, session, entityName, relDesc, oldValue);
                    }
            //  }
            }
        }
    }
    

It seems to work but I have to test a little bit more.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to JiKe DevOps Community for programmer and developer-Open, Learning and Share
...