在我的博客应用程序中,您可以查看任何用户的个人资料,例如,我的个人资料页面将为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
,因此将调用插入MongoUsersConnectionRepository
的ConnectionSignUp
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