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:
parent
413ee370c2
commit
e798355a92
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> {
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
spring.data.mongodb.auto-index-creation=true
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue