首页 > 解决方案 > JPARepository:deleteAll() 上没有发生任何事情;deleteAllInBatch() 作品

问题描述

在我的 Spring 集成测试中(使用 JUnit 5。我的测试类已注释@SpringBootTest(classes = {SecurityBeanOverrideConfiguration.class, XXXApp.class}),我试图调用repository.deleteAll()我的@AfterEach方法。

看日志中的SQL,好像什么都没执行;事实上,在接下来的测试中,无法创建具有相同 ID 的实体,因为它已经存在——这意味着某些东西阻止了数据库。正如其他问题所提到的,我玩过不同的事务类型(传播、隔离......),但无济于事。

不过,有趣的是,调用repository.deleteAllInBatch()而不是deleteAll() 确实有效:所有测试都通过了。

到底是怎么回事?

编辑:添加代码。

@Transactional
@SpringBootTest(classes = {SecurityBeanOverrideConfiguration.class, XXXApp.class})
public class DeviceResourceIT {
    @Autowired DeviceRepository deviceRepository;
    @Autowired DeviceService lotService;

    @Autowired private MappingJackson2HttpMessageConverter jacksonMessageConverter;
    @Autowired private ExceptionTranslator exceptionTranslator;

    private MockMvc mockMvc;
    private Logger log = LoggerFactory.getLogger(DeviceResourceIT.class);

    @PostConstruct
    void setup() {
        DeviceResource deviceResource = new DeviceResource(deviceService);
        mockMvc = MockMvcBuilders.standaloneSetup(deviceResource)
                                 .setControllerAdvice(exceptionTranslator)
                                 .setConversionService(createFormattingConversionService())
                                 .setMessageConverters(jacksonMessageConverter)
                                 .build();
    }

    @Test
    public void getLot() throws Exception
    {
        String lotID;
        String wrongLotID = "aRandomString";

        final List<DeviceRequestDTO> requests = Arrays.asList(
            new DeviceRequestDTO("l1", "ble1"),
            new DeviceRequestDTO("l2", "ble2"),
            new DeviceRequestDTO("l3", "ble3")
        );

        LotDTO lotDTO = new LotDTO(requests);

        MvcResult mvcResult = mockMvc.perform(post("/api/admin/lot")
                                                  .contentType(MediaType.APPLICATION_JSON)
                                                  .characterEncoding("utf-8")
                                                  .content(toJsonString(lotDTO)))
                                     .andDo(print())
                                     .andExpect(status().isOk())
                                     .andReturn();

        LotDTO returnedLotDTO = convertJsonBytes(mvcResult.getResponse().getContentAsByteArray(), LotDTO.class);
        lotID = returnedLotDTO.getId();
        log.info("{the lot id is : }" + lotID);

        // retrieve the Lot with the right lot ID
        mockMvc.perform(get("/api/admin/lot/{lot_id}", lotID))
               .andDo(print())
               .andExpect(status().isOk());


    }

    @AfterEach
    public void tearDown() {
        try {
            log.info("{}", deviceRepository.count());
            // Returns: 3

            deviceRepository.deleteAll();
            log.info("{}", deviceRepository.count());
            // Returns: 3
            // ... but we would expect to see 0, given the call to
            //     deleteAll() just above...

        } catch (Exception e) {
            Fail.fail(e.getMessage());
        }
    }

}

标签: hibernatespring-bootjpaspring-data-jpajunit5

解决方案


这是一个奇怪的诊断错误。事实证明,我实现了isNew()(of Persistable<T>) 错误,并且它返回了 true。

因此,调用SimpleJpaRepository#delete(T entity)执行此检查:

public void deleteAll(T entity)
   // [...]
   
   if (entityInformation.isNew(entity)) {
      return;
   }

   // actually delete...
   // [...]
}

我的实体正在返回isNew() = true,所以自然而然,存储库只是跳过了所有实体,从不删除它们。同时,deleteAllInBatch()不执行该检查。

为了解决这个问题,我已经停止实施PersistableSerializable改为创建我的实体。

2020 年 6 月更新:

无需切换到可序列化即可工作的最小实现示例:

class XXX implements Persistable<T>
{
    @Id private String id;


    // (getters, setters, logic, etc...)
    // implement the `T getId()` method for the Persistable<T> interface, 
    // most likely this will simply be a getter for the id field above


    // transient fields are not stored but provide information to Hibernate/JPA
    // regarding the status of this entity instance
    @Transient private Boolean persisted = false;

    @PostPersist @PostLoad void setPersisted() {
        persisted = true;
    }

    @Override public boolean isNew() {
        return !persisted;
    }

}

推荐阅读