读取数据时违反约束
Constraint violation when reading data
我正在用 junit-jupiter 编写集成测试,发生了一些非常奇怪的事情 -> 当我读取(不保存数据)时发生约束违反异常
storesTemplateRepository.findByCountryOrderByTemplateName(country, pageable);
引发以下异常:
could not execute statement; SQL [n/a]; constraint ["PRIMARY KEY ON PUBLIC.STORES_TEMPLATE(ID)"; SQL statement:
insert into stores_template (country, stores, template_name, id) values (?, ?, ?, ?) [23505-196]]
实体:
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "STORES_TEMPLATE")
public class StoresTemplate {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "STORES_TEMPLATE_ID_SEQ")
@SequenceGenerator(name = "STORES_TEMPLATE_ID_SEQ", sequenceName = "STORES_TEMPLATE_ID_SEQ", allocationSize = 1)
private long id;
@Enumerated(EnumType.STRING)
private CountryEnum country;
private String templateName;
@Lob
private String stores;
public void setStores(List<String> stores) {
this.stores = String.join(",", stores);
}
@JsonIgnore
public List<String> getStoresAsList() {
return Arrays.stream(stores.split(","))
.distinct()
.collect(Collectors.toList());
}
}
测试
@Slf4j
@Transactional
@SpringBootTest
public class StoresTemplateControllerTest {
@Autowired
private WebApplicationContext context;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private StoresTemplateRepository storesTemplateRepository;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
@Test
public void fullApiTest() {
OAuth2AuthenticationToken dePrincipal = new TestDataBuilder()
.createOAuth2AuthenticationToken()
.setDefaultStoreUser()
.setRoles(SiamRoles.DE_CREATOR_ADMIN)
.build();
CreateStoresTemplateDto createStoresTemplateDeDto = CreateStoresTemplateDto.builder()
.country(CountryEnum.DE)
.stores(List.of("de1000", "de1100"))
.templateName("de template")
.build();
CreateStoresTemplateDto createStoresTemplateBgDto = CreateStoresTemplateDto.builder()
.country(CountryEnum.BG)
.stores(List.of("bg2000", "bg2100"))
.templateName("bg template")
.build();
CreateStoresTemplateDto createStoresTemplateDefaultDto = CreateStoresTemplateDto.builder()
.country(null)
.stores(List.of("de3000", "de3100"))
.templateName("default template")
.build();
try {
// find all existing by predefined insertion script
for (long id: findAll(dePrincipal, CountryEnum.DE).map(e -> e.id)) {
storesTemplateRepository.deleteById(id);
}
storesTemplateRepository.save(StoresTemplateMapper.toStoresTemplate(createStoresTemplateDeDto));
storesTemplateRepository.findByCountryOrderByTemplateName(CountryEnum.DE);
storesTemplateRepository.save(StoresTemplateMapper.toStoresTemplate(createStoresTemplateBgDto));
storesTemplateRepository.findByCountryOrderByTemplateName(CountryEnum.DE); // Here the exception occurs
storesTemplateRepository.save(StoresTemplateMapper.toStoresTemplate(createStoresTemplateDefaultDto));
storesTemplateRepository.findByCountryOrderByTemplateName(CountryEnum.DE);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
}
JPA
/Hibernate
尽可能将其 session
中的操作排队,不会立即调用数据库,然后在事务完成之前,订购那些基于类型的操作并执行它们。这在休眠中称为 Transactional write-behind
。如您所见,即使您先调用删除,hibernate 也会在排队时将其排在最后。
- 插入,按照执行的顺序
- 更新
- 删除集合元素
- 集合元素的插入
- 按执行顺序删除
因此,即使您确实先删除,正如您所见,hibernate 也会最后删除。如果你想控制订单,你需要刷新它。所以请执行以下操作。
for (long id: findAll(dePrincipal, CountryEnum.DE).map(e -> e.id)) {
storesTemplateRepository.deleteById(id);
}
storesTemplateRepository.flush();
参考
我正在用 junit-jupiter 编写集成测试,发生了一些非常奇怪的事情 -> 当我读取(不保存数据)时发生约束违反异常
storesTemplateRepository.findByCountryOrderByTemplateName(country, pageable);
引发以下异常:
could not execute statement; SQL [n/a]; constraint ["PRIMARY KEY ON PUBLIC.STORES_TEMPLATE(ID)"; SQL statement:
insert into stores_template (country, stores, template_name, id) values (?, ?, ?, ?) [23505-196]]
实体:
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "STORES_TEMPLATE")
public class StoresTemplate {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "STORES_TEMPLATE_ID_SEQ")
@SequenceGenerator(name = "STORES_TEMPLATE_ID_SEQ", sequenceName = "STORES_TEMPLATE_ID_SEQ", allocationSize = 1)
private long id;
@Enumerated(EnumType.STRING)
private CountryEnum country;
private String templateName;
@Lob
private String stores;
public void setStores(List<String> stores) {
this.stores = String.join(",", stores);
}
@JsonIgnore
public List<String> getStoresAsList() {
return Arrays.stream(stores.split(","))
.distinct()
.collect(Collectors.toList());
}
}
测试
@Slf4j
@Transactional
@SpringBootTest
public class StoresTemplateControllerTest {
@Autowired
private WebApplicationContext context;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private StoresTemplateRepository storesTemplateRepository;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
@Test
public void fullApiTest() {
OAuth2AuthenticationToken dePrincipal = new TestDataBuilder()
.createOAuth2AuthenticationToken()
.setDefaultStoreUser()
.setRoles(SiamRoles.DE_CREATOR_ADMIN)
.build();
CreateStoresTemplateDto createStoresTemplateDeDto = CreateStoresTemplateDto.builder()
.country(CountryEnum.DE)
.stores(List.of("de1000", "de1100"))
.templateName("de template")
.build();
CreateStoresTemplateDto createStoresTemplateBgDto = CreateStoresTemplateDto.builder()
.country(CountryEnum.BG)
.stores(List.of("bg2000", "bg2100"))
.templateName("bg template")
.build();
CreateStoresTemplateDto createStoresTemplateDefaultDto = CreateStoresTemplateDto.builder()
.country(null)
.stores(List.of("de3000", "de3100"))
.templateName("default template")
.build();
try {
// find all existing by predefined insertion script
for (long id: findAll(dePrincipal, CountryEnum.DE).map(e -> e.id)) {
storesTemplateRepository.deleteById(id);
}
storesTemplateRepository.save(StoresTemplateMapper.toStoresTemplate(createStoresTemplateDeDto));
storesTemplateRepository.findByCountryOrderByTemplateName(CountryEnum.DE);
storesTemplateRepository.save(StoresTemplateMapper.toStoresTemplate(createStoresTemplateBgDto));
storesTemplateRepository.findByCountryOrderByTemplateName(CountryEnum.DE); // Here the exception occurs
storesTemplateRepository.save(StoresTemplateMapper.toStoresTemplate(createStoresTemplateDefaultDto));
storesTemplateRepository.findByCountryOrderByTemplateName(CountryEnum.DE);
} catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
}
JPA
/Hibernate
尽可能将其session
中的操作排队,不会立即调用数据库,然后在事务完成之前,订购那些基于类型的操作并执行它们。这在休眠中称为Transactional write-behind
。如您所见,即使您先调用删除,hibernate 也会在排队时将其排在最后。- 插入,按照执行的顺序
- 更新
- 删除集合元素
- 集合元素的插入
- 按执行顺序删除
因此,即使您确实先删除,正如您所见,hibernate 也会最后删除。如果你想控制订单,你需要刷新它。所以请执行以下操作。
for (long id: findAll(dePrincipal, CountryEnum.DE).map(e -> e.id)) {
storesTemplateRepository.deleteById(id);
}
storesTemplateRepository.flush();
参考