Spring Data中MongoDB文檔中的唯一字段
一、簡介
在本教程中,我們將學習如何使用 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
類,為storeId
和number
字段定義一個唯一索引:
@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
不同。如果我們嘗試插入具有相同storeId
和number
值的客戶,這種方法只會導致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
註釋的方法。
與往常一樣,源代碼可在 GitHub 上獲得。