SpringBoot MongoDB 색인 충돌 분석 및 해결 방법
14272 단어 SpringBootMongoDB색인 충돌
spring-data-mongo 는 MongoDB 기반 ORM-Mapping 능력 을 실현 하 였 습 니 다.
간단 한 주석,Query 패 키 징 과 도구 류 를 통 해 대상 작업 을 통 해 집합,문서 의 삭제 와 검 사 를 실현 할 수 있 습 니 다.
SpringBoot 시스템 에서 spring-data-mongo 는 MongoDB 자바 도구 창고 의 두 가지 선택 입 니 다.
2.문제 발생
프로젝트 문제 의 추적 에서 SpringBoot 응용 프로그램 시작 에 실 패 했 습 니 다.잘못된 정 보 는 다음 과 같 습 니 다.
Error creating bean with name 'mongoTemplate' defined in class path resource [org/bootfoo/BootConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.mongodb.core.MongoTemplate]: Factory method 'mongoTemplate' threw exception; nested exception is org.springframework.dao.DataIntegrityViolationException: Cannot create index for 'deviceId' in collection 'T_MDevice' with keys '{ "deviceId" : 1}' and options '{ "name" : "deviceId"}'. Index already defined as '{ "v" : 1 , "unique" : true , "key" : { "deviceId" : 1} , "name" : "deviceId" , "ns" : "appdb.T_MDevice"}'.; nested exception is com.mongodb.MongoCommandException: Command failed with error 85: 'exception: Index with name: deviceId already exists with different options' on server 127.0.0.1:27017. The full response is { "createdCollectionAutomatically" : false, "numIndexesBefore" : 6, "errmsg" : "exception: Index with name: deviceId already exists with different options", "code" : 85, "ok" : 0.0 }
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
...
Caused by: org.springframework.dao.DataIntegrityViolationException: Cannot create index for 'deviceId' in collection 'T_MDevice' with keys '{ "deviceId" : 1}' and options '{ "name" : "deviceId"}'. Index already defined as '{ "v" : 1 , "unique" : true , "key" : { "deviceId" : 1} , "name" : "deviceId" , "ns" : "appdb.T_MDevice"}'.; nested exception is com.mongodb.MongoCommandException: Command failed with error 85: 'exception: Index with name: deviceId already exists with different options' on server 127.0.0.1:27017. The full response is { "createdCollectionAutomatically" : false, "numIndexesBefore" : 6, "errmsg" : "exception: Index with name: deviceId already exists with different options", "code" : 85, "ok" : 0.0 }
at org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator.createIndex(MongoPersistentEntityIndexCreator.java:157)
at org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator.checkForAndCreateIndexes(MongoPersistentEntityIndexCreator.java:133)
at org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator.checkForIndexes(MongoPersistentEntityIndexCreator.java:125)
at org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator.
at org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator.
at org.springframework.data.mongodb.core.MongoTemplate.
at org.bootfoo.BootConfiguration.mongoTemplate(BootConfiguration.java:121)
at org.bootfoo.BootConfiguration$$EnhancerBySpringCGLIB$$1963a75.CGLIB$mongoTemplate$2(
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
... 58 more
Caused by: com.mongodb.MongoCommandException: Command failed with error 85: 'exception: Index with name: deviceId already exists with different options' on server 127.0.0.1:27017. The full response is { "createdCollectionAutomatically" : false, "numIndexesBefore" : 6, "errmsg" : "exception: Index with name: deviceId already exists with different options", "code" : 85, "ok" : 0.0 }
at com.mongodb.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:115)
at com.mongodb.connection.CommandProtocol.execute(CommandProtocol.java:114)
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:168)
핵심 정보:org.springframework.dao.Data Integrity ViolationException:색인 을 만 들 수 없습니다
이상 정보 에서 볼 때 색인 충돌(Command failed with error 85)이 발생 합 니 다.spring-data-mongo 구성 요 소 는 프로그램 이 시 작 될 때 주석 에 따라 색인 을 만 드 는 기능 을 수행 합 니 다.
비 즈 니스 실체 정의 보기:
@Document(collection = "T_MDevice")
public class MDevice {
@Id
private String id;
@Indexed(unique=true)
private String deviceId;
deviceId 필드 에 색인 이 정의 되 어 있 습 니 다.유 니 크=true 는 유일한 색인 임 을 표시 합 니 다.우 리 는 MongoDB 에서 표 의 정 의 를 계속 봅 니 다.
db.getCollection('T_MDevice').getIndexes()
>>
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "appdb.T_MDevice"
},
{
"v" : 1,
"key" : {
"deviceId" : 1
},
"name" : "deviceId",
"ns" : "appdb.T_MDevice"
}
]
데이터베이스 시트 에 도 deviceId 라 는 색인 이 존재 하지만 유일한 색인 은 아 닙 니 다!3.상세 분석
오류 가 발생 한 원인 을 확인 하기 위해 서,우 리 는 Mongo Shell 을 통 해 색인 생 성 을 실행 하려 고 시도 하 였 는데,같은 오 류 를 되 돌려 주 었 음 을 발견 하 였 다.
데이터베이스 에 있 는 색인 을 삭제 하거나 유 니 크=true 로 수정 하면 현재 문 제 를 해결 할 수 있 습 니 다.
엄밀 한 측면 에서 볼 때 색인 충돌 로 인해 SpringBoot 서비스 가 시작 되 지 않 고 받 아들 일 수 있 습 니 다.
그러나 유연성 으로 볼 때 색인 자동 생 성 을 사용 하지 않 거나 로그 만 인쇄 할 수 있 는 방법 이 있 습 니까?
Google spring data mongodb 에서 인덱스 생 성 을 비활성화 하려 고 시도 합 니 다.
발견JIRA-DATAMONGO-12012015 년 에 이미 제기 되 었 고 지금까지 해결 되 지 않 았 다.
그림.
stackoverflow 에서 많은 것 을 찾 았 습 니 다같은 문제,
그러나 대부분의 해답 은 색인 주 해 를 사용 하지 않 고 다른 방식 으로 색인 을 관리 하 는 것 이다.
이 결과 들 은 결코 만 족 스 럽 지 못 하 다.
spring-data-mongo 의 메커니즘 을 보고 Mongo PersistentEntity IndexCreator 클래스 로 찾 아 보십시오.
초기 화 방법 에 서 는 MappingContext(실체 맵 컨 텍스트)에 있 는 실체 에 따라 색인 을 만 듭 니 다.
public MongoPersistentEntityIndexCreator(MongoMappingContext mappingContext, MongoDbFactory mongoDbFactory,
IndexResolver indexResolver) {
...
//
for (MongoPersistentEntity<?> entity : mappingContext.getPersistentEntities()) {
checkForIndexes(entity);
}
}
MappingContextEvent 를 받 을 때 실체 에 대한 색인 을 만 듭 니 다.
public void onApplicationEvent(MappingContextEvent<?, ?> event) {
if (!event.wasEmittedBy(mappingContext)) {
return;
}
PersistentEntity<?, ?> entity = event.getPersistentEntity();
// Double check type as Spring infrastructure does not consider nested generics
if (entity instanceof MongoPersistentEntity) {
//
checkForIndexes((MongoPersistentEntity<?>) entity);
}
}
MongoPersistentEntityIndexCreator 는 MongoTemplate 를 통 해 도 입 된 것 으로 다음 과 같다.
public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) {
Assert.notNull(mongoDbFactory);
this.mongoDbFactory = mongoDbFactory;
this.exceptionTranslator = mongoDbFactory.getExceptionTranslator();
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
...
// We always have a mapping context in the converter, whether it's a simple one or not
mappingContext = this.mongoConverter.getMappingContext();
// We create indexes based on mapping events
if (null != mappingContext && mappingContext instanceof MongoMappingContext) {
indexCreator = new MongoPersistentEntityIndexCreator((MongoMappingContext) mappingContext, mongoDbFactory);
eventPublisher = new MongoMappingEventPublisher(indexCreator);
if (mappingContext instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
}
}
}
...
//MongoTemplate ApplicationContextAware, ApplicationContext
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
prepareIndexCreator(applicationContext);
eventPublisher = applicationContext;
if (mappingContext instanceof ApplicationEventPublisherAware) {
//MappingContext , ApplicationContext
((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
}
resourceLoader = applicationContext;
}
...
//
private void prepareIndexCreator(ApplicationContext context) {
String[] indexCreators = context.getBeanNamesForType(MongoPersistentEntityIndexCreator.class);
for (String creator : indexCreators) {
MongoPersistentEntityIndexCreator creatorBean = context.getBean(creator, MongoPersistentEntityIndexCreator.class);
if (creatorBean.isIndexCreatorFor(mappingContext)) {
return;
}
}
if (context instanceof ConfigurableApplicationContext) {
// IndexCreator ApplicationContext
((ConfigurableApplicationContext) context).addApplicationListener(indexCreator);
}
}
이 를 통 해 알 수 있 듯 이 MongoTemplate 는 초기 화 할 때 MongoConverter 를 통 해 MongoMappingContext 를 가 져 옵 니 다.그 다음 에 일련의 초기 화 를 완 성 했 습 니 다.전체 과정 은 다음 과 같 습 니 다.
4.문제 해결
앞의 분석 에서 문 제 는 IndexCreator 에 있 습 니 다.사용자 정의 실현 을 제공 할 수 있 습 니까?답 은 가능 합 니 다!
실현 의 요점 은 다음 과 같다.
@Bean
public MongoMappingContext mappingContext() {
return new MongoMappingContext();
}
// MappingContext MongoTemplate
@Bean
public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MongoMappingContext mappingContext) {
MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory),
mappingContext);
converter.setTypeMapper(new DefaultMongoTypeMapper(null));
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);
return mongoTemplate;
}
사용자 정의 IndexCreator
// IndexCreator
@Component
public static class CustomIndexCreator extends MongoPersistentEntityIndexCreator {
// MappingContext
public CustomIndexCreator(MongoMappingContext mappingContext, MongoDbFactory mongoDbFactory) {
super(mappingContext, mongoDbFactory);
}
public void onApplicationEvent(MappingContextEvent<?, ?> event) {
PersistentEntity<?, ?> entity = event.getPersistentEntity();
// Mongo
if (entity instanceof MongoPersistentEntity) {
System.out.println("Detected MongoEntity " + entity.getName());
// ..
}
}
}
여기 서 CustomIndexCreator 는 Mongo Persistent Entity IndexCreator 를 계승 하여 Mapping ContextEvent 사건 의 감청 을 자동 으로 인수 합 니 다.업무 수행 에 있어 서 필요 에 따라 색인 처 리 를 완성 할 수 있 습 니 다!
작은 매듭
spring-data-mongo 는 매우 큰 편의 성 을 제공 하지만 유연성 지원 에 있어 서 는 여전히 부족 하 다.상술 한 방법 은 사실상 약간 모호 해서 공식 문서 에서 이런 방식 을 언급 하지 않 았 다.
ORM-Mapping 프레임 워 크 는 Schema 맵 처 리 를 실현 할 때 검증 단 계 를 고려 해 야 합 니 다.예 를 들 어 Hibernate 는 none/create/update/vaidation 여러 가지 선택 을 제공 합 니 다.이것 은 개발 자 에 게 더욱 우호 적 이기 때 문 입 니 다.
spring-data-mongo 가 후속 발전 에서 Schema 의 관리 기능 을 조속히 보완 할 수 있 기 를 기대 합 니 다!
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
【Java・SpringBoot・Thymeleaf】 에러 메세지를 구현(SpringBoot 어플리케이션 실천편 3)로그인하여 사용자 목록을 표시하는 응용 프로그램을 만들고, Spring에서의 개발에 대해 공부하겠습니다 🌟 마지막 데이터 바인딩에 계속 바인딩 실패 시 오류 메시지를 구현합니다. 마지막 기사🌟 src/main/res...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.