拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Spring Data中MongoDB文档中的唯一字段

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

白鹭 - 2022-07-04 2167 0 2

一、简介

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

2.配置

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

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

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

3.@Indexed注解

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

@Document
public class Company {
@Id
private String id;
private String name;
@Indexed(unique = true)
private String email;
// getters and setters
}

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

@Test
public void givenUniqueIndex_whenInsertingDupe_thenExceptionIsThrown() {
Company a = new Company();
a.setName("Name");
a.setEmail("a[email protected]");
companyRepo.insert(a);
Company b = new Company();
b.setName("Other");
b.setEmail("[email protected]");
assertThrows(DuplicateKeyException.class, () -> {
companyRepo.insert(b);
});
}

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

3.1。注释多个字段

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

@Document
public class Asset {
@Indexed(unique = true)
private String name;
@Indexed(unique = true)
private Integer number;
}

请注意,我们没有在任何字段上明确设置@Id。MongoDB 仍会自动为我们设置一个“ _id”字段,但我们的应用程序无法访问它。但是,我们不能将@Id与标记为unique@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);
});
}

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

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

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

public class SaleId {
private Long item;
private String date;
// getters and setters
}

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

@Document
public class Sale {
@Indexed(unique = true)
private SaleId saleId;
private Double value;
// getters and setters
}

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

@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);
});
}

这种方法的优点是保持索引定义分开。这允许我们在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
}

这与多个字段上的@Indexed不同。如果我们尝试插入具有相同storeIdnumber值的客户,这种方法只会导致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);
});
}

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

5. 结论

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


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *