В моем приложении блога вы можете просмотреть профиль любого пользователя, например, страница моего профиля будет http://www.jiwhiz.com/profile/user1, а user1 — это мой идентификатор пользователя в системе. В MongoDB каждый объект документа будет иметь уникальный идентификатор, и часто мы храним его как String, поэтому для этого у меня есть класс BaseEntity:
|
1
2
3
4
5
6
7
|
@Document@SuppressWarnings('serial')public abstract class BaseEntity implements Serializable { @Id private String id;…} |
Но сгенерированный системой идентификатор обычно очень длинный, и я хочу создать свой собственный идентификатор пользователя в своем классе UserAccount :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@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:
|
01
02
03
04
05
06
07
08
09
10
|
@SuppressWarnings('serial')@Document(collection = 'Counter')public class Counter extends BaseEntity{ private String name; private long sequence;...} |
Поскольку мы будем использовать счетчик особым образом, вам не нужно иметь репозиторий. Я просто создаю CounterService с методом для возврата следующего идентификатора пользователя:
|
1
2
3
|
public interface CounterService { long getNextUserIdSequence();} |
Реализация будет использовать findAndModify для получения следующей последовательности:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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». В этом примере мы не используем Spring Data Repository для Counter документа, но вместо этого используем MongoTemplate напрямую. Из моего класса MongoConfig , который расширяет AbstractMongoConfiguration , который предоставляет MongoTemplate компонент MongoTemplate , мы можем легко внедрить MongoTemplate в другой bean-компонент конфигурации, например CounterService :
|
1
2
3
4
5
6
7
8
9
|
@Configurationclass MainAppConfig {... @Bean public CounterService counterService(MongoTemplate mongoTemplate) { return new CounterServiceImpl(mongoTemplate); }...} |
Прежде чем запускать приложение в любой среде, сначала необходимо настроить документ Counter . Просто введите следующий скрипт в оболочку MongoDB:
|
1
|
db.Counter.insert({ 'name' : 'user_id', sequence : 1}) |
ОК, это шаги для подготовки генератора последовательности идентификаторов пользователей. Но как мы можем использовать это, когда мы хотим добавить нового пользователя в нашу систему? Это становится очень легко сейчас. У нас будет createUserAccount , у которого есть метод createUserAccount , чтобы создать новый UserAccount при UserAccount пользователя в систему.
|
1
2
3
4
5
6
7
8
9
|
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 , см. Выделенный код ниже:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
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:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
@Configurationclass MainAppConfig {... @Bean public AccountService accountService(MongoTemplate mongoTemplate, UserAccountRepository accountRepository, UserSocialConnectionRepository userSocialConnectionRepository) { AccountServiceImpl service = new AccountServiceImpl(accountRepository, userSocialConnectionRepository, counterService(mongoTemplate)); return service; }...} |
Когда мы вызываем AccountService.createUserAccount() ? В то время, когда пользователь впервые пытается войти в систему и система не может найти существующий UserAccount , будет вызван компонент ConnectionSignUp подключенный к MongoUsersConnectionRepository . (См. Мой предыдущий пост о другом весеннем коде, связанном с социальными связями.) Поэтому ConnectionSignUp передаст ConnectionData AccountService.createUserAccount()
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
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: Добавьте данные CounterWithSpring от нашего партнера по JCG Юаня Цзи в блог Jiwhiz .