I have a method that generates an alphanumeric sequence based on a particular pattern. I want to save the generated sequence and when next I want to generate a new one, it should start from the last saved one. I am having an issue with this happening because it is alphanumeric. Also, I want to put the generated IDs in an excel file.
These two methods below check if it already exists and also try to fetch the last item saved.
public Optional<Terminal> findByGeneratedTerminalIDs(String id){
return Optional.ofNullable(this.collection.findOne(Filters.eq("generatedTerminalID", id)));
}
public Optional<FindIterable<Terminal>> findLastGeneratedTerminalID(String generatedTerminalID) {
return Optional.ofNullable(this.collection.find(Filters.eq("generatedTerminalID", generatedTerminalID)).
sort(Sorts.descending("generatedTerminalID")).limit(1));
}
Here is the updated repository
private JacksonMongoCollection<Sequence> collection;
public SequenceRepository(JacksonMongoCollection<Sequence> collection) {
this.collection = collection;
}
public Sequence save(Sequence sequence) {
collection.save(sequence);
return sequence;
}
public Sequence findAllSequences(Sequence sequence) {
return collection.find(Filters.eq("generatedTerminalID", sequence.getGeneratedTerminalID())).first();
}
public BaseResponse generateTerminalIDs(TerminalIDDTO terminalDto) {
Sequence sequence = new Sequence();
sequence.setNext(0);
sequenceRepository.save(sequence);
int totalLength = 8;
int numberOfIds = terminalDto.getNumberOfTerminals();
int countRemainingSymbols = totalLength - START.length();
//there should be only 1 row at all times
sequence = this.sequenceRepository.findAllSequences(sequence);
int start = sequence.getNext();//start generation of sequences from the value of next
int next = start + numberOfIds;//this will be next value of sequence.next
for (int i = start; i < next; i++) {
StringBuilder end = new StringBuilder();
int current = i;
int remainder = current % ALPHANUMERIC.length();//the index of next character
do {
end.append(ALPHANUMERIC.charAt(remainder));
current /= ALPHANUMERIC.length();//update to check if we need to add more characters
remainder = current % ALPHANUMERIC.length();//update index, only used if more chars are needed
} while (current > 0);
int padCount = countRemainingSymbols - end.length();
StringBuilder result = new StringBuilder(START).append("-");//- is for easier debugging, remove it for your case
for (int j = 0; j < padCount; j++) {
result.append("0");
}
result.append(end.reverse());
log.info("These are the values {}", result);
}
//update next value and save in db
sequence.setNext(next);
sequenceRepository.save(sequence);
return BaseResponse.builder().status(true).message("TerminalIDs have been generated").build();
}
I want the saving and checking if it exists to be done in this method I posted above. What is the best way to go about it?
You could do it adding a new table to db. Since it has to be unique for entire db, this table will hold a single row, which keeps track of next integer to use for id generation.
I'll assume you use spring data, then your entity could be something like this:
public class Sequence {
private String id;
private int next;
//getters and setters
}
The repository:
@Repository
public interface SequenceRepository extends MongoRepository<Sequence, String> {
}
Assuming you are using mongo from question tag.
Initial value for next should be zero.
Sequence sequence = new Sequence();
sequence.setNext(0);
this.repository.saveAndFlush(sequence);
Initialise and save in db at appropriate time, maybe app startup. Just do this initialization only once.
Then generation method might be like this:
@Autowired
private SequenceRepository repository;
public BaseResponse generateTerminalIDs(TerminalIDDTO terminalDto) {
Terminal terminal = new Terminal();
int totalLength = 8;
int numberOfIds = terminalDto.getNumberOfTerminals();
int countRemainingSymbols = totalLength - START.length();
//there should be only 1 row at all times
Sequence sequence = this.repository.findAll().get(0);
int start = sequence.getNext();//start generation of sequences from the value of next
int next = start + numberOfIds;//this will be next value of sequence.next
for (int i = start; i < next; i++) {
StringBuilder end = new StringBuilder();
int current = i;
int remainder = current % ALPHANUMERIC.length();//the index of next character
do {
end.append(ALPHANUMERIC.charAt(remainder));
current /= ALPHANUMERIC.length();//update to check if we need to add more characters
remainder = current % ALPHANUMERIC.length();//update index, only used if more chars are needed
} while (current > 0);
int padCount = countRemainingSymbols - end.length();
StringBuilder result = new StringBuilder(START).append("-");//- is for easier debugging, remove it for your case
for (int j = 0; j < padCount; j++) {
result.append("0");
}
//this should be outside pad loop
result.append(end.reverse());
}
//update next value and save in db
sequence.setNext(next);
this.repository.saveAndFlush(sequence);
return BaseResponse.builder().status(true).message("TerminalIDs have been generated").build();
}
There are some assumptions here, but this is the overall algorithm you should follow.
Important: This solution does not take into account possible simultaneous access to the table, which may lead to duplicating ids. If you have such use cases, you might want to consider:
generateTerminalIDs
execution, thus forcing other threads who want access for next to wait until all ids are generated.Edit: SaveAndFlush is equivalent for create or update. In spring it's implemented in this way - if entity has id property(sequence.id != null
in this case), do an update, otherwise do create(insert). For your case, create the row when initializing new table, update it when ids are created.
About findAll()
- it returns a list with all rows/entites from a table. Here repository.findAll()
will return List<Sequence>
. repository.findAll().get(0)
will return Sequence
, the first and only row in the table.