В моем приложении блога вы можете просмотреть профиль любого пользователя, например, страница моего профиля будет 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
|
@Configuration class 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
|
@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
, будет вызван компонент 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 .