在我的博客应用程序中,您可以查看任何用户的个人资料,例如,我的个人资料页面将为http://www.jiwhiz.com/profile/user1,“user1”是我在系统中的用户ID。 在MongoDB中,每个文档对象都会有一个唯一的标识符,通常我们将其存储为String,因此我有一个BaseEntity类:

@Document
@SuppressWarnings('serial')
public abstract class BaseEntity implements Serializable {
    @Id
    private String id;
…
}

但是系统生成的ID通常很长,我想在UserAccount类中生成自己的userId:

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

    private UserRoleType[] roles;

    private String email;

    private String displayName;

    private String imageUrl;

    private String webSite;
...
}

生成的userId非常简单,只是带有序列号的'user',例如,我是第一个用户,因此我的userId是'User1',下一个已注册的用户将是'User2',依此类推。 MongoDB的序列号生成器,为我提供唯一的序列号。 该操作将返回当前序列号,并增加数据库中的序列号。 在MongoDB中,命令findAndModify自动修改并返回单个文档。 因此,我们可以使用此命令来查询序列号并通过$ inc函数对其进行增加。

首先,我们创建一个Counter类来存储用于不同目的的序列号,例如userId:

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

    private String name;

    private long sequence;

...
}

由于我们将以特殊方式使用计数器,因此无需存储库。 我只是使用以下方法创建CounterService来返回下一个用户ID:

public interface CounterService {
    long getNextUserIdSequence();
}

该实现将使用findAndModify来获取下一个序列:

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

    private final MongoTemplate mongoTemplate;

    @Inject
    public CounterServiceImpl(MongoTemplate mongoTemplate){
        this.mongoTemplate = mongoTemplate;
    }

    @Override
    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();
    }
}

使用这种方法,您可以根据需要添加任意数量的序列,只需为其命名即可。 例如,您可以记录对您的网站的访问,因此可以添加一个类似logVisit()的方法,该方法increaseCounter()使用诸如“ visit_num”之类的名称来调用私有方法logVisit() 。 在此示例中,我们不将Spring Data Repository用于Counter文档,而是直接使用MongoTemplate 。 从我的MongoConfig类扩展了AbstractMongoConfiguration ,该类暴露了MongoTemplate bean,我们可以轻松地将MongoTemplate注入其他配置bean中,例如CounterService

@Configuration
class MainAppConfig {
...
    @Bean
    public CounterService counterService(MongoTemplate mongoTemplate) {
        return new CounterServiceImpl(mongoTemplate);
    }
...
}

在任何环境中开始运行您的应用之前,您必须首先设置一个Counter文档。 只需在MongoDB Shell中键入以下脚本:

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

好的,这些是准备用户ID序列生成器的步骤。 但是,当我们要将新用户添加到系统中时,如何使用它呢? 现在变得非常容易。 我们将具有一个AccountService ,它具有createUserAccount方法,用于在用户首次登录时创建一个新的UserAccount

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

    List<UserAccount> getAllUsers();

    List<UserSocialConnection> getConnectionsByUserId(String userId);

    UserAccount createUserAccount(ConnectionData data);
}

在我们的实现类AccountServiceImpl ,我们可以使用CounterService ,请参见下面的突出显示的代码:

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

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

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

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

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

    @Override
    public UserAccount createUserAccount(ConnectionData data) {
        UserAccount account = new UserAccount();
        account.setUserId('user' + this.counterService.getNextUserIdSequence());
        account.setDisplayName(data.getDisplayName());
        account.setImageUrl(data.getImageUrl());
        account.setRoles(new UserRoleType[] { UserRoleType.ROLE_USER });
        this.accountRepository.save(account);
        return account;
    }

    @Override
    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;
    }

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

    @Override
    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;
    }

}

Java配置代码将它们结合在一起以用于AccountService:

@Configuration
class MainAppConfig {
...
    @Bean
    public AccountService accountService(MongoTemplate mongoTemplate, UserAccountRepository accountRepository,
            UserSocialConnectionRepository userSocialConnectionRepository) {
        AccountServiceImpl service = new AccountServiceImpl(accountRepository, userSocialConnectionRepository,
                counterService(mongoTemplate));
        return service;
    }
...
}

我们什么时候调用AccountService.createUserAccount() ? 在用户首次尝试登录时,系统找不到现有的UserAccount ,因此将调用插入MongoUsersConnectionRepositoryConnectionSignUp bean。 (有关其他与Spring社交相关的代码,请参阅我的上一篇文章 。)因此ConnectionSignUp会将ConnectionData传递给AccountService.createUserAccount()

public class AutoConnectionSignUp implements ConnectionSignUp{
    private final AccountService accountService;

    @Inject
    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();
    }
}

我对Spring Data MongoDB的经验非常积极。 它在提供基本的CRUD功能以及丰富的查询功能方面非常强大,并且您无需编写任何实现代码。 如果必须使用MongoDB的特殊命令,则MongoTemplate足够灵活,可以满足您的要求。

参考: MongoDB:Jiwhiz博客上从我们的JCG合作伙伴 Yuan Ji 添加一个CounterWithSpring数据

翻译自: https://www.javacodegeeks.com/2013/03/mongodb-add-a-counter-with-spring-data.html