java - Hibernate:在 SQLIntegrityConstraintViolationException 之后生成的 Id 保留在非持久实体中
问题描述
大家好!
前段时间我遇到了一个麻烦:如果存储库的保存方法失败,则由 Hibernate 注入到 bean 的标识符保留在 bean 中。这种行为可能会导致我们遇到一种情况,当我们将我们的非持久 bean 视为持久 bean 时。我很高兴知道避免这种情况的常见做法。
示例测试(spring boot + hibernate + oracle 数据库):
@Entity
@SequenceGenerator(name = "TEST_ENTITY_GENERATOR", allocationSize = 1, sequenceName = "TEST_ENTITY_SEQ")
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_ENTITY_GENERATOR")
private Long id;
@Column(nullable = false)
private String name;
public Long getId() {
return id;
}
}
@Repository
public interface TestEntityRepository extends JpaRepository<TestEntity, Long> {
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class RemainingIdTest {
@Autowired
private TestEntityRepository testEntityRepository;
@Test
public void test() {
TestEntity entity = new TestEntity();
try {
Assertions.assertThat(entity.getId()).isNull();
testEntityRepository.save(entity);
Assertions.fail("Save must fail");
} catch (DataIntegrityViolationException e) {
Assertions.assertThat(entity.getId()).isNotNull();
}
}
}
解决方案
一种可能的解决方案是使用org.hibernate.event.spi.PreInsertEventListener
我们可以将交易与处理器绑定的地方,如果交易失败,该处理器将清除您的实体。
例子:
@Component
public class IdentifierCleaner implements PreInsertEventListener {
@Autowired
private EntityManagerFactory entityManagerFactory;
@PostConstruct
private void init() {
SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(this);
}
@Override
public boolean onPreInsert(PreInsertEvent event) {
Object entity = event.getEntity();
event.getSession().getActionQueue().registerProcess(((success, session) -> {
if (!success) {
event.getPersister().resetIdentifier(
entity,
event.getId(),
event.getPersister().getVersion(entity),
event.getSession()
);
}
}));
return false;
}
}
推荐阅读
- r - 根据 1 个列表将列表列表转换为数据框 - 每个列表都不同
- firebase - 获取 firebase/firestore SERVERTIME 并转换为日期(月和日)
- ios - 我得到一个不能为 nil obj c 的参数的错误
- c - execve 系统调用中的 envp 不包含字符串“LS_C”的任何 execpt
- c++ - 为什么我的点没有在我的鼠标在 OpenGL 中的位置绘制?
- java - 从 Android Studio 运行的 java.lang.IllegalArgumentException(无错误消息)
- excel - 通过 Excel 中的公式函数将图像从另一列插入一列
- azure-functions - 无法查看用户生成的 Azure 函数 python 日志
- mysql - preg_replace():编译失败:Laravel Slug 中偏移 11 处的字符类中的无效范围
- python - 将 Conda R 指向已安装的 R 版本