首页 > 解决方案 > 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();
        }
    }
}

标签: javaspringhibernate

解决方案


一种可能的解决方案是使用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;
    }
}

推荐阅读