hibernate - 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());
}
}
}
解决方案
这是一个奇怪的诊断错误。事实证明,我实现了isNew()
(of Persistable<T>
) 错误,并且它返回了 true。
因此,调用SimpleJpaRepository#delete(T entity)
执行此检查:
public void deleteAll(T entity)
// [...]
if (entityInformation.isNew(entity)) {
return;
}
// actually delete...
// [...]
}
我的实体正在返回isNew() = true
,所以自然而然,存储库只是跳过了所有实体,从不删除它们。同时,deleteAllInBatch()
不执行该检查。
为了解决这个问题,我已经停止实施Persistable
并Serializable
改为创建我的实体。
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;
}
}
推荐阅读
- ios - Firebase Analytics DebugView 收集的事件数据不完整
- powershell - Google Calendar API 的 PowerShell Invoke-RestMethod 仅返回 1 个事件
- javascript - 如何使用javascript以数组格式显示一组地图
- java - 默认的 micronaut 应用程序未运行
- xamarin - 当我从 xamarin 中的同步领域数据库中删除项目时出现空引用异常
- java - 将 C++ 向量传递给文件编写器
- python-3.x - 如何在 Bokeh 中按 z 列的分类数据制作散点图?像 matplotlib 'rainbow' 颜色图
- android - 我需要让 ImageView 消失。我怎么能做到?这是我需要做的最后一个
- python - 如何在取消停靠后调整几何图形后触发 QTreeView 的更新
- java - 给定一个HashMap,如何根据Java中的重复值对其进行过滤