本机查询 executeUpdate 似乎在 Spring 事务中提交数据
Native query executeUpdate seems to commit data in Spring transaction
我的用例如下:
创建应用程序用户时(EntityManager.persist)我还必须创建一个数据库用户并授予他权限(这就是我需要 hibernate nativeQuery 的原因)。
我有一个 Spring @Transactional 方法,它通过两个调用调用我的 DAO:
@Transactional
public Integer createCompany(Company company) throws Exception {
companyDao.createReportUser(ReportUser user);
...
}
我的 DAO 方法如下所示:
getEm().persist(companyReportsUser);
getEm().createNativeQuery("CREATE USER user1@localhost IDENTIFIED BY :password").setParameter("password", password).executeUpdate();
getEm().createNativeQuery("GRANT SELECT ON appdb.v_company TO user1@localhost").executeUpdate();
//several grants
现在,一旦第一行 executeUpdate() 被执行,我就可以在数据库中看到持久化的 companyReportsUser 以及数据库用户 (user1@localhost) .
所有的nativeQueries都被执行并立即一一提交。由于它们已提交,因此无法回滚。
我的配置中的任何地方都没有设置 auto-commit 参数,所以我假设它是 Hibernate 文档中的 'false'。
我已经在没有本机查询的情况下测试了@Transactional 的行为,它按预期工作(当我抛出 RuntimeException 并且没有数据插入数据库时事务被回滚)
调试时,我发现持久化操作在 运行 事务中调用时会延迟执行。
本机查询似乎会立即创建并执行 PreparedStatement(至少我没有找到任何类型的队列。
我想我可能无法理解 hibernate 本机查询和 Spring 事务之间的交互,但是我花时间阅读了 Spring 和 Hibernate 文档关于事务和本机查询,但没有找到任何对我有帮助的东西。
也许有比本地查询更好的创建数据库用户和授予权限的方法(虽然我没有找到)
以下是我的应用程序配置:
applicationContext.xml
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory">
<ref local="entityManagerFactory" />
</property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="domainPU" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.user.name}" />
<property name="password" value="${db.user.password}" />
<property name="validationQuery" value="select 1 as dbcp_connection_test" />
<property name="testOnBorrow" value="true" />
</bean>
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="domainPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.domain.Entity1</class>
....
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.jdbc.batch_size" value="100"></property>
<property name="hibernate.order_inserts" value="true"></property>
<property name="hibernate.order_updates" value="true"></property>
<property name="hibernate.c3p0.min_size" value="5"></property>
<property name="hibernate.c3p0.max_size" value="30"></property>
<property name="hibernate.c3p0.timeout" value="300"></property>
<property name="hibernate.c3p0.max_statements" value="100"></property>
<property name="hibernate.c3p0.idle_test_period" value="${hibernate.c3p0.idle_test_period}"></property>
</properties>
</persistence-unit>
使用的库:
- 休眠 4.1.6.FINAL
- Spring 3.2.2.RELEASE
- mysql-连接器-java-5.1.21
- MySQL 5.5
在深入了解交易在 MySQL 中的运作方式后,我找到了答案:
问题出在本机 sql 查询中的特定语句中。
来自 MySQL 文档:
13.3.3 Statements That Cause an Implicit Commit
Data definition language (DDL) statements that define or modify database objects (...CREATE TABLE, DROP DATABASE...)
Statements that implicitly use or modify tables in the mysql database (CREATE USER, DROP USER, and RENAME USER..., GRANT, REVOKE, ...)
...
此处有更多详细信息:
http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html
我决定将该操作分为两部分:
- 普通休眠语句(在事务内),
- 原生查询语句
并为用户提供 tool/action 以在出现问题时重新调用第二部分。
其他解决方案是迁移到支持围绕 DDL 操作的事务的其他 RDBMS。
我的用例如下: 创建应用程序用户时(EntityManager.persist)我还必须创建一个数据库用户并授予他权限(这就是我需要 hibernate nativeQuery 的原因)。
我有一个 Spring @Transactional 方法,它通过两个调用调用我的 DAO:
@Transactional
public Integer createCompany(Company company) throws Exception {
companyDao.createReportUser(ReportUser user);
...
}
我的 DAO 方法如下所示:
getEm().persist(companyReportsUser);
getEm().createNativeQuery("CREATE USER user1@localhost IDENTIFIED BY :password").setParameter("password", password).executeUpdate();
getEm().createNativeQuery("GRANT SELECT ON appdb.v_company TO user1@localhost").executeUpdate();
//several grants
现在,一旦第一行 executeUpdate() 被执行,我就可以在数据库中看到持久化的 companyReportsUser 以及数据库用户 (user1@localhost) .
所有的nativeQueries都被执行并立即一一提交。由于它们已提交,因此无法回滚。 我的配置中的任何地方都没有设置 auto-commit 参数,所以我假设它是 Hibernate 文档中的 'false'。
我已经在没有本机查询的情况下测试了@Transactional 的行为,它按预期工作(当我抛出 RuntimeException 并且没有数据插入数据库时事务被回滚)
调试时,我发现持久化操作在 运行 事务中调用时会延迟执行。
本机查询似乎会立即创建并执行 PreparedStatement(至少我没有找到任何类型的队列。
我想我可能无法理解 hibernate 本机查询和 Spring 事务之间的交互,但是我花时间阅读了 Spring 和 Hibernate 文档关于事务和本机查询,但没有找到任何对我有帮助的东西。
也许有比本地查询更好的创建数据库用户和授予权限的方法(虽然我没有找到)
以下是我的应用程序配置:
applicationContext.xml
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory">
<ref local="entityManagerFactory" />
</property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="domainPU" />
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.user.name}" />
<property name="password" value="${db.user.password}" />
<property name="validationQuery" value="select 1 as dbcp_connection_test" />
<property name="testOnBorrow" value="true" />
</bean>
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="domainPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.domain.Entity1</class>
....
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.jdbc.batch_size" value="100"></property>
<property name="hibernate.order_inserts" value="true"></property>
<property name="hibernate.order_updates" value="true"></property>
<property name="hibernate.c3p0.min_size" value="5"></property>
<property name="hibernate.c3p0.max_size" value="30"></property>
<property name="hibernate.c3p0.timeout" value="300"></property>
<property name="hibernate.c3p0.max_statements" value="100"></property>
<property name="hibernate.c3p0.idle_test_period" value="${hibernate.c3p0.idle_test_period}"></property>
</properties>
</persistence-unit>
使用的库:
- 休眠 4.1.6.FINAL
- Spring 3.2.2.RELEASE
- mysql-连接器-java-5.1.21
- MySQL 5.5
在深入了解交易在 MySQL 中的运作方式后,我找到了答案:
问题出在本机 sql 查询中的特定语句中。
来自 MySQL 文档:
13.3.3 Statements That Cause an Implicit Commit
Data definition language (DDL) statements that define or modify database objects (...CREATE TABLE, DROP DATABASE...)
Statements that implicitly use or modify tables in the mysql database (CREATE USER, DROP USER, and RENAME USER..., GRANT, REVOKE, ...)
...
此处有更多详细信息:
http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html
我决定将该操作分为两部分:
- 普通休眠语句(在事务内),
- 原生查询语句
并为用户提供 tool/action 以在出现问题时重新调用第二部分。
其他解决方案是迁移到支持围绕 DDL 操作的事务的其他 RDBMS。