Home » Java » Enterprise Java » MongoDB: Add A Counter With Spring Data

About Yuan Ji

Yuan Ji
Yuan is a passionate Java programmer and open source evangelist. He is eager to learn new technologies and loves clean and beautiful application design. He lives in Edmonton, Canada as an independent consultant and contractor.

MongoDB: Add A Counter With Spring Data

In my blog app, you can view any user’s profile, for example, my profile page will be http://www.jiwhiz.com/profile/user1, and ‘user1’ is my user Id in the system. In MongoDB, every document object will have a unique identifier, and often we store it as String, so I have a BaseEntity class for that:

public abstract class BaseEntity implements Serializable {
    private String id;

But the system generated id usually is very long, and I want to generate my own userId in my UserAccount class:

@Document(collection = 'UserAccount')
public class UserAccount extends BaseEntity implements SocialUserDetails {
    private String userId;

    private UserRoleType[] roles;

    private String email;

    private String displayName;

    private String imageUrl;

    private String webSite;

The generated userId is very simple, just ‘user’ with a sequence number, for example, I’m the first user, so my userId is ‘User1’, and the next signed up user will be ‘User2’, etc. I want a sequence number generator from MongoDB to give me unique sequence numbers. The operations are to return current sequence number and also increase the sequence number in the database. In MongoDB, command findAndModify automatically modifies and returns a single document. So we can use this command to query the sequence number and increase it by $inc function.

First we create a Counter class to store sequence numbers for different purposes, like userId:

@Document(collection = 'Counter')
public class Counter extends BaseEntity{

    private String name;

    private long sequence;


Since we will use counter in a special way, there is no need to have a repository. I just create a CounterService with the method to return the next user id:

public interface CounterService {
    long getNextUserIdSequence();

The implementation will use findAndModify to get next sequence:

public class CounterServiceImpl implements CounterService {
    public static final String USER_ID_SEQUENCE_NAME = 'user_id';

    private final MongoTemplate mongoTemplate;

    public CounterServiceImpl(MongoTemplate mongoTemplate){
        this.mongoTemplate = mongoTemplate;

    public long getNextUserIdSequence() {
        return increaseCounter(USER_ID_SEQUENCE_NAME);

    private long increaseCounter(String counterName){
        Query query = new Query(Criteria.where('name').is(counterName));
        Update update = new Update().inc('sequence', 1);
        Counter counter = mongoTemplate.findAndModify(query, update, Counter.class); // return old Counter object
        return counter.getSequence();

Using this approach, you can add as many sequence as you want, just create a name for it. For example, you can record visits to your web site, so add a method like logVisit(), which calls the private method increaseCounter() with a name like ‘visit_num’. In this example, we don’t use Spring Data Repository for Counter document, but instead use MongoTemplate directly. From my MongoConfig class, which extends AbstractMongoConfiguration, which exposes MongoTemplate bean, we can easily inject MongoTemplate into other config bean, like CounterService:

class MainAppConfig {
    public CounterService counterService(MongoTemplate mongoTemplate) {
        return new CounterServiceImpl(mongoTemplate);

Before you start running your app in any environment, you have to set up a Counter document first. Just type the following script in MongoDB shell:

db.Counter.insert({ 'name' : 'user_id', sequence : 1})

OK, those are the steps to prepare a user id sequence generator. But how can we use it when we want to add a new user to our system? It becomes very easy now. We will have an AccountService, which has createUserAccount method, to create a new UserAccount when the user sign in for the first time.

public interface AccountService extends SocialUserDetailsService, UserDetailsService, UserIdExtractor {
    UserAccount findByUserId(String userId);

    List<UserAccount> getAllUsers();

    List<UserSocialConnection> getConnectionsByUserId(String userId);

    UserAccount createUserAccount(ConnectionData data);

In our implementation class AccountServiceImpl, we can use CounterService, see highlighted code below:

public class AccountServiceImpl implements AccountService {
    private final UserAccountRepository accountRepository;
    private final UserSocialConnectionRepository userSocialConnectionRepository;
    private final CounterService counterService;

    public AccountServiceImpl(UserAccountRepository accountRepository, UserSocialConnectionRepository userSocialConnectionRepository, CounterService counterService) {
        this.accountRepository = accountRepository;
        this.userSocialConnectionRepository = userSocialConnectionRepository;
        this.counterService = counterService;

    public UserAccount findByUserId(String userId) {
        return accountRepository.findByUserId(userId);

    public List<UserAccount> getAllUsers() {
        return accountRepository.findAll();

    public List<UserSocialConnection> getConnectionsByUserId(String userId){
        return this.userSocialConnectionRepository.findByUserId(userId);

    public UserAccount createUserAccount(ConnectionData data) {
        UserAccount account = new UserAccount();
        account.setUserId('user' + this.counterService.getNextUserIdSequence());
        account.setRoles(new UserRoleType[] { UserRoleType.ROLE_USER });
        return account;

    public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException, DataAccessException {
        UserAccount account = findByUserId(userId);
        if (account == null) {
            throw new UsernameNotFoundException('Cannot find user by userId ' + userId);
        return account;

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return loadUserByUserId(username);

    public String extractUserId(Authentication authentication) {
        if (authentication instanceof SocialAuthenticationToken) {
            SocialAuthenticationToken token = (SocialAuthenticationToken) authentication;
            if (token.getPrincipal() instanceof SocialUserDetails) {
                return ((SocialUserDetails) token.getPrincipal()).getUserId();
        return null;


The Java config code to glue them together for AccountService:

class MainAppConfig {
    public AccountService accountService(MongoTemplate mongoTemplate, UserAccountRepository accountRepository,
            UserSocialConnectionRepository userSocialConnectionRepository) {
        AccountServiceImpl service = new AccountServiceImpl(accountRepository, userSocialConnectionRepository,
        return service;

When do we call AccountService.createUserAccount()? At the time when a first time user tries to sign in, and the system cannot find an existing UserAccount, so the ConnectionSignUp bean plugged into MongoUsersConnectionRepository will be called. (See my previous post for other spring social connection related code.) So ConnectionSignUp will pass ConnectionData to AccountService.createUserAccount():

public class AutoConnectionSignUp implements ConnectionSignUp{
    private final AccountService accountService;

    public AutoConnectionSignUp(AccountService accountService){
        this.accountService = accountService;

    public String execute(Connection<?> connection) {
        ConnectionData data = connection.createData();

        UserAccount account = this.accountService.createUserAccount(data);

        return account.getUserId();

My experience with Spring Data MongoDB is very positive. It is very powerful in providing basic CRUD functions as well as abundant query functions, and you don’t need to write any implementation code. If you have to use a special command of MongoDB, MongoTemplate is flexible enough to meet your requirements.

Reference: MongoDB: Add A CounterWithSpring Data from our JCG partner Yuan Ji at the Jiwhiz blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!


1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design


and many more ....



Leave a Reply

Your email address will not be published. Required fields are marked *


Want to take your Java skills to the next level?

Grab our programming books for FREE!

Here are some of the eBooks you will get:

  • Spring Interview QnA
  • Multithreading & Concurrency QnA
  • JPA Minibook
  • JVM Troubleshooting Guide
  • Advanced Java
  • Java Interview QnA
  • Java Design Patterns