Spring MongoDB - Почему уникальный индекс также применяется к встроенному документу?

У меня есть документ A с уникальным индексом и другой документ B, в который встроен A. Когда я пытаюсь сохранить 2 документа B с одним и тем же документом A, я получаю ошибку ключа дублирования. Зачем? Разве уникальный индекс не должен быть обеспечен только в той коллекции, где он указан?

A.java

@Document(collection = "docsA") 
public class A {
    @Id
    private String id;

    @Indexed(unique = true)  
    private String name;

    private B embed;
}

B.java

@Document(collection="docsB")
public class B {
    @Id
    private String id;

    @Indexed(unique = true)  
    private String name;
}

Сохраняющиеся операции:

B b = new B();
b.setName("BName1");
docsBRepository.save(b);

A a1 = new A();
a1.setName("AName1");
a1.setB(b);
docsARepository.save(a1); // stored

A a2 = new A();
a2.setName("AName2");
a2.setB(b);
docsARepository.save(a2); // dup key error

2 ответа

Я делаю тест со своей стороны с вашими документами и использую этот конфиг.

public interface DocsARepository extends MongoRepository<A,Long>{}
public interface DocsBRepository extends MongoRepository<B,Long>{}

Этот весенний конфиг

<mongo:mongo id="mongo" />
<mongo:repositories base-package="spring.mongodb.repositories"/>

И этот тест

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "spring.xml")
public class Launcher {

    @Autowired
    DocsBRepository docsBRepository;
    @Autowired
    DocsARepository docsARepository;

   @Test
   public void test1(){

        B b = new B();
        b.setName("BName1");
        b = docsBRepository.save(b);

        A a1 = new A();
        a1.setName("AName1");
        a1.setB(b);
       docsARepository.save(a1); // stored

        A a2 = new A();
        a2.setName("AName2");
        a2.setB(b);
        docsARepository.save(a2); //Dont get any error but record is not inserted
    }

}

После запуска теста я обнаружил, что объект a2 не вставляется в базу данных, не совсем уверен, что именно вызвало такое поведение, но если я добавлю @Reference поверх частного поля B, это будет работать как шарм.

> db.docsA.find().pretty()
{
        "_id" : ObjectId("5717acb720e54e20a4d96517"),
        "_class" : "com.koitoer.spring.mongodb.domain.A",
        "name" : "AName1",
        "b" : {
                "_id" : ObjectId("5717acb720e54e20a4d96516"),
                "name" : "BName1"
        }
}
{
        "_id" : ObjectId("5717acb720e54e20a4d96518"),
        "_class" : "com.koitoer.spring.mongodb.domain.A",
        "name" : "AName2",
        "b" : {
                "_id" : ObjectId("5717acb720e54e20a4d96516"),
                "name" : "BName1"
        }
}

Мой совет, который вы пытаетесь использовать в классе А

@Reference
private B b;

С помощью

<org.mongodb.version>1.5.5.RELEASE</org.mongodb.version>
<org.mongodb.driver>3.0.0-beta3</org.mongodb.driver>
<spring-framework.version>3.2.5.RELEASE</spring-framework.version>

Добавление этого рабочего примера в github

Вы не можете сделать это способом, основанным на аннотациях, но вы можете достичь этого программно, добавив такой Spring's @Configuration:

@Configuration
@DependsOn("mongoTemplate")
public class CollectionsConfig {

    @Autowired
    private MongoTemplate mongoTemplate;

    @PostConstruct
    public void initIndexes() {
        mongoTemplate.indexOps("docsB") // so index is applied only to "docsB" collection
            .ensureIndex(
                new Index().on("name", Sort.Direction.ASC)
        );
    }
}
Другие вопросы по тегам