0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring Boot + MyBatisでPlatformTransactionManagerをカスタム定義するとデータ挿入が反映されない問題とその解決方法

Posted at

今回は、Spring BootとMyBatisを使用して開発している際に直面した、PlatformTransactionManagerをカスタム定義した際にデータベースへの挿入が反映されない問題について共有します。この問題の原因と解決策を詳しく解説しますので、同じような課題を抱えている方の参考になれば幸いです。

問題の背景

Spring BootプロジェクトでMyBatisを使用してデータベース操作を行っていました。以下のような設定クラスを作成し、PlatformTransactionManagerをカスタム定義しました。

java

package com.burgerhub.config;

import lombok.Data;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@ConfigurationProperties("spring.datasource")
@Configuration
@MapperScan("com.burgerhub.mybatis.order")
@Data
public class MyBatisConfig {
    private String url;
    private String username;
    private String password;
    private String driverClassName;

    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
                .url(this.url)
                .username(this.username)
                .password(this.password)
                .driverClassName(this.driverClassName)
                .build();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource());
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("/com/burgerhub/mybatis/order/CustomOrderMapper.xml"));
        factoryBean.setConfigLocation(new ClassPathResource("/mybatis-config.xml"));
        return factoryBean.getObject();
    }

    // トランザクションマネージャーのBeanを定義
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

この設定を行ったところ、データベースへのINSERT操作が行われないという問題が発生しました。実際にはSQLが発行されているものの、データベースにはデータが反映されませんでした。しかし、PlatformTransactionManagerのBean定義を削除すると、挿入が正常に行われるようになりました。

なぜこの問題が発生したのか?

  1. PlatformTransactionManagerをカスタム定義するとSpring Bootの自動設定が上書きされる
    Spring Bootは、多くのコンポーネントを自動的に設定してくれる「自動設定」機能を持っています。データベース操作に関しても、DataSourceが存在する場合、DataSourceTransactionManagerが自動的に設定されます。しかし、ユーザーが明示的にPlatformTransactionManagerのBeanを定義すると、Spring Bootの自動設定はこのカスタム定義を優先し、デフォルトのトランザクションマネージャーを上書きします。

  2. トランザクション管理が適切に有効化されていない
    PlatformTransactionManagerを定義するだけでは、Springはトランザクション管理を自動的に適用しません。トランザクション管理を有効にするためには、以下の2つのステップが必要です。

@EnableTransactionManagementの追加
@Transactionalアノテーションの適用

今回の設定では、PlatformTransactionManagerを定義しましたが、@EnableTransactionManagementが設定されていなかったため、Springはトランザクション管理を認識せず、@Transactionalアノテーションも無視されました。その結果、トランザクションが開始されず、データベースへのINSERT操作がコミットされずにロールバックされてしまったのです。

質問1: PlatformTransactionManagerを定義するとSpringのデフォルトが適用されず、なぜデータの挿入ができなくなったのか?

自動コミット設定の変更

デフォルト動作: Spring Bootの自動設定では、DataSourceの自動コミットモードが有効になっており、各SQL操作が即座にコミットされます。

カスタム定義時: PlatformTransactionManagerをカスタム定義すると、Springは自動コミットを無効化し、明示的なトランザクション管理を期待します。しかし、@EnableTransactionManagement@Transactionalが設定されていないため、トランザクションが開始されてもコミットされず、結果としてロールバックされます。
トランザクション管理の有効化がされていない

@EnableTransactionManagementがないため、Springはトランザクション管理を有効化せず、@Transactionalアノテーションも認識しません。これにより、トランザクションの境界が設定されず、コミットが行われない状態になります。
解決策
@EnableTransactionManagementの追加

設定クラスに@EnableTransactionManagementを追加して、Springにトランザクション管理を有効化するよう指示します。

java

@Configuration
@EnableTransactionManagement
@MapperScan("com.burgerhub.mybatis.order")
@ConfigurationProperties("spring.datasource")
@Data
public class MyBatisConfig {
    ///コード
}

@Transactionalの適用

トランザクションが必要なサービス層やメソッドに@Transactionalアノテーションを付与します。

java

@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Transactional
    public void createOrder(Order order) {
        orderMapper.insertOrder(order);
        // 他のデータ操作
    }
}

デフォルトのトランザクションマネージャーを使用する

特にカスタムのトランザクションマネージャーが必要ない場合、PlatformTransactionManagerのBean定義を削除し、Spring Bootの自動設定に任せることで問題を回避できます。

質問2: 自動コミットが無効な場合、@Transactionalが付与されていなくてもデータは挿入されるのではないか?

自動コミットとトランザクションの関係

自動コミットが有効な場合: 各SQL文が実行されると即座にコミットされ、データベースに反映されます。この場合、@Transactionalアノテーションがなくてもデータは挿入されます。

自動コミットが無効な場合: トランザクション内で明示的にコミット(またはロールバック)を行う必要があります。@Transactionalが付与されていないと、トランザクションが開始されてもコミットされず、接続がクローズされる際にロールバックされます。結果として、データベースにはデータが挿入されません。

なぜ自動コミットが無効になるのか?
PlatformTransactionManagerが定義されている場合、Springはトランザクション管理を行うためにデータソースの自動コミットを無効化します。これにより、@Transactionalが適用されていない場合、デフォルトでコミットが行われず、ロールバックされてしまうのです。

まとめ

PlatformTransactionManagerの定義: カスタムでPlatformTransactionManagerを定義すると、Springは自動コミットを無効化し、明示的なトランザクション管理を期待します。

トランザクション管理の有効化: @EnableTransactionManagement@Transactionalを適切に設定することで、トランザクションが正しくコミットされ、データベースへの挿入が反映されます。

自動コミットとの関係: 自動コミットが無効な状態では、トランザクションが明示的にコミットされない限り、データはデータベースに反映されません。

Spring BootとMyBatisを使用する際に、PlatformTransactionManagerをカスタム定義すると、トランザクション管理が適切に有効化されていない場合、データベースへの挿入が反映されない問題が発生します。これを防ぐためには、以下の点に注意する必要があります。

@EnableTransactionManagementの設定: トランザクション管理を有効にするために、設定クラスに@EnableTransactionManagementを追加します。

@Transactionalの適用: トランザクションが必要なサービス層やメソッドに@Transactionalアノテーションを付与します。

デフォルト設定の利用: 特別な要件がない場合は、Spring Bootの自動設定に任せることで、トランザクション管理に関する設定ミスを防ぐことができます。

これらのポイントを押さえることで、Spring BootとMyBatisを使用したアプリケーションでのトランザクション管理を適切に行い、データの整合性を保ちながら正しくデータベース操作を実行できるようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?