2017-04-24 32 views
0

TestServiceサービス実装のsavePerson()メソッド内で同じトランザクション内から2つのレコード(Person)を挿入しようとしています。ロールバック時のSpringトランザクションでDBが以前の状態に復元されない

また、PERSONテーブルのfirst_nameカラムに一意のキーインデックスを作成しました。同じ名字の2人を挿入しようとすると重複キーエラーが発生し、トランザクションのロールバックをテストできます。

savePerson()メソッドを実行すると、JTAは重複したキーによりロールバックを開始しますが、DBでもロールバックを開始しません。私はここで何が欠けているのですか?

MySQLの一般的なログには、「START TRANSACTION」の後に「ROLLBACK」/「COMMIT」が続くSQLはありません。また、WildFlyサーバーを起動すると、ログSQL文 "SET autocommit = 1"に表示されます。 @TransactionalアノテーションはDBレベルでもこの​​処理を処理することになっていますか?

私はspring-boot、hibernate、およびMySQL jdbcを使用しています。私はMySQLのInnoDBテーブルを使用してい

15:00:54,060 DEBUG default task-12 jta.JtaTransactionManager:367 - Creating new transaction with name [com.example.test.TestServiceImpl.savePerson]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-java.lang.Exception 
15:00:54,066 DEBUG default task-12 support.DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'transactionManager' 
15:00:54,067 DEBUG default task-12 jta.JtaTransactionManager:476 - Participating in existing transaction 
15:00:54,083 INFO default task-12 stdout:71 - Hibernate: insert into person (age, first_name, last_name) values (?, ?, ?) 
15:00:54,125 DEBUG default task-12 jta.JtaTransactionManager:476 - Participating in existing transaction 
15:00:54,126 INFO default task-12 stdout:71 - Hibernate: insert into person (age, first_name, last_name) values (?, ?, ?) 
15:00:54,134 WARN default task-12 spi.SqlExceptionHelper:129 - SQL Error: 1062, SQLState: 23000 
15:00:54,135 ERROR default task-12 spi.SqlExceptionHelper:131 - Duplicate entry 'John' for key 'idx_first_name_unique' 
15:00:54,137 DEBUG default task-12 jta.JtaTransactionManager:858 - Participating transaction failed - marking existing transaction as rollback-only 
15:00:54,137 DEBUG default task-12 jta.JtaTransactionManager:1074 - Setting JTA transaction rollback-only 
15:00:54,138 DEBUG default task-12 support.DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'entityManagerFactory' 
15:00:54,140 DEBUG default task-12 jta.JtaTransactionManager:851 - Initiating transaction rollback 

にconsole.log。ここでは、サーバのログは識別子#22と同じ接続で(いや、何もSETの自動コミット= 0、無ROLLBACKを??? COMMITません)です:

  22 Connect [email protected] as anonymous on test 
      22 Query /* mysql-connector-java-6.0.6 (Revision: 3dab84f4d9bede3cdd14d57b99e9e98a02a5b97d) */SELECT @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout, @@license AS license, @@lower_case_table_names AS lower_case_table_names, @@max_allowed_packet AS max_allowed_packet, @@net_buffer_length AS net_buffer_length, @@net_write_timeout AS net_write_timeout, @@query_cache_size AS query_cache_size, @@query_cache_type AS query_cache_type, @@sql_mode AS sql_mode, @@system_time_zone AS system_time_zone, @@time_zone AS time_zone, @@tx_isolation AS tx_isolation, @@wait_timeout AS wait_timeout 
      22 Query SELECT @@session.autocommit 
      22 Query SET NAMES utf8mb4 
      22 Query SET character_set_results = NULL 
      22 Query SET collation_connection = utf8_general_ci 
      22 Query SET autocommit=1 
      22 Query SET sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES' 
      22 Query SET autocommit=1 
170424 15:02:07 22 Query SHOW FULL TABLES FROM `test` LIKE '%' 
      22 Query SHOW FULL TABLES FROM `test` LIKE '%' 
      22 Query SHOW FULL COLUMNS FROM `person` FROM `test` LIKE '%' 
      22 Query SHOW INDEX FROM `person` FROM `test` 
170424 15:02:24 22 Query SELECT 1 
      22 Query insert into person (age, first_name, last_name) values (40, 'John', 'Smith') 
      22 Query insert into person (age, first_name, last_name) values (40, 'John', 'Smith') 

代わりに、このようなものでなければなりません:

set autocommit = 0 
insert into ... 
insert into ... 
rollback 

TestServiceImpl.java

package com.example.test; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Transactional; 

import com.example.test.Person; 

@Service 
public class TestServiceImpl implements TestService { 

    @Autowired 
    private TestRepository testRepository; 

    @Override 
    @Transactional(rollbackFor = Exception.class) 
    public void savePerson() { 
     Person p1 = new Person("John", "Smith", 40); 
     Person p2 = new Person("John", "Smith", 40); 

     testRepository.save(p1); 
     testRepository.save(p2); 
    } 

} 

TestService.java

package com.example.test; 

public interface TestService { 
    public void savePerson(); 
} 

TestRepository.java:

package com.example.test; 

import org.springframework.data.jpa.repository.JpaRepository; 
import org.springframework.stereotype.Repository; 

@Repository 
public interface TestRepository extends JpaRepository<Person, Integer>{ 

} 

Application.java:

package com.example.test; 

import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.boot.builder.SpringApplicationBuilder; 
import org.springframework.boot.web.support.SpringBootServletInitializer; 
import org.springframework.transaction.annotation.EnableTransactionManagement; 

@SpringBootApplication 
@EnableTransactionManagement 
public class Application extends SpringBootServletInitializer { 

    @Override 
    protected SpringApplicationBuilder configure(
      SpringApplicationBuilder application) { 
     return application.sources(Application.class); 
    } 

    public static void main(String[] args) { 

     SpringApplication.run(Application.class, args); 
    } 
} 

Person.javaバージョンと

package com.example.test; 

import javax.persistence.Column; 
import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.Table; 
import javax.persistence.Index; 

@Entity 
@Table(name = "PERSON", indexes = { @Index(name = "idx_first_name_unique", columnList = "first_name", unique = true), }) 
public class Person { 

    @Id 
    @GeneratedValue 
    @Column(name = "id") 
    private Integer id; 

    @Column(name = "first_name") 
    private String firstName; 

    @Column(name = "last_name") 
    private String lastName; 

    @Column(name = "age") 
    private Integer age; 

    public Person() { 
    } 

    public Person(String firstname, String lastname, Integer age) { 
     this.firstName = firstname; 
     this.lastName = lastname; 
     this.age = age; 
    } 

    public Integer getId() { 
     return id; 
    } 

    public void setId(Integer id) { 
     this.id = id; 
    } 

    public String getFirstName() { 
     return firstName; 
    } 

    public void setFirstName(String firstName) { 
     this.firstName = firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 

    public void setLastName(String lastName) { 
     this.lastName = lastName; 
    } 

    public Integer getAge() { 
     return age; 
    } 

    public void setAge(Integer age) { 
     this.age = age; 
    } 

    @Override 
    public String toString() { 
     return "Person [firstName=" + firstName + ", lastName=" + lastName 
       + ", age=" + age + "]"; 
    } 

} 

POMの依存関係:

<dependencies> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-web</artifactId> 
      <version>1.5.2.RELEASE</version> 
      <exclusions> 
       <exclusion> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-logging</artifactId> 
       </exclusion> 
      </exclusions> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-log4j2</artifactId> 
      <version>1.5.2.RELEASE</version> 
     </dependency> 
     <dependency> 
      <groupId>com.vaadin</groupId> 
      <artifactId>vaadin-spring-boot-starter</artifactId> 
      <version>2.0.1</version> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-tomcat</artifactId> 
      <version>1.5.2.RELEASE</version> 
      <scope>provided</scope> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-test</artifactId> 
      <version>1.5.2.RELEASE</version> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-jpa</artifactId> 
      <version>1.5.2.RELEASE</version> 
     </dependency> 
     <dependency> 
      <groupId>org.hibernate.javax.persistence</groupId> 
      <artifactId>hibernate-jpa-2.1-api</artifactId> 
      <version>1.0.0.Final</version> 
     </dependency> 
     <dependency> 
      <groupId>org.hibernate</groupId> 
      <artifactId>hibernate-entitymanager</artifactId> 
      <version>5.2.10.Final</version> 
     </dependency> 
     <dependency> 
      <groupId>org.hibernate</groupId> 
      <artifactId>hibernate-core</artifactId> 
      <version>5.2.10.Final</version> 
     </dependency> 
     <dependency> 
      <groupId>mysql</groupId> 
      <artifactId>mysql-connector-java</artifactId> 
      <version>6.0.6</version> 
     </dependency> 
</dependencies> 

application.properties:しばらく掘った後

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test 
spring.datasource.username=test 
spring.datasource.password=test 
spring.datasource.driverClassName=com.mysql.jdbc.Driver 

spring.jpa.show-sql=true 
spring.jpa.hibernate.ddl-auto=update 
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy 
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57InnoDBDialect 
+0

API呼び出しJDBC接続 'setAutoCommit(false)'を使ってトランザクションを開始します。 'START TRANSACTION'が表示されない理由は、JDBCドライバが古いMySQLバージョンhttps://github.com/mysql/mysql-connector-jとの下位互換性から' SET autocommit = 0'を残して実装しているからです/blob/6.0.6/src/main/java/com/mysql/cj/jdbc/ConnectionImpl.java#L3773 – coladict

+0

さらに、MySQLでは、テーブル用に使用しているストレージエンジンが重要です。 InnoDBとNDBのみがトランザクションをサポートします。 https://dev.mysql.com/doc/refman/5.5/en/storage-engines.html – coladict

+0

それを明確にしていただきありがとうございます。しかし、やはり、WildFlyを起動すると、MySQLサーバのログには "SET autocommit = 1"しか表示されません。 "SET autocommit = 0"になっていませんか?とにかく、トランザクションはDBでロールバックされません。自動コミットとの接続で発行された挿入がtrueに設定されていることがわかります。つまり、それぞれが単一のトランザクションとして実行されています。 DBレベルでもトランザクションを動作させる方法はわかりません。 –

答えて

0

、私はそれを見つけました!最後に。 :)

1)標準のDBトランザクション(BEGIN/COMMIT/ROLLBACK)を使用するには、JTAを無効にする必要があります。 spring-bootでは、application.propertiesで実行できます。

application.properties:

spring.jta.enabled = false 

2)JTAを使用するときは、XA 2フェーズは、特定のデータベースのコミットプロトコルを有効にする必要があります。デプロイされたアプリケーションはWildfly AS上でJTA TransactionManagerの実装を使用しているため、すべてのデータソースをそこに設定する必要があります。また、application.propertiesでは、JNDI名を使用してデータソースを参照するだけで十分です。

Wildflyスタンドアロン-demo.xml:

... 
      <datasources> 
       ... 
       <xa-datasource jndi-name="java:jboss/datasources/test" pool-name="TestXADS" enabled="true"> 
        <xa-datasource-property name="url"> 
         jdbc:mysql://127.0.0.1:3306/test 
        </xa-datasource-property> 
        <driver>mysql</driver> 
        <xa-pool> 
         <min-pool-size>10</min-pool-size> 
         <max-pool-size>20</max-pool-size> 
         <prefill>true</prefill> 
        </xa-pool> 
        <security> 
         <user-name>test</user-name> 
         <password>test</password> 
        </security> 
       </xa-datasource> 
       <drivers> 
        ... 
        <driver name="mysql" module="com.mysql"> 
         <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class> 
        </driver> 
       </drivers> 
      </datasources> 
... 

モジュール/ COM/mysqlの/メイン/ module.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<module xmlns="urn:jboss:module:1.0" name="com.mysql"> 
    <resources> 
     <resource-root path="mysql-connector-java-5.1.41-bin.jar"/> 
    </resources> 
    <dependencies> 
     <module name="javax.api"/> 
     <module name="javax.transaction.api"/> 
    </dependencies> 
</module> 

application.properties:

spring.datasource.jndi-name=java:jboss/datasources/test 
関連する問題