• Spring Data中MongoDB文档中的唯一字段


    1. 简介

    在本教程中,我们将学习如何使用 Spring Data 在 MongoDB中定义一个唯一字段。唯一字段是数据库设计的重要组成部分。它们同时保证一致性和性能,防止不应该出现的重复值。

    2. 配置

    与关系数据库不同,MongoDB 不提供创建约束的选项。**因此,我们唯一的选择是创建唯一索引。**但是,默认情况下,Spring Data 中的自动索引创建是关闭的。首先,让我们在 application.properties 中打开它:

    spring.data.mongodb.auto-index-creation=true
    
    • 1

    使用该配置,如果索引尚不存在,则将在引导时创建索引。**但是,我们必须记住,在我们已经有重复值之后,我们不能创建唯一索引。**这将导致在我们的应用程序启动期间引发异常。

    3. @Indexed注解

    @Indexed注解允许我们将字段标记为具有索引。由于我们配置了自动索引创建,我们不必自己创建它们。**默认情况下,索引不是唯一的。**因此,我们必须通过unique属性将其打开。让我们通过创建第一个示例来看看它的实际效果:

    @Document
    public class Company {
        @Id
        private String id;
    
        private String name;
    
        @Indexed(unique = true)
        private String email;
    
        // getters and setters
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    请注意,我们仍然可以拥有我们的*@Id注解,它完全独立于我们的索引。这就是我们需要拥有一个具有唯一字段的文档的全部内容。因此,如果我们使用相同的email插入多个文档,则会导致 DuplicateKeyException

    @Test
    public void givenUniqueIndex_whenInsertingDupe_thenExceptionIsThrown() {
        Company a = new Company();
        a.setName("Name");
        a.setEmail("a@mail.com");
    
        companyRepo.insert(a);
    
        Company b = new Company();
        b.setName("Other");
        b.setEmail("a@mail.com");
        assertThrows(DuplicateKeyException.class, () -> {
            companyRepo.insert(b);
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    当我们想要强制唯一性但仍然有一个自动生成的唯一 ID 字段时,这种方法很有用。

    3.1. 注解多个字段

    我们还可以将注解添加到多个字段。让我们继续创建第二个示例:

    @Document
    public class Asset {
        @Indexed(unique = true)
        private String name;
    
        @Indexed(unique = true)
        private Integer number;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    请注意,我们没有在任何字段上明确设置*@Id 。MongoDB 仍会自动为我们设置一个“ _id ”字段,但我们的应用程序无法访问它。但是,我们不能将@Id与标记为唯一的@Indexed*注解放在同一字段上。当应用程序尝试创建索引时,它会抛出异常。

    此外,现在我们有两个独特的字段。**请注意,这并不意味着它是一个复合索引。**因此,对任何字段多次插入相同值将导致重复键。让我们测试一下:

    @Test
    public void givenMultipleIndexes_whenAnyFieldDupe_thenExceptionIsThrown() {
        Asset a = new Asset();
        a.setName("Name");
        a.setNumber(1);
    
        assetRepo.insert(a);
    
        assertThrows(DuplicateKeyException.class, () -> {
            Asset b = new Asset();
            b.setName("Name");
            b.setNumber(2);
    
            assetRepo.insert(b);
        });
    
        assertThrows(DuplicateKeyException.class, () -> {
            Asset b = new Asset();
            b.setName("Other");
            b.setNumber(1);
    
            assetRepo.insert(b);
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    如果我们只希望组合值形成唯一索引,我们必须创建一个复合索引。

    3.2. 使用自定义类型作为索引

    **同样,我们可以注解自定义类型的字段。这样就达到了复合索引的效果。**让我们从一个SaleId类开始来表示我们的复合索引:

    public class SaleId {
        private Long item;
        private String date;
    
        // getters and setters
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    现在让我们创建我们的Sale类来使用它:

    @Document
    public class Sale {
        @Indexed(unique = true)
        private SaleId saleId;
    
        private Double value;
    
        // getters and setters
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    现在,每次我们尝试添加具有相同 SaleId 的新销售*时,我们都会得到一个重复的键。让我们测试一下:

    @Test
    public void givenCustomTypeIndex_whenInsertingDupe_thenExceptionIsThrown() {
        SaleId id = new SaleId();
        id.setDate("2022-06-15");
        id.setItem(1L);
    
        Sale a = new Sale(id);
        a.setValue(53.94);
    
        saleRepo.insert(a);
    
        assertThrows(DuplicateKeyException.class, () -> {
            Sale b = new Sale(id);
            b.setValue(100.00);
    
            saleRepo.insert(b);
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这种方法的优点是保持索引定义分开。这允许我们在SaleId中包含或删除新字段,而无需重新创建或更新我们的索引。它也与复合键非常相似。但是,索引与键不同,因为它们可以有一个空值。

    4. @CompoundIndex注解

    要在没有自定义类的情况下拥有由多个字段组成的唯一索引,我们必须创建一个复合索引。为此,我们直接在类中使用 @CompoundIndex注解。 这个注解包含一个 def 属性,我们将使用它来包含我们需要的字段。让我们创建我们的*Customer类,为 storeIdnumber 字段定义一个唯一索引:

    @Document
    @CompoundIndex(def = "{'storeId': 1, 'number': 1}", unique = true)
    public class Customer {
        @Id
        private String id;
    
        private Long storeId;
        private Long number;
        private String name;
    
        // getters and setters
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这与多个字段上的*@Indexed不同。如果我们尝试插入具有相同storeId数字值的客户,这种方法只会导致DuplicateKeyException :*

    @Test
    public void givenCompoundIndex_whenDupeInsert_thenExceptionIsThrown() {
        Customer customerA = new Customer("Name A");
        customerA.setNumber(1l);
        customerA.setStoreId(2l);
    
        Customer customerB = new Customer("Name B");
        customerB.setNumber(1l);
        customerB.setStoreId(2l);
    
        customerRepo.insert(customerA);
    
        assertThrows(DuplicateKeyException.class, () -> {
            customerRepo.insert(customerB);
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用这种方法,我们的优势在于不必仅为我们的索引创建另一个类。此外,可以将 @Id 注解添加到复合索引定义中的字段。但是,与 @Indexed 不同,它不会导致异常。

    5. 结论

    在本文中,我们学习了如何为文档定义唯一字段。因此,我们了解到我们唯一的选择是使用唯一索引。此外,使用 Spring Data,我们可以轻松配置我们的应用程序以自动创建索引。而且,我们看到了许多使用 @Indexed@CompoundIndex 注解的方法。

  • 相关阅读:
    Hibernate知识大合集
    mysql约束
    Spring源码编译一次性通过遇到问题如何解决?
    Mac OS 使用Metal渲染NV12、YUV420、CMSampleBufferRef视频
    技术干货 | MindSpore AI科学计算系列(四):AlphaFold2分析
    Java测试(10)--- selenium
    30岁年薪28W,我还是没顶住压力跳槽了····
    YOLOv8独家改进:逐元素乘法(star operation)二次创新 | 微软新作StarNet:超强轻量级Backbone CVPR 2024
    B. Moderate Modular Mode(nmodx=ymodn )
    删除数据库
  • 原文地址:https://blog.csdn.net/wjw465150/article/details/127792284