一、概述
默认情况下,MongoDB Java 驱动程序生成ObjectId
类型的ID。有时,我们可能希望使用另一种类型的数据作为对象的唯一标识符,例如UUID。但是,MongoDB Java 驱动程序不能自动生成UUID。
在本教程中,我们将研究使用MongoDB Java 驱动程序和Spring Data MongoDB 生成UUID 的三种方法。
2. 共同点
一个应用程序只管理一种类型的数据是非常罕见的。为了简化MongoDB 数据库中ID 的管理,实现一个抽像类来定义我们所有Document
类的ID 会更容易。
public abstract class UuidIdentifiedEntity {
@Id
protected UUID id;
public void setId(UUID id) {
if (this.id != null) {
throw new UnsupportedOperationException("ID is already defined");
}
this.id = id;
}
// Getter
}
对于本教程中的示例,我们将假设MongoDB 数据库中持久化的所有类都继承自该类。
3.配置UUID支持
为了允许在MongoDB 中存储UUID,我们必须配置驱动程序。这个配置非常简单,只告诉驱动程序如何在数据库中存储UUID。如果多个应用程序使用同一个数据库,我们必须小心处理。
我们所要做的就是在启动时在我们的MongoDB 客户端中指定uuidRepresentation
参数:
@Bean
public MongoClient mongo() throws Exception {
ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/test");
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.uuidRepresentation(UuidRepresentation.STANDARD)
.applyConnectionString(connectionString).build();
return MongoClients.create(mongoClientSettings);
}
如果我们使用Spring Boot,我们可以在application.properties 文件中指定这个参数:
spring.data.mongodb.uuid-representation=standard
4. 使用生命周期事件
处理UUID 生成的第一种方法是使用Spring 的生命周期事件。对于MongoDB 实体,我们不能使用JPA 注释@PrePersist
等。因此,我们必须实现在ApplicationContext
中注册的事件监听器类。为此,我们的类必须扩展Spring 的AbstractMongoEventListener
类:
public class UuidIdentifiedEntityEventListener extends AbstractMongoEventListener<UuidIdentifiedEntity> {
@Override
public void onBeforeConvert(BeforeConvertEvent<UuidIdentifiedEntity> event) {
super.onBeforeConvert(event);
UuidIdentifiedEntity entity = event.getSource();
if (entity.getId() == null) {
entity.setId(UUID.randomUUID());
}
}
}
在这种情况下,我们使用OnBeforeConvert
事件,该事件在Spring 将我们的Java 对象转换为Document
对象并将其发送到MongoDB 驱动程序之前触发。
键入我们的事件以捕获UuidIdentifiedEntity
类允许处理此抽象超类型的所有子类。一旦使用UUID 作为ID 的对像被转换,Spring 就会调用我们的代码。
我们必须知道,Spring 将事件处理委托给可能是异步的TaskExecutor
。Spring 不保证在对像被有效转换之前处理事件。如果您的TaskExecutor
是异步的,则不建议使用此方法,因为ID 可能在对象转换后生成,从而导致异常:
InvalidDataAccessApiUsageException: Cannot autogenerate id of type java.util.UUID for entity
我们可以通过使用@Component
注释或在@Configuration
类中生成它来在ApplicationContext
中注册事件侦听器:
@Bean
public UuidIdentifiedEntityEventListener uuidIdentifiedEntityEventListener() {
return new UuidIdentifiedEntityEventListener();
}
5. 使用实体回调
Spring 基础设施提供了在实体生命周期中的某些点执行自定义代码的钩子。这些称为EntityCallbacks,
我们可以在我们的案例中使用它们在对像被持久化到数据库之前生成一个UUID。
与之前看到的事件侦听器方法不同,回调保证它们的执行是同步的,并且代码将在对像生命周期中的预期点运行。
Spring Data MongoDB 提供了一组我们可以在应用程序中使用的回调。在我们的例子中,我们将使用与之前相同的事件。回调可以直接在@Configuration
类中提供:
@Bean
public BeforeConvertCallback<UuidIdentifiedEntity> beforeSaveCallback() {
return (entity, collection) -> {
if (entity.getId() == null) {
entity.setId(UUID.randomUUID());
}
return entity;
};
}
我们还可以使用实现BeforeConvertCallback
接口的Component
。
6. 使用自定义存储库
Spring Data MongoDB 提供了第三种方法来实现我们的目标:使用自定义存储库实现。通常,我们只需要声明一个继承自MongoRepository,
然后Spring 处理与存储库相关的代码。
如果我们想改变Spring Data 处理对象的方式,我们可以定义Spring 将在存储库级别执行的自定义代码。为此,我们必须首先定义一个扩展MongoRepository
的接口:
@NoRepositoryBean
public interface CustomMongoRepository<T extends UuidIdentifiedEntity> extends MongoRepository<T, UUID> { }
@NoRepositoryBean
注解防止Spring 生成与MongoRepository
关联的通常代码段。此接口强制使用UUID 作为对像中ID 的类型。
然后,我们必须创建一个存储库类来定义处理UUID 所需的行为:
public class CustomMongoRepositoryImpl<T extends UuidIdentifiedEntity>
extends SimpleMongoRepository<T, UUID> implements CustomMongoRepository<T>
在这个存储库中,我们必须通过覆盖SimpleMongoRepository
的相关方法来捕获需要生成ID 的所有方法调用。在我们的例子中,这些方法是save()
和insert()
:
@Override
public <S extends T> S save(S entity) {
generateId(entity);
return super.save(entity);
}
最后,我们需要告诉Spring 使用我们的自定义类作为存储库的实现,而不是默认实现。我们在@Configuration
类中这样做:
@EnableMongoRepositories(basePackages = "com.baeldung.repository", repositoryBaseClass = CustomMongoRepositoryImpl.class)
然后,我们可以像往常一样声明我们的存储库,无需更改:
public interface BookRepository extends MongoRepository<Book, UUID> { }
7. 结论
在本文中,我们看到了三种使用Spring Data MongoDB 将UUID 实现为MongoDB 对象ID 的方法。
0 评论