spring-boot - 读取数据时违反约束

标签 spring-boot jpa junit-jupiter

我正在用 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 中对操作进行排队,不会立即调用数据库事务正在完成,根据类型对这些操作进行排序并执行它们。这在 hibernate 中称为 Transactional write-behind。如您所见,即使您先调用删除,如果已排队,hibernate 也会将其排在最后。

    1. 按照执行的顺序插入
    2. 更新
    3. 集合元素的删除
    4. 集合元素的插入
    5. 按执行顺序删除
  • 因此,即使您确实先删除,正如您所见,hibernate 也会最后执行。如果你想控制订单,你需要刷新它。所以请执行以下操作。

    for (long id: findAll(dePrincipal, CountryEnum.DE).map(e -> e.id)) {
        storesTemplateRepository.deleteById(id);
    }
    storesTemplateRepository.flush();

引用

关于spring-boot - 读取数据时违反约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63237223/

相关文章:

spring-boot - Spring boot JSF可执行JAR找不到xhtml文件

spring-boot - Junit5-木星所有测试套件@BeforeAll @AfterAll 不起作用

spring-el - 如何为多个系统属性执行多个逻辑结束?

junit - 在 JUnit 5 中使用枚举值作为标签

spring-mvc - ContentCachingResponseWrapper 生成空响应

java - 如何在 spring boot 应用程序的 .log 文件中写入 stdout 语句

spring-boot - 为什么将 Spring Boot 与 Docker 结合使用?

java - 如何让Java JPA创建实体?

java - 如何进行自定义 EJB/JPA 映射类型?

java - 使用 Jooq 和 JPA 的 @Column 注释验证字段长度