【问题标题】:Spring MongoDB - Why unique index are also applied to embedded document?Spring MongoDB - 为什么唯一索引也适用于嵌入式文档?
【发布时间】:2016-04-19 13:11:05
【问题描述】:

我有一个带有唯一索引的文档 A 和另一个嵌入 A 的文档 B。当我尝试保留 2 个文档 B 中包含相同的文档 A 时,我得到一个 dup key 错误。为什么?难道不应该在指定的集合中确保唯一索引吗?

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

【问题讨论】:

  • 但它在集合中实现。 “嵌入”文档是源集合的“一部分”,实际上并未存储在另一个集合中。因此,当数据嵌入到数组中时,拥有“唯一”属性几乎没有任何意义,除非你真的是认真的。因此,您要么困惑地认为数据存储在其他地方,要么您想要“每个文档的数组中的唯一项”,为此还有其他不涉及索引的编程解决方案。唯一索引始终适用于“集合范围”。
  • 我忘了提到每个文档都存储在自己的集合中(我刚刚编辑了帖子)。这就是为什么我需要每个文档中的唯一索引。同时我需要将 doc B 嵌入到 doc A 中,但不确保它的唯一索引。

标签: java spring mongodb unique-constraint


【解决方案1】:

我使用您的文档并使用此配置进行测试。

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 没有被插入到数据库中,不确定发生了什么,但如果我在私有 B 字段上添加 @Reference,这就像一个魅力。

> 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"
        }
}

我的建议您尝试在 A 类中使用

@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中添加这个工作示例

【讨论】:

  • 问题也出在复合索引上。 @CompoundIndexes({ @CompoundIndex(name = "a_name_b_name", def = "{'name': 1, embed.name': -1}", unique=true)}) 也会抛出 dup 键。
  • 粘贴您的插入语句的代码或您要保存的项目我认为然后有一个副本
  • 我已经使用持久代码示例编辑了帖子。
  • 我已经添加了另一个响应,我自己做并且没有发现重复的错误,但是直到我添加参考注释后第二个 A 记录才被插入
  • 那么使用@Reference 插入的记录是否正确?
【解决方案2】:

你不能以基于注解的方式做到这一点,但你可以通过添加这样的 Spring 的@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)
        );
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多