rogodeter
rogodeter

Reputation: 470

Why does Spring Data MongoDB not expose events for update…(…) methods?

It appears that the update for mongoOperations do not trigger the events in AbstractMongoEventListener.

This post indicates that was at least the case in Nov 2014

Is there currently any way to listen to update events like below? This seems to be quite a big omission if it is the case.

MongoTemplate.updateMulti()

Thanks!

Upvotes: 2

Views: 6039

Answers (3)

Majid Asgari-Bidhendi
Majid Asgari-Bidhendi

Reputation: 512

You can listen to database changes, even the changes completely outside your program (MongoDB 4.2 and newer).

(code is in kotlin language. same for java)

@Autowired private lateinit var op: MongoTemplate

@PostConstruct
fun listenOnExternalChanges() {
    Thread {
        op.getCollection("Item").watch().onEach {
            if(it.updateDescription.updatedFields.containsKey("name")) {
                println("name changed on a document: ${it.updateDescription.updatedFields["name"]}")
            }
        }
    }.start()
}

This code only works when replication is enabled. You can enable it even when you have a single node:

Add the following replica set details to mongodb.conf (/etc/mongodb.conf or /usr/local/etc/mongod.conf or C:\Program Files\MongoDB\Server\4.0\bin\mongod.cfg) file

replication:
  replSetName: "local"

Restart mongo service, Then open mongo console and run this command:

rs.initiate()

Upvotes: 1

Arko
Arko

Reputation: 972

I know it's too late to answer this Question, I have the same situation with MongoTemplate.findAndModify method and the reason I needed events is for Auditing purpose. here is what i did.

1.EventPublisher (which is ofc MongoTemplate's methods)

public class CustomMongoTemplate extends MongoTemplate {

    private ApplicationEventPublisher applicationEventPublisher;


    @Autowired
    public void setApplicationEventPublisher(ApplicationEventPublisher
                                                         applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

   //Default Constructor here

    @Override
    public <T> T findAndModify(Query query, Update update, Class<T> entityClass) {
        T result = super.findAndModify(query, update, entityClass);

        //Publishing Custom Event on findAndModify
        if(result!=null && result instanceof Parent)//All of my Domain class extends Parent
            this.applicationEventPublisher.publishEvent(new AfterFindAndModify
                    (this,((Parent)result).getId(),
                            result.getClass().toString())
            );

        return result;
    } }

2.Application Event

public class AfterFindAndModify extends ApplicationEvent {

    private DocumentAuditLog documentAuditLog;

    public AfterFindAndModify(Object source, String documentId,
                            String documentObject) {
        super(source);
        this.documentAuditLog = new DocumentAuditLog(documentId,
                documentObject,new Date(),"UPDATE");
    }

    public DocumentAuditLog getDocumentAuditLog() {
        return documentAuditLog;
    }
}

3.Application Listener

public class FindandUpdateMongoEventListner implements ApplicationListener<AfterFindAndModify> {

    @Autowired
    MongoOperations mongoOperations;

    @Override
    public void onApplicationEvent(AfterFindAndModify event) {
        mongoOperations.save(event.getDocumentAuditLog());
    }
}

and then

@Configuration
@EnableMongoRepositories(basePackages = "my.pkg")
@ComponentScan(basePackages = {"my.pkg"})
public class MongoConfig extends AbstractMongoConfiguration {

    //.....

    @Bean
    public FindandUpdateMongoEventListner findandUpdateMongoEventListner(){
        return new FindandUpdateMongoEventListner();
    }   

}

Upvotes: 3

Oliver Drotbohm
Oliver Drotbohm

Reputation: 83131

This is no oversight. Events are designed around the lifecycle of a domain object or a document at least, which means they usually contain an instance of the domain object you're interested in.

Updates on the other hand are completely handled in the database. So there are no documents or even domain objects handled in MongoTemplate. Consider this basically the same way JPA @EntityListeners are only triggered for entities that are loaded into the persistence context in the first place, but not triggered when a query is executed as the execution of the query is happening in the database.

Upvotes: 5

Related Questions