如何防止 Spring 在 MongoDB 事务期间尝试创建索引?
How to prevent Spring from trying to create indexes during MongoDB transactions?
我正在尝试将 Spring 数据 Mongo 与 T运行 操作一起使用。我最初 运行 遇到一个问题,由于 Spring 在第一次插入文档期间尝试创建集合 and/or 索引,我的插入将失败。我已经通过在任何 t运行saction 开始之前在启动时创建我的所有集合和索引来解决这个问题。但是,我仍然使用 Spring 数据注释来定义索引(即 @Indexed
、@CompoundIndexes
等)。但是,即使我已经创建了所有索引,Spring 仍在 mongo 处理期间尝试 ensure/create 索引。
在我的情况下,我对我的数据类型使用继承。即,BasicUnit
其他单元的扩展。如果我去存储一个特定类型的Unit,并且是我在当前应用程序期间第一次尝试保存该类型的对象运行,Spring无法识别它,并创建一个新的PersistentEntity
。作为创建新 PersistentEntity
的一部分,Spring 发布一个事件,MongoPersistentEntityIndexCreator
捕获该事件,该事件反过来尝试确保创建所有索引,因此抛出异常。
例外情况:
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.call(CreateIndexesOperation.java:174)
at com.mongodb.operation.CreateIndexesOperation.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[=12=](DefaultIndexOperations.java:135)
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:545)
这是调用 ensureIndex 的调用堆栈:
DefaultIndexOperations.lambda$ensureIndex[=13=](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>.access0(Object, MappingContext, ConversionService) line: 580
EntityOperations.forEntity(T, ConversionService) line: 105
MongoTemplate.doInsert(String, T, MongoWriter<T>) line: 1237
您可以看到 Spring 尝试 获取 持久实体的位置,然后最终决定 add,然后触发关闭事件,然后通过尝试创建索引来处理事件。
我需要知道如何防止 Spring 在 t运行 操作期间尝试创建这些索引。
我考虑过的事情
- 想办法预注册我所有的永久实体,
但我不确定除了手动保留列表之外该怎么做,或者
编写代码扫描 class 路径以查找所有 classes
由@Document 注释或扩展这样的 class。
- 完全放弃Spring的索引注解,只使用显式调用
我需要的每个索引的 ensureIndex。
这两种解决方案都不是很吸引我。
我很难相信没有其他人 运行 使用 Spring 数据 Mongo 和 T运行 操作来解决类似的索引问题,所以你们中的任何一个知道这个问题的解决方案,我很想听听。
谢谢。
原来是我java包编排造成的。默认情况下,Spring MongoDB 将扫描并注册与 MongoConfiguration
相同包中的所有 @Document
和 @Persistent
实体。但是,我恰好将 class 放在与我所有实体不同的包中。
我覆盖了 MongoConfiguration
中的 MongoConfigurationSupport#getMappingBasePackages
然后 Spring 能够找到并 'pre-register' 我的所有实体,所以它们不再是 'discovered' 在交易中。
public class MongoConfiguration extends AbstractMongoConfiguration {
....
/* (non-Javadoc)
* @see org.springframework.data.mongodb.config.MongoConfigurationSupport#getMappingBasePackages()
*/
@Override
protected Collection<String> getMappingBasePackages() {
java.util.List<String> packages = new ArrayList<>(1);
packages.add("my.entity.base.package");
return packages;
}
}
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+
我正在尝试将 Spring 数据 Mongo 与 T运行 操作一起使用。我最初 运行 遇到一个问题,由于 Spring 在第一次插入文档期间尝试创建集合 and/or 索引,我的插入将失败。我已经通过在任何 t运行saction 开始之前在启动时创建我的所有集合和索引来解决这个问题。但是,我仍然使用 Spring 数据注释来定义索引(即 @Indexed
、@CompoundIndexes
等)。但是,即使我已经创建了所有索引,Spring 仍在 mongo 处理期间尝试 ensure/create 索引。
在我的情况下,我对我的数据类型使用继承。即,BasicUnit
其他单元的扩展。如果我去存储一个特定类型的Unit,并且是我在当前应用程序期间第一次尝试保存该类型的对象运行,Spring无法识别它,并创建一个新的PersistentEntity
。作为创建新 PersistentEntity
的一部分,Spring 发布一个事件,MongoPersistentEntityIndexCreator
捕获该事件,该事件反过来尝试确保创建所有索引,因此抛出异常。
例外情况:
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.call(CreateIndexesOperation.java:174)
at com.mongodb.operation.CreateIndexesOperation.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[=12=](DefaultIndexOperations.java:135)
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:545)
这是调用 ensureIndex 的调用堆栈:
DefaultIndexOperations.lambda$ensureIndex[=13=](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>.access0(Object, MappingContext, ConversionService) line: 580
EntityOperations.forEntity(T, ConversionService) line: 105
MongoTemplate.doInsert(String, T, MongoWriter<T>) line: 1237
您可以看到 Spring 尝试 获取 持久实体的位置,然后最终决定 add,然后触发关闭事件,然后通过尝试创建索引来处理事件。
我需要知道如何防止 Spring 在 t运行 操作期间尝试创建这些索引。
我考虑过的事情
- 想办法预注册我所有的永久实体, 但我不确定除了手动保留列表之外该怎么做,或者 编写代码扫描 class 路径以查找所有 classes 由@Document 注释或扩展这样的 class。
- 完全放弃Spring的索引注解,只使用显式调用 我需要的每个索引的 ensureIndex。
这两种解决方案都不是很吸引我。
我很难相信没有其他人 运行 使用 Spring 数据 Mongo 和 T运行 操作来解决类似的索引问题,所以你们中的任何一个知道这个问题的解决方案,我很想听听。
谢谢。
原来是我java包编排造成的。默认情况下,Spring MongoDB 将扫描并注册与 MongoConfiguration
相同包中的所有 @Document
和 @Persistent
实体。但是,我恰好将 class 放在与我所有实体不同的包中。
我覆盖了 MongoConfiguration
中的 MongoConfigurationSupport#getMappingBasePackages
然后 Spring 能够找到并 'pre-register' 我的所有实体,所以它们不再是 'discovered' 在交易中。
public class MongoConfiguration extends AbstractMongoConfiguration {
....
/* (non-Javadoc)
* @see org.springframework.data.mongodb.config.MongoConfigurationSupport#getMappingBasePackages()
*/
@Override
protected Collection<String> getMappingBasePackages() {
java.util.List<String> packages = new ArrayList<>(1);
packages.add("my.entity.base.package");
return packages;
}
}
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+