0

I have written EntityListener using eclipseLink's "DescriptorEventAdapter". I tried almost all variations whatever present online BUT the entity which I am saving from my listener is not getting saved. I suspect something fishy is going on with transaction but didn't get the root cause. Here is the code :

package com.db;

import java.util.Date;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.jpa.JpaEntityManager;
import org.eclipse.persistence.queries.InsertObjectQuery;
import org.eclipse.persistence.queries.UpdateObjectQuery;
import org.eclipse.persistence.sessions.changesets.DirectToFieldChangeRecord;
import org.eclipse.persistence.sessions.changesets.ObjectChangeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class NotificationMessageListener extends DescriptorEventAdapter {

    public static Logger logger = LoggerFactory.getLogger(NotificationMessageListener.class);

    private static final String targetColumn  = "STATUS";

    //private static AuditRepository auditRepo;

    @Autowired 
    private StatusAuditRepository statusAuditRepo;

    @Autowired
    private RuleResultAuditRepository ruleResultRepo;

    @Autowired
    private EntityManagerFactory factory;

    JpaEntityManager entityManager = null;

    @PostConstruct
    public void init() {

        try {
            entityManager = (JpaEntityManager) factory.createEntityManager();
            // Use the entity manager to get a ClassDescriptor for the Entity class
            ClassDescriptor desc = 
                entityManager.getSession().getClassDescriptor(NotificationMessage.class);
            // Add this class as a listener to the class descriptor
            desc.getEventManager().addListener(this);
        } finally {
            if (entityManager != null) {
                // Cleanup the entity manager
                entityManager.close();
            }
        }
    }

    /*@Autowired
    public void setAuditRepo(AuditRepository auditRepo) {
        NotificationMessageListener.auditRepo = auditRepo;
    }*/

    @Transactional(value = TxType.REQUIRES_NEW)
    @Override
    public void postInsert(DescriptorEvent event) {
        logger.info("post insert is called ");
        //NotificationMessage notificationMsg = (NotificationMessage) ((InsertObjectQuery) event.getQuery()).getObject();
        //entityManager.getTransaction().begin();
        NotificationStatusAudit statusAudit = new NotificationStatusAudit();
        statusAudit.setInsertionTime(new Date());
        //statusAudit.setNewVal(notificationMsg.getStatus());
        statusAudit.setNewVal("abc");
        statusAudit.setOldval("asdf");
        statusAudit.setTargetColumnName("from listner");
        //statusAudit.setTargetRecordId(notificationMsg.getId());
        statusAudit.setTargetRecordId(123L);
        statusAudit = statusAuditRepo.save(statusAudit);
        //entityManager.getTransaction().commit();
        //logger.info("Number of records "+statusAuditRepo.count());
        //auditRuleResult(notificationMsg.getMessageCorrelationId() , true);
    }

    @Override
    public void postUpdate(DescriptorEvent event) {
            ObjectChangeSet objectChanges = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
            DirectToFieldChangeRecord statusChanges = (DirectToFieldChangeRecord) objectChanges
                    .getChangesForAttributeNamed("status");
            if (statusChanges != null && !statusChanges.getNewValue().equals(statusChanges.getOldValue())) {
                NotificationStatusAudit statusAudit = new NotificationStatusAudit();
                statusAudit.setInsertionTime(new Date());
                statusAudit.setNewVal("abc");
                statusAudit.setOldval("asdf");
                statusAudit.setTargetColumnName(targetColumn);
                statusAudit.setTargetRecordId((Long) objectChanges.getId());
                statusAudit = statusAuditRepo.save(statusAudit);
        }
    }
}

Here all I have to do is save the record in another (Audit) table when data is getting inserted in one table. My application is spring boot app and am using eclipseLink for persistent. I had to manually register my entity-listener in "PostConstruct" because if it is registered using @EntityListner annotation , spring-data-repos were not getting autowired. Here are my questions :

1) Using EntityListener for my requirement is good approach or should I use direct "save" operations ? 2) I debugged the EntityListener code and method is not initiated a new Transaction even after adding Requires_new. I can see method is not being called $proxy (spring-proxy). I don't understand why ?

Chandan Rai
  • 9,879
  • 2
  • 20
  • 28
rishi
  • 1,792
  • 5
  • 31
  • 63

1 Answers1

1

I am not sure about what you are doing in your @PostConstruct init() method... but I suspect you should be configuring this DescriptorEventAdapter using EclipseLink's DescriptorCustomizer. Here is an example:

public class MessageEventListener extends DescriptorEventAdapter implements DescriptorCustomizer {

    @Override
    public void customize(ClassDescriptor descriptor) {
        descriptor.getEventManager().addListener(this);
    }

    @Override
    public void postUpdate(DescriptorEvent event) {
        ObjectChangeSet objectChanges = ((UpdateObjectQuery) event.getQuery()).getObjectChangeSet();
        //More business logic...
    }
}

@Entity
@Customizer(MessageEventListener.class)
public class Message {
    @Id private long id;
    private String content;
}
Will Dazey
  • 253
  • 2
  • 13