1. Обзор
В этом руководстве мы узнаем, как реализовать последовательное автоматически сгенерированное поле для MongoDB в Spring Boot.
Когда мы используем MongoDB в качестве базы данных для приложения Spring Boot, мы не можем использовать аннотацию @GeneratedValue
в наших моделях, поскольку она недоступна. Следовательно, нам нужен метод для получения такого же эффекта, как если бы мы использовали JPA и базу данных SQL.
Общее решение этой проблемы простое. Мы создадим коллекцию (таблицу), в которой будет храниться сгенерированная последовательность для других коллекций. Во время создания новой записи мы будем использовать ее для получения следующего значения.
2. Зависимости
Давайте добавим в наш pom.xml
следующие стартеры Spring-Boot :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<versionId>2.2.2.RELEASE</versionId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<versionId>2.2.2.RELEASE</versionId>
</dependency>
</dependencies>
Последняя версия зависимостей управляется spring-boot-starter-parent
.
3. Коллекции
Как обсуждалось в обзоре, мы создадим коллекцию, в которой будет храниться автоматически увеличивающаяся последовательность для других коллекций. Мы назовем эту коллекцию database_sequences.
Его можно создать с помощью оболочки mongo
или MongoDB Compass. Создадим соответствующий класс модели:
@Document(collection = "database_sequences")
public class DatabaseSequence {
@Id
private String id;
private long seq;
//getters and setters omitted
}
Давайте затем создадим коллекцию пользователей
и соответствующий объект модели, в котором будут храниться сведения о людях, использующих нашу систему:
@Document(collection = "users")
public class User {
@Transient
public static final String SEQUENCE_NAME = "users_sequence";
@Id
private long id;
private String email;
//getters and setters omitted
}
В созданную выше модель пользователя мы добавили статическое поле
SEQUENCE_NAME,
которое является уникальной ссылкой на автоматически увеличивающуюся последовательность для коллекции пользователей .
Мы также аннотируем его с помощью @Transient
, чтобы предотвратить его сохранение вместе с другими свойствами модели.
4. Создание новой записи
На данный момент мы создали необходимые коллекции и модели. Теперь мы создадим службу, которая будет генерировать автоматически увеличивающееся значение, которое можно использовать в качестве идентификатора
для наших сущностей.
Давайте создадим SequenceGeneratorService
с generateSequence()
:
public long generateSequence(String seqName) {
DatabaseSequence counter = mongoOperations.findAndModify(query(where("_id").is(seqName)),
new Update().inc("seq",1), options().returnNew(true).upsert(true),
DatabaseSequence.class);
return !Objects.isNull(counter) ? counter.getSeq() : 1;
}
Теперь мы можем использовать generateSequence()
при создании новой записи:
User user = new User();
user.setId(sequenceGenerator.generateSequence(User.SEQUENCE_NAME));
user.setEmail("john.doe@example.com");
userRepository.save(user);
Чтобы перечислить всех пользователей, мы будем использовать UserRepository
:
List<User> storedUsers = userRepository.findAll();
storedUsers.forEach(System.out::println);
Как и сейчас, мы должны устанавливать поле id каждый раз, когда создаем новый экземпляр нашей модели. Мы можем обойти этот процесс, создав прослушиватель событий жизненного цикла Spring Data MongoDB.
Для этого мы создадим UserModelListener
, который расширяет AbstractMongoEventListener<User>
, а затем мы переопределим onBeforeConvert()
:
@Override
public void onBeforeConvert(BeforeConvertEvent<User> event) {
if (event.getSource().getId() < 1) {
event.getSource().setId(sequenceGenerator.generateSequence(User.SEQUENCE_NAME));
}
}
Теперь каждый раз, когда мы сохраняем нового пользователя,
идентификатор будет устанавливаться автоматически .
5. Вывод
В заключение мы увидели, как генерировать последовательные автоматически увеличивающиеся значения для поля id и имитировать то же поведение, что и в базах данных SQL.
Hibernate по умолчанию использует аналогичный метод для генерации автоматически увеличивающихся значений.
Как обычно, полный исходный код доступен на Github .