Java 如何使Hibernate在发生异常时不回滚

Java 如何使Hibernate在发生异常时不回滚,java,hibernate,Java,Hibernate,在MSSQL中运行以下SQL if将成功插入第一行和第三行: BEGIN TRAN INSERT ... -- valid data INSERT ... -- invalid data (e.g. over column width) INSERT ... -- valid data COMMIT 即使第二行在事务中失败,您仍然可以在表中的提交之后看到这两行包含一些有效数据 但是,在Hibernate中尝试类似的操作时,它会回滚整个事务。有没有办法告诉Hibernate不要回滚失败的行,并像

在MSSQL中运行以下SQL if将成功插入第一行和第三行:

BEGIN TRAN
INSERT ... -- valid data
INSERT ... -- invalid data (e.g. over column width)
INSERT ... -- valid data
COMMIT
即使第二行在事务中失败,您仍然可以在表中的提交之后看到这两行包含一些有效数据

但是,在Hibernate中尝试类似的操作时,它会回滚整个事务。有没有办法告诉Hibernate不要回滚失败的行,并像MSSQL那样提交其余的行

e、 g


我这样做的方式是将您的逻辑划分为不同的函数,并在持久化函数(而不是主函数)中打开事务

我在代码中看到的主要问题是,除了为每个操作打开一个事务外,还定义了一个块事务

以下是我的片段:

persistEntity(new MyEntity("good"));
persistEntity(new MyEntity("bad"));
persistEntity(new MyEntity("good"));

...

private void persistEntity(MyEntity entity){
    EntityTransaction transaction = em.getTransaction();
    transaction.begin();
    em.persist(entity);
    transaction.commit();    
}
这样,它将只为坏实体回滚,并继续与另一个实体同步。如果要记录异常,还可以在persistEntity方法中添加try catch


有趣的是,如果您使用Spring,您可以为持久化操作创建另一个@Component,并且只将@Transactional添加到持久化方法中,这样您就不必自己管理事务了。

不要这样做,这通常是错误的,首先,只需查看事务的实际范围。

您可以编写代码,在打开autocommit的情况下一次运行一条语句,而不使用@Transactional。。。然后可能捕获任何异常,并在运行时将其丢弃。但这句话中的几乎所有内容都很麻烦,甚至连一个负责任的开发人员都无法想象,这会影响到你的整个应用程序。弗拉维乌斯的帖子将更精细一些,用于处理显式较小的事务,这也是一个很好的方法

正如其他人所评论的那样,这不是一个长期的伟大计划,与正确编写程序的许多方法以及事务的好处和目的背道而驰。也许,如果您计划仅将此作为一次性数据摄取计划使用,那么您可以再次非常小心在生产级应用程序中使用这些模式

在受到足够的警告之后,您可以在这里阅读更多关于自动提交的信息,并且一定要阅读关于为什么您可能不应该使用它的帖子链接


您可以通过在hibernate配置xml文件中添加以下属性来实现

<property name="hibernate.connection.autocommit" value="true"/>
那么我建议对代码进行一些修改。最好在循环中添加实体,并在每个事务中捕获异常。

这在同一事务中是不可能的。Hibernate根本不允许这样做。语句中的错误导致异常,Hibernate无法从中恢复

如果JPA EntityManager或特定于Hibernate的会话抛出异常,包括任何JDBC SQLException,则必须立即回滚数据库 事务并关闭当前EntityManager或会话

JPA EntityManager或Hibernate会话的某些方法不会使持久性上下文保持一致状态。根据经验,Hibernate抛出的任何异常都不能视为可恢复的。通过在finally块中调用close()方法,确保会话将被关闭

现在这是Hibernate的限制(设计决策),而不是底层JDBC或数据库堆栈的限制。因此,直接使用JDBC完全可以满足您的需要。如果获得该行为非常重要,您可以考虑使用JDBC调用来访问代码的这一部分。在那里,您可以像在SQL客户机中一样执行此操作:打开事务、发出语句、手动捕获任何异常并“忽略”它们,最后提交事务

示例代码:

Session Session=em.unwrap(Session.class);
会话.工作(连接->{
//手动提交模式
connection.setAutoCommit(false);
executeInsertIgnoringError(连接,新对象[]{123,null,“abc”});
executeInsertIgnoringError(连接,新对象[]{…});
...
commit();
});
private void executeInsertIgnoringError(连接,对象[]值){
try(PreparedStatement stmt=
connection.prepareStatement(“插入MY_实体值(?,,,,…)”){
对于(int i=0;i
如果在MSSQL中运行以下SQL
。。。这种观察对我来说似乎很奇怪,我本以为如果事务的任何部分失败,SQL Server会回滚整个事务。@TimBiegeleisen我想这取决于什么类型的异常,在这种情况下,MSSQL决定不回滚所有事务。即使在这种情况下,我也会质疑Hibernate会话,不是SQL Server。也许Hibernate将工作划分为多个数据库事务,因此您当前的观察结果似乎有些误解。Hibernate会回滚(我不想要);在SSMS中直接运行SQL语句会毫无例外地插入这些语句,而不会回滚(这就是我希望并且希望对Hibernate执行相同操作的原因,因此提出了这个问题)。我想补充一点,就是MSSQL的行为很奇怪,而且出人意料。Hibernate在事务的预期行为方面做得完全正确。是的,但是不是很多事务而不是一个事务吗?以您的方式使用事务有什么好处?@user1589188您知道事务的核心概念是原子性,对吗?要么所有操作都成功,要么没有成功。您明确要求放弃此属性,因此颠倒了问题:您希望从坚持单一交易中得到什么?@crizzis在我的测试中,执行n个单独的插入比执行
<property name="hibernate.connection.autocommit" value="true"/>
@Transactional(dontRollbackOn={SQLException.class, NOResultException.class})