2
\$\begingroup\$

For my web application I have to generate a sequence of following format:

<year><sometext><month><sequence>

For example:

2014TOYS11000001 -> for first sequence of month of November this year. If month has changed, we have to reset the sequence to 1.

The application is Spring-based, and I have used NoSQL (Mongodb) to persist the sequence. NoSQL because we already have Mongodb usage in the application and this sequence that is generated is not used to refer anything in the backend database(Oracle).

Here is my implementation: an API of String nextVal() backed by a HashMap maintained in Mongodb.

import my.dao.MongoDao;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

public final class MySequence {
    private final String SEQUENCE_TEXT = "TOYS";
    private final String SEQUENCE_FORMAT = "%d%s%02d%010d";

    private static final String KEY_ID = "id";
    private static final String KEY_MONTH = "month";
    private static final String KEY_SEQNB = "seq_nb";

    private static Map<String, String> seqData;

    @Autowired
    private MongoDao mongoDao;

    public MySequence() {
    }

    public String nextVal() {
        synchronized(seqData){
            seqData = mongoDao.findById(HashMap.class, SEQUENCE_TEXT);

            if (seqData == null) {
                //create new hash map with starting values and persist.
                seqData = new HashMap<String, String>(3);
                initSequenceDataMap(seqData);
                //save new map with "id" map field as the id to use in DB
                mongoDao.addObject(seqData, KEY_ID);
            } else {
                int currentMonth = Calendar.getInstance().get(Calendar.MONTH) + 1;
                int monthInDb = Integer.parseInt(seqData.get(KEY_MONTH));
                if (currentMonth != monthInDb) {
                    //reset sequence for the month
                    initSequenceForMonth(seqData, currentMonth);
                } else {
                    incrementSequenceNumber(seqData);
                }

                mongoDao.update(seqData);
            }
        }
        return mapToString(seqData);
    }

    private void incrementSequenceNumber(Map<String, String> seqData) {
        int seqNum = Integer.parseInt(seqData.get(KEY_SEQNB));
        seqNum++;
        seqData.put(KEY_SEQNB, seqNum + "");
    }

    private void initSequenceDataMap(Map<String, String> seqData) {

        seqData.put(KEY_ID, SEQUENCE_TEXT);
        int month = Calendar.getInstance().get(Calendar.MONTH) + 1;
        initSequenceForMonth(seqData, month);
    }

    private void initSequenceForMonth(Map<String, String> dataMap, int month) {
        dataMap.put(KEY_MONTH, month + "");
        dataMap.put(KEY_SEQNB, "1");
    }

    private String mapToString(Map<String, String> seqData) {
        return String.format(SEQUENCE_FORMAT, Calendar.getInstance().get(Calendar.YEAR),SEQUENCE_TEXT,
                Integer.valueOf(seqData.get(KEY_MONTH)),
                Integer.valueOf(seqData.get(KEY_SEQNB)));
    }

    public GenericMongoDao getGenericMongoDao() {
        return mongoDao;
    }

    public void setGenericMongoDao(MongoDao mongoDao) {
        this.mongoDao = mongoDao;
    }
}

Concerns:

  • Is this thread-safe even when two separate beans can be created?
  • Is there a way to achieve less ceremonious call such as `MySequence.nextVal() using spring?
  • Any other improvements?
\$\endgroup\$
  • \$\begingroup\$ If you are afraid of multiple instances why not inject the class or use a singleton? \$\endgroup\$ – user1281750 Dec 12 '14 at 21:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.