BAEL-5394 - Unique Field in MongoDB Document in Spring Data (#12384)

* BAEL-5370 - MongoDB Composite Key

First Draft.

* removing comments

* BAEL-5370

Test could fail if ran in a different order:
givenCompositeId_whenSearchingByIdObject_thenFound

* BAEL-5370

removing compound index related stuff

* removing first insert from assertThrows

* first draft

* removing Customer2
This commit is contained in:
Ulisses Lima 2022-06-26 13:32:33 -03:00 committed by GitHub
parent 413ee370c2
commit e798355a92
13 changed files with 437 additions and 0 deletions

View File

@ -0,0 +1,15 @@
package com.baeldung.boot.unique.field;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@SpringBootApplication
@PropertySource("classpath:boot.unique.field/app.properties")
@EnableMongoRepositories(basePackages = { "com.baeldung.boot.unique.field" })
public class SpringBootUniqueFieldApplication {
public static void main(String... args) {
SpringApplication.run(SpringBootUniqueFieldApplication.class, args);
}
}

View File

@ -0,0 +1,8 @@
package com.baeldung.boot.unique.field.dao;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.boot.unique.field.data.Asset;
public interface AssetRepository extends MongoRepository<Asset, String> {
}

View File

@ -0,0 +1,11 @@
package com.baeldung.boot.unique.field.dao;
import java.util.Optional;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.boot.unique.field.data.Company;
public interface CompanyRepository extends MongoRepository<Company, String> {
Optional<Company> findByEmail(String email);
}

View File

@ -0,0 +1,11 @@
package com.baeldung.boot.unique.field.dao;
import java.util.Optional;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.boot.unique.field.data.Customer;
public interface CustomerRepository extends MongoRepository<Customer, String> {
Optional<Customer> findByStoreIdAndNumber(Long storeId, Long number);
}

View File

@ -0,0 +1,12 @@
package com.baeldung.boot.unique.field.dao;
import java.util.Optional;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.boot.unique.field.data.Sale;
import com.baeldung.boot.unique.field.data.SaleId;
public interface SaleRepository extends MongoRepository<Sale, String> {
Optional<Sale> findBySaleId(SaleId id);
}

View File

@ -0,0 +1,29 @@
package com.baeldung.boot.unique.field.data;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Asset {
@Indexed(unique = true)
private String name;
@Indexed(unique = true)
private Integer number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
}

View File

@ -0,0 +1,40 @@
package com.baeldung.boot.unique.field.data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Company {
@Id
private String id;
private String name;
@Indexed(unique = true)
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@ -0,0 +1,57 @@
package com.baeldung.boot.unique.field.data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@CompoundIndex(name = "customer_idx", def = "{ 'storeId': 1, 'number': 1 }", unique = true)
public class Customer {
@Id
private String id;
private Long storeId;
private Long number;
private String name;
public Customer() {
}
public Customer(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getStoreId() {
return storeId;
}
public void setStoreId(Long storeId) {
this.storeId = storeId;
}
public Long getNumber() {
return number;
}
public void setNumber(Long number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,36 @@
package com.baeldung.boot.unique.field.data;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Sale {
@Indexed(unique = true)
private SaleId saleId;
private Double value;
public Sale() {
}
public Sale(SaleId saleId) {
super();
this.saleId = saleId;
}
public SaleId getSaleId() {
return saleId;
}
public void setSaleId(SaleId saleId) {
this.saleId = saleId;
}
public Double getValue() {
return value;
}
public void setValue(Double value) {
this.value = value;
}
}

View File

@ -0,0 +1,22 @@
package com.baeldung.boot.unique.field.data;
public class SaleId {
private Long item;
private String date;
public Long getItem() {
return item;
}
public void setItem(Long item) {
this.item = item;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}

View File

@ -0,0 +1,83 @@
package com.baeldung.boot.unique.field.web;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.baeldung.boot.unique.field.dao.AssetRepository;
import com.baeldung.boot.unique.field.dao.CompanyRepository;
import com.baeldung.boot.unique.field.dao.CustomerRepository;
import com.baeldung.boot.unique.field.dao.SaleRepository;
import com.baeldung.boot.unique.field.data.Asset;
import com.baeldung.boot.unique.field.data.Company;
import com.baeldung.boot.unique.field.data.Customer;
import com.baeldung.boot.unique.field.data.Sale;
import com.baeldung.boot.unique.field.data.SaleId;
@RestController
@RequestMapping("/unique-field")
public class UniqueFieldController {
@Autowired
private SaleRepository saleRepo;
@Autowired
private CompanyRepository companyRepo;
@Autowired
private CustomerRepository customerRepo;
@Autowired
private AssetRepository assetRepo;
@PostMapping("/sale")
public Sale post(@RequestBody Sale sale) {
return saleRepo.insert(sale);
}
@GetMapping("/sale")
public Optional<Sale> getSale(SaleId id) {
return saleRepo.findBySaleId(id);
}
@PostMapping("/company")
public Company post(@RequestBody Company company) {
return companyRepo.insert(company);
}
@PutMapping("/company")
public Company put(@RequestBody Company company) {
return companyRepo.save(company);
}
@GetMapping("/company/{id}")
public Optional<Company> getCompany(@PathVariable String id) {
return companyRepo.findById(id);
}
@PostMapping("/customer")
public Customer post(@RequestBody Customer customer) {
return customerRepo.insert(customer);
}
@GetMapping("/customer/{id}")
public Optional<Customer> getCustomer(@PathVariable String id) {
return customerRepo.findById(id);
}
@PostMapping("/asset")
public Asset post(@RequestBody Asset asset) {
return assetRepo.insert(asset);
}
@GetMapping("/asset/{id}")
public Optional<Asset> getAsset(@PathVariable String id) {
return assetRepo.findById(id);
}
}

View File

@ -0,0 +1 @@
spring.data.mongodb.auto-index-creation=true

View File

@ -0,0 +1,112 @@
package com.baeldung.boot.unique.field;
import static org.junit.Assert.assertThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.boot.unique.field.dao.AssetRepository;
import com.baeldung.boot.unique.field.dao.CompanyRepository;
import com.baeldung.boot.unique.field.dao.CustomerRepository;
import com.baeldung.boot.unique.field.dao.SaleRepository;
import com.baeldung.boot.unique.field.data.Asset;
import com.baeldung.boot.unique.field.data.Company;
import com.baeldung.boot.unique.field.data.Customer;
import com.baeldung.boot.unique.field.data.Sale;
import com.baeldung.boot.unique.field.data.SaleId;
@SpringBootTest
@DirtiesContext
@RunWith(SpringRunner.class)
public class UniqueFieldIntegrationTest {
@Autowired
private SaleRepository saleRepo;
@Autowired
private CompanyRepository companyRepo;
@Autowired
private CustomerRepository customerRepo;
@Autowired
private AssetRepository assetRepo;
@Test
public void givenMultipleIndexes_whenAnyFieldDupe_thenExceptionIsThrown() {
Asset a = new Asset();
a.setName("Name");
a.setNumber(1);
assetRepo.insert(a);
Asset b = new Asset();
b.setName("Name");
b.setNumber(2);
assertThrows(DuplicateKeyException.class, () -> {
assetRepo.insert(b);
});
Asset c = new Asset();
c.setName("Other");
c.setNumber(1);
assertThrows(DuplicateKeyException.class, () -> {
assetRepo.insert(c);
});
}
@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);
});
}
@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);
});
}
@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);
Sale b = new Sale(id);
b.setValue(100.00);
assertThrows(DuplicateKeyException.class, () -> {
saleRepo.insert(b);
});
}
}