Dav*_*idA 5 mongodb spring-transactions spring-data-mongodb
我正在尝试将 Spring Data Mongo 与事务一起使用。我最初遇到了一个问题,即由于 Spring 在第一次插入文档期间尝试创建集合和/或索引,我的插入会失败。从那以后,我通过在任何事务开始之前在启动时创建我的所有集合和索引来解决这个问题。不过,我还是使用Spring数据注解来定义索引(即@Indexed,@CompoundIndexes等)。但是,即使我已经创建了所有索引,Spring 仍在尝试在 mongo 处理期间确保/创建索引。
在我的情况下,我对我的数据类型使用继承。即,BasicUnit其他单位的扩展。如果我去存储特定类型的 Unit,并且这是我在当前应用程序运行期间第一次尝试保存该类型的对象,则 Spring 无法识别它,并PersistentEntity为它创建一个新对象。作为创建 new 的一部分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$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)
Run Code Online (Sandbox Code Playgroud)
下面是倾向于调用 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
Run Code Online (Sandbox Code Playgroud)
您可以看到 Spring 尝试获取持久实体的位置,然后最终决定添加,然后触发事件,然后通过尝试创建索引来处理事件。
我需要知道如何防止 Spring 在事务期间尝试创建这些索引。
我考虑过的事情
这些解决方案对我来说都不是很有吸引力。
我发现很难相信没有其他人在使用 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);
> }
> -----------------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
尝试这个
@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));
}
Run Code Online (Sandbox Code Playgroud)
请记住 var 仅适用于 Java 11+
原来是我的java包安排造成的。默认情况下,Spring MongoDB 似乎会扫描并注册同一包中的@Document所有实体。但是,我碰巧将该类放在与我的所有实体不同的包中。@PersistentMongoConfiguration
我重写了MongoConfigurationSupport#getMappingBasePackagesin my MongoConfiguration,然后 Spring 能够找到并“预注册”我的所有实体,因此它们不再在事务中间“被发现”。
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;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8548 次 |
| 最近记录: |