In this tutorial, we will learn how to generate an auto-incrementing sequence id in our Spring WebFlux and Reactive MongoDB project.
First we will create our model class
Models
package com.jkoder.employee.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Document(collection = "employee")
public class Employee {
@Transient
public static final String SEQUENCE_NAME = "employee_sequence";
@Id
long id;
String name;
String role;
}
In the above Employee collection class, we have to give a sequence name for this collection, which is annotated with @Transient. We will have to give a separate sequence name in all our collection classes.
package com.jkoder.employee.model;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@Document(collection = "database_sequences")
public class DatabaseSequence {
@Id
private String id;
private long sequence;
}
The above class is the main collection, which is going to store all the sequence values.
Now we will write a sequence generator class, this class will fetch the respective value from the database_sequences collection and increment the value and return the value to the collection in which we will be storing the value.
package com.jkoder.employee.service;
import java.util.concurrent.ExecutionException;
public interface ISequenceGeneratorService {
long generateSequence(final String sequenceName) throws InterruptedException, ExecutionException;
}
package com.jkoder.employee.service;
import com.jkoder.employee.model.DatabaseSequence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutionException;
import static org.springframework.data.mongodb.core.FindAndModifyOptions.options;
@Service
public class SequenceGeneratorService implements ISequenceGeneratorService{
private static final Logger logger = LoggerFactory.getLogger(SequenceGeneratorService.class);
private ReactiveMongoOperations mongoOperations;
@Autowired
public SequenceGeneratorService(ReactiveMongoOperations mongoOperations) {
this.mongoOperations = mongoOperations;
}
@Override
public long generateSequence(final String sequenceName) throws InterruptedException, ExecutionException {
return mongoOperations.findAndModify(new Query(Criteria.where("_id").is(sequenceName)),
new Update().inc("sequence", 1), options().returnNew(true).upsert(true), DatabaseSequence.class).doOnSuccess(object -> {
logger.debug("databaseSequence is evaluated: {}", object);
}).toFuture().get().getSequence();
}
}
Now a class that identifies an add or update event in the collection model, a class by extending the AbstractMongoEventListener.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.jkoder.employee.listener;
import com.jkoder.employee.model.Employee;
import com.jkoder.employee.service.ISequenceGeneratorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.stereotype.Component;
import java.util.concurrent.ExecutionException;
@Component
public class EmployeeModelListener extends AbstractMongoEventListener<Employee> {
private static final Logger logger = LoggerFactory.getLogger(EmployeeModelListener.class);
private ISequenceGeneratorService sequenceGenerator;
@Autowired
public EmployeeModelListener(ISequenceGeneratorService sequenceGenerator) {
this.sequenceGenerator = sequenceGenerator;
}
@Override
public void onBeforeConvert(BeforeConvertEvent<Employee> event) {
try {
event.getSource().setId(sequenceGenerator.generateSequence(Employee.SEQUENCE_NAME));
} catch (InterruptedException | ExecutionException e) {
logger.error("Error:{}", e.getMessage());
}
}
}
That’s it. For full project sample please refer to the article – Spring WebFlux & Reactive MongoDB