2

私のユースケースは次のとおりです。アプリケーションユーザー(EntityManager.persist)を作成するときに、DBユーザーも作成して権限を付与する必要があります(そのため、休止状態のnativeQueryが必要です)。

両方の呼び出しで DAO を呼び出す Spring @Transactional メソッドがあります。

@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()を含む最初の行が実行されるとすぐに、DB ユーザー (user1@localhost) とともにデータベースに永続化された companyReportsUser が表示されます。

すべての nativeQuery が実行され、すぐに 1 つずつコミットされます。それらはコミットされているため、ロールバックできません。私の構成のどこにも自動コミットパラメーターが設定されていないため、Hibernate docs にあるように「false」であると想定しています。

  1. ネイティブ クエリを使用せずに @Transactional の動作をテストしましたが、想定どおりに動作します (RuntimeException をスローするとトランザクションがロールバックされ、データベースにデータが挿入されません)。

  2. デバッグ時に、実行中のトランザクションで呼び出されたときに永続化操作が実行を遅らせることがわかりました。

  3. ネイティブ クエリは、すぐに PreparedStatement を作成して実行するようです (少なくとも、どのような種類のキューも見つかりませんでした。

  4. 休止状態のネイティブ クエリと Spring トランザクションの間の相互作用が得られない可能性があると思いますが、トランザクションとネイティブ クエリに関する Spring と Hibernate のドキュメントを読むのに時間をかけましたが、役立つものは何も見つかりませんでした。

  5. データベースユーザーを作成し、ネイティブクエリよりも優れた権限を付与する方法があるかもしれません (ただし、何も見つかりませんでした)。

以下は私のアプリケーション構成です:

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
  • 春 3.2.2.RELEASE
  • mysql-connector-java-5.1.21
  • MySQL 5.5
4

1 に答える 1

3

MySQLでトランザクションがどのように機能するかを深く掘り下げた後、私は答えを見つけました:

問題は、ネイティブ SQL クエリ内の特定のステートメントにありました。

MySQL のドキュメントから:

13.3.3 暗黙的なコミットを引き起こすステートメント

  • データベース オブジェクトを定義または変更するデータ定義言語 (DDL) ステートメント (...CREATE TABLE、DROP DATABASE...)

  • mysql データベース内のテーブルを暗黙的に使用または変更するステートメント ( CREATE USER、 DROP USER 、および RENAME USER...、 GRANT、 REVOKE など)

  • ...

詳細はこちら:

http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html

そのアクションを 2 つの部分に分割することにしました。

  • 通常の hibernate ステートメント (トランザクション内)、
  • ネイティブ クエリ ステートメント

何か問題が発生した場合に備えて、2 番目の部分を再起動するためのツール/アクションをユーザーに提供します。

他の解決策は、DDL 操作に関するトランザクションをサポートする他の RDBMS に移行することです。

于 2015-04-02T12:53:20.227 に答える