如果不在事务中,休眠 session.update 不起作用
Posted
技术标签:
【中文标题】如果不在事务中,休眠 session.update 不起作用【英文标题】:Hibernate session.update not working if not in transaction 【发布时间】:2017-10-19 10:28:02 【问题描述】:我的休眠配置:
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.mysqlDialect");
properties.put("hibernate.hbm2ddl.auto", "validate");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.id.new_generator_mappings", "false");
properties.put("hibernate.connection.autocommit", "true");
properties.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
properties.put("hibernate.connection.url", DBConnection.url);
properties.put("hibernate.connection.username", DBConnection.username);
properties.put("hibernate.connection.password", DBConnection.password);
代码示例:
// pattern 1
Session s = sessionFactory.openSession();
ObjectA A = s.load(ObjectA.class, pk);
A.setAttr("abc");
s.update(A);
s.close();
// pattern 2
Session s = sessionFactory.openSession();
s.beginTransaction();
ObjectA A = s.load(ObjectA.class, pk);
A.setAttr("abc");
s.update(A);
s.close();
// pattern 3
Session s = sessionFactory.openSession();
Transaction tx = s.beginTransaction();
ObjectA A = s.load(ObjectA.class, pk);
A.setAttr("abc");
s.update(A);
tx.commit();
s.close();
请忽略我的编译错误。我在 Web 应用程序中使用休眠(没有spring
),并且没有使用事务,因为我使用的是MySql
数据库和MySql
autocommit
是真的,所以反过来,在休眠中,我将其设置为@987654327 @true 也是。 (我也在使用mysql-connector-java-5.1.23-bin.jar
)。
三种模式,我只能得到模式3的作品。我现在完全糊涂了。我有几个问题:
1) 我不明白为什么模式 1 不起作用,我所有的 select
(通过休眠 CriteriaBuilder
或 load
)和 insert
(通过休眠 session.save
)都有效,但只有更新不起作用没用。
2) 好的,然后我尝试使用模式 2 之类的事务,我的休眠 auto-commit
是真的,所以我假设当我关闭会话时,事务应该自动提交,但它不起作用。为什么?
3) 模式 3 有效,为什么我需要事务管理器?我希望jdbc
在每个事务中执行每个查询(一个事务中一个sql),我不担心性能,但我必须在此处包含事务,为什么?
对于模式1和2,我发现update
脚本甚至没有生成(基于hibernate日志),问题不是因为脚本生成但提交失败。不明白为什么?请帮忙...
PS:
经过反复试验,总结一些要点以供将来参考:
1) Hibernate 只会在调用session.flush()
而不是tx.commit()
时生成sql 脚本,并且必须在事务块中调用session.flush()
。没有事务,就会导致异常。如果刷新模式为自动,则不需要显式刷新,commit()
将触发刷新。
2) Hibernate Transaction 不等同于数据库事务,经过一些尝试,我发现,如果 hibernate autocommit 为 false,是的,它们在功能上是等效的,并且通过 JDBC 生成相应的begin transaction
脚本并发送到数据库(只是我的猜测)。如果 hibernate autocommit 为 true,则不会启动 begin transaction
,尽管我们在 hibernate Transaction tx = s.beginTransaction()
中声明了它,所有查询都将自动提交,rollback
将不起作用。
3)我的情况的原因,session.save()
(还有select
)在没有事务的情况下工作,这有点特别,因为必须触发save
才能获得表标识符(主键)和所以即使没有flush也会生成sql脚本。
4) 对于模式 2,我误会了,autocommit 不代表autocommit upon session closed
,它的真正含义应该是autocommit upon each sql reach database
。所以模式 2 不起作用,因为没有tx.commit
,这意味着没有刷新,所以没有生成 sql 脚本。 (tx.commit
是否会在session.close
上自动调用,这取决于供应商的实现,有些会回滚。)
结论,在Hibernate中无论如何都需要事务块。
【问题讨论】:
我的猜测是 Hibernate 本身并不处于自动提交模式,尽管您已经设置了hibernate.connection.autocommit
连接字符串。顺便说一句,将 Hibernate 设置为自动提交模式可能不是一个好主意,因为如果出现问题,它不会回滚任何东西。
【参考方案1】:
我觉得你有点困惑。事务(org.hibernate.transaction)不完全是数据库事务。 当您刷新会话(Session.flush)以将指令绑定到单个数据库事务中时,休眠会使用此类对象。换句话说,不要将 Hibernate Session 与 DB session 混淆,但不要将 hibernate Sessio 与 db connection 混淆。 最重要的是,hibernate通过规范只为hibernate事务之间包含的内容生成sql代码。这就是为什么模式 A 和 B 不起作用并且不会生成 sql 代码的原因。更具体地说,模式 B 中的自动提交没有影响,因为从未生成 sql cod。此外,根据休眠最佳实践,即使是简单的选择指令,您也必须记住打开和关闭事务。顺便说一句,即使没有事务,选择也应该可以工作,但是您可能会遇到一些麻烦。
为了更好地理解这个概念,我们可以恢复架构:
hibernate session:是一个容器,它把你的hibernate对象和你的db操作作为java对象,还有很多其他的东西。 hibernate 事务:是一个引用休眠会话的事务对象。 db 连接:是您与 DB 的连接 连接池:是一组db连接。可以通过以下步骤恢复会话刷新时发生的情况:
-
从连接池获取连接
对于每个提交的
会话中的事务从池中获取数据库连接,
生成 sql 命令并发送到数据库
数据库连接被放回池中
这只是一个小回顾,但希望这会有所帮助 r.
【讨论】:
感谢您的重述,我认为我最好始终遵循模式 3,因为我无法真正找到关于休眠autocommit
的良好解释,根据我的情况,我不明白为什么我的模式 1,session.save()
仍然可以在没有事务的情况下工作...
我忘记了,autocommit
与休眠事务无关,但它用于(不推荐)在每次 JDBC 调用后释放连接,您可以在此处找到详细信息 [docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/…跨度>
我想你提到的是hibernate.connection.release_mode
。对于自动提交,hibernate 只有两行解释,这让我很困惑:Enables autocommit for JDBC pooled connections (it is not recommended). e.g. true | false
以上是关于如果不在事务中,休眠 session.update 不起作用的主要内容,如果未能解决你的问题,请参考以下文章
REQUIRES_NEW 如果不在一个事务那么自己创建一个事务 如果在一个事务中 自己在这个大事务里面在创建一个子事务 相当于嵌套事务 双层循环那种