mongodb - 如何防止 Spring 在 MongoDB 事务期间尝试创建索引?

标签 mongodb spring-data-mongodb spring-transactions

我正在尝试将 Spring Data Mongo 与事务结合使用。我最初遇到了一个问题,由于 Spring 在第一次插入文档期间尝试创建集合和/或索引,所以我的插入会失败。我已经通过在任何事务开始之前在启动时创建我的所有集合和索引来解决这个问题。但是,我仍然使用 Spring Data 注释来定义索引(即 @Indexed@CompoundIndexes 等)。然而,即使我已经创建了所有索引,Spring 仍在尝试在 mongo 处理期间确保/创建索引。

在我的情况下,我对我的数据类型使用继承。即,其他单元扩展的 BasicUnit。如果我去存储特定类型的 Unit,并且这是我在当前应用程序运行期间第一次尝试保存该类型的对象,Spring 不会识别它,并创建一个新的 PersistentEntity 为它。作为创建新 PersistentEntity 的一部分,Spring 发布一个事件,MongoPersistentEntityIndexCreator 捕获该事件,该事件反过来会尝试确保创建所有索引,从而抛出异常。

异常(exception)情况:

Caused by: com.mongodb.MongoCommandException: Command failed with error 263 (OperationNotSupportedInTransaction): 'It is illegal to run command createIndexes in a multi-document transaction.' on server 127.0.0.1:27017. The full response is { "operationTime" : { "$timestamp" : { "t" : 1560198052, "i" : 1 } }, "ok" : 0.0, "errmsg" : "It is illegal to run command createIndexes in a multi-document transaction.", "code" : 263, "codeName" : "OperationNotSupportedInTransaction", "$clusterTime" : { "clusterTime" : { "$timestamp" : { "t" : 1560198052, "i" : 1 } }, "signature" : { "hash" : { "$binary" : "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "$type" : "00" }, "keyId" : { "$numberLong" : "0" } } } }
    at com.mongodb.internal.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:179)
    at com.mongodb.internal.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:293)
    at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:255)
    at com.mongodb.internal.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:99)
    at com.mongodb.internal.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:444)
    at com.mongodb.internal.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:72)
    at com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:200)
    at com.mongodb.internal.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:269)
    at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:131)
    at com.mongodb.internal.connection.DefaultServerConnection.command(DefaultServerConnection.java:123)
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:242)
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:233)
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:170)
    at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:163)
    at com.mongodb.operation.CreateIndexesOperation$1.call(CreateIndexesOperation.java:174)
    at com.mongodb.operation.CreateIndexesOperation$1.call(CreateIndexesOperation.java:169)
    at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:453)
    at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:415)
    at com.mongodb.operation.CreateIndexesOperation.execute(CreateIndexesOperation.java:169)
    at com.mongodb.operation.CreateIndexesOperation.execute(CreateIndexesOperation.java:70)
    at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:193)
    at com.mongodb.client.internal.MongoCollectionImpl.executeCreateIndexes(MongoCollectionImpl.java:805)
    at com.mongodb.client.internal.MongoCollectionImpl.createIndexes(MongoCollectionImpl.java:800)
    at com.mongodb.client.internal.MongoCollectionImpl.createIndexes(MongoCollectionImpl.java:793)
    at com.mongodb.client.internal.MongoCollectionImpl.createIndex(MongoCollectionImpl.java:778)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:246)
    at org.springframework.data.mongodb.SessionAwareMethodInterceptor.invoke(SessionAwareMethodInterceptor.java:123)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy202.createIndex(Unknown Source)
    at org.springframework.data.mongodb.core.DefaultIndexOperations.lambda$ensureIndex$0(DefaultIndexOperations.java:135)
    at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:545)

这是调用 ensureIndex 的调用堆栈:

DefaultIndexOperations.lambda$ensureIndex$0(IndexDefinition, MongoCollection) line: 135 
1894975953.doInCollection(MongoCollection) line: not available  
MongoTemplate.execute(String, CollectionCallback<T>) line: 545  
DefaultIndexOperations.execute(CollectionCallback<T>) line: 218 
DefaultIndexOperations.ensureIndex(IndexDefinition) line: 121   
MongoPersistentEntityIndexCreator.createIndex(MongoPersistentEntityIndexResolver$IndexDefinitionHolder) line: 145   
MongoPersistentEntityIndexCreator.checkForAndCreateIndexes(MongoPersistentEntity<?>) line: 135  
MongoPersistentEntityIndexCreator.checkForIndexes(MongoPersistentEntity<?>) line: 127   
MongoPersistentEntityIndexCreator.onApplicationEvent(MappingContextEvent<?,?>) line: 111    
MongoPersistentEntityIndexCreator.onApplicationEvent(ApplicationEvent) line: 54 
SimpleApplicationEventMulticaster.doInvokeListener(ApplicationListener, ApplicationEvent) line: 172 
SimpleApplicationEventMulticaster.invokeListener(ApplicationListener<?>, ApplicationEvent) line: 165    
SimpleApplicationEventMulticaster.multicastEvent(ApplicationEvent, ResolvableType) line: 139    
AnnotationConfigWebApplicationContext(AbstractApplicationContext).publishEvent(Object, ResolvableType) line: 398    
AnnotationConfigWebApplicationContext(AbstractApplicationContext).publishEvent(ApplicationEvent) line: 355  
MongoMappingContext(AbstractMappingContext<E,P>).addPersistentEntity(TypeInformation<?>) line: 405  
MongoMappingContext(AbstractMappingContext<E,P>).getPersistentEntity(TypeInformation<?>) line: 248  
MongoMappingContext(AbstractMappingContext<E,P>).getPersistentEntity(Class<?>) line: 191    
MongoMappingContext(AbstractMappingContext<E,P>).getPersistentEntity(Class) line: 85    
MongoMappingContext(MappingContext<E,P>).getRequiredPersistentEntity(Class<?>) line: 73 
EntityOperations$AdaptibleMappedEntity<T>.of(T, MappingContext<MongoPersistentEntity<?>,MongoPersistentProperty>, ConversionService) line: 600  
EntityOperations$AdaptibleMappedEntity<T>.access$100(Object, MappingContext, ConversionService) line: 580   
EntityOperations.forEntity(T, ConversionService) line: 105  
MongoTemplate.doInsert(String, T, MongoWriter<T>) line: 1237

你可以看到 Spring 在哪里尝试获取持久实体,然后最终决定添加,然后触发事件,然后通过尝试处理事件创建索引。

我需要知道如何防止 Spring 在事务期间尝试创建这些索引。

我考虑过的事情

  1. 试着弄清楚如何预注册我所有的持久实体, 但我不确定除了手动保留列表之外该怎么做,或者 编写代码扫描类路径以查找所有类 由@Document 注释或扩展此类。
  2. 完全放弃Spring的索引注解,只使用显式调用 ensureIndex 我需要的每个索引。

这两种解决方案对我来说都不是很有吸引力。

我很难相信没有其他人在使用 Spring Data Mongo with Transactions 时遇到过类似的索引问题,所以你们中的任何一个都知道这个问题的解决方案,我很想听听。

谢谢。

最佳答案

Automatic index creation will be disabled by default as of Spring Data MongoDB 3.x.     Please use 'MongoMappingContext#setAutoIndexCreation(boolean)' or override 'MongoConfigurationSupport#autoIndexCreation()' to be explicit.  However, we recommend setting up indices manually in an application ready block. You may use index derivation there as well.

> -----------------------------------------------------------------------------------------
> @EventListener(ApplicationReadyEvent.class)
> public void initIndicesAfterStartup() {
>
>     IndexOperations indexOps = mongoTemplate.indexOps(DomainType.class);
>
>     IndexResolver resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);
>     resolver.resolveIndexFor(DomainType.class).forEach(indexOps::ensureIndex);
> }
> -----------------------------------------------------------------------------------------

Try this

@EventListener(ApplicationReadyEvent.class)
public void initIndicesAfterStartup() {    
  log.info("Mongo InitIndicesAfterStartup init");
  var init = System.currentTimeMillis();

  var mappingContext = this.mongoConverter.getMappingContext();

  if (mappingContext instanceof MongoMappingContext) {
    var mongoMappingContext = (MongoMappingContext) mappingContext;

    for (BasicMongoPersistentEntity<?> persistentEntity : mongoMappingContext.getPersistentEntities()) {
      var clazz = persistentEntity.getType();
      if (clazz.isAnnotationPresent(Document.class)) {
        var indexOps = mongoTemplate.indexOps(clazz);
        var resolver = new MongoPersistentEntityIndexResolver(mongoMappingContext);                    
        resolver.resolveIndexFor(clazz).forEach(indexOps::ensureIndex);
      }
    }
  }
  log.info("Mongo InitIndicesAfterStartup take: {}", (System.currentTimeMillis() - init));
}

记住 var 只适用于 java 11+

关于mongodb - 如何防止 Spring 在 MongoDB 事务期间尝试创建索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56533431/

相关文章:

Mongodb 组然后是子组

javascript - Mongo $lookup $pipeline 具有多个 $expr 参数,未在 $lookup 结果中返回正确的数据

Spring嵌套事务

java - @Sql 失败的 SQL 脚本 : The configured DataSource [*] (named 'fooDS' ) is not the one associated with transaction manager [*] (named 'fooTM' )

java - 在 ControllerTest 中使用 @WebAppConfiguration 而不是 @SpringBootTest 时,Spring Data JPA 中会忽略延迟初始化

mongodb - 我怎样才能在 json 中响应时间毫秒为零

带有spring数据mongodb的mongodb动态模式

spring - 如何记录 Spring Data MongoDB 执行的查询?

spring - 如何使用spring data mongodb mongotemplate插入嵌入文档

ruby - Mongoid $project 聚合不返回任何东西