Springboot+Mybatis-JPA와 JDBC 충돌로 인해 @Transactional 트랜잭션이 적용되지 않고 롤백되지 않는 문제 해결

Springboot+Mybatis-JPA와 JDBC 충돌로 인해 @Transactional 트랜잭션이 적용되지 않고 롤백되지 않는 문제 해결

환경

  • 스프링부트 1.5.6
  • 마이봇 3.5.3
  • 마이바티스 플러스 3.3.1

문제의 원인

트랜잭션 관리자를 인쇄하면 Mybatis 환경의 트랜잭션 관리자 인스턴스가 분명히 잘못된 JpaTransactionManager 개체라는 것을 알 수 있습니다.

Mybatis 트랜잭션에서 Jpa 트랜잭션 관리를 사용하면 트랜잭션 실패 및 롤백이 실패하고

ORM 트랜잭션 관리자의 해당 관계는 다음과 같습니다.

  • 마이바티스 -> org.springframework.jdbc.datasource.DataSourceTransactionManager@50061d56
  • JPA -> org.springframework.orm.jpa.JpaTransactionManager@204d101

해결책

@Bean 구성을 추가하고 jdbc 트랜잭션 관리자 DataSourceTransactionManager를 등록합니다. 여기서 dataSource는 프레임워크에 의해 자동으로 삽입됩니다.

프로젝트에서 jpa와 mybatis가 혼합된 경우 등록 객체에 주석을 달기 위해 @Bean("xxx")을 사용해야 합니다. dataSource, sqlSessionFactory 및 DataSourceTransactionManager 구성에 표시합니다.

//注册
@Configuration
public class MybatisConfig {
    @Bean("jdbcTransactionManager")
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
//使用
@Transactional(value = "jdbcTransactionManager",rollbackFor = {Exception.class})
public void TestTran4Salf(){
    ...
}

문제 해결 프로세스

출력 SQL 실행 과정

세 가지 구성 방법:

  • yml 구성

    구성 개체: org.apache.ibatis.session.Configuration
mybatis:
  configuration:
    #事务注册日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

mybatis-plus:
  mapper-locations: classpath:/mapper/**/**.xml
  configuration:
    #事务注册日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

logging:
  level:
    root: info
    #事务提交回滚日志
    org.springframework.jdbc.datasource.DataSourceTransactionManager: debug    
  • SqlSessionFactory 속성 수정
@Configuration
public class MybatisConfig {
    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;
    @PostConstruct
    public void addSqlInterceptor() {
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
            sqlSessionFactory.getConfiguration().setCallSettersOnNulls(true);//返回查询中的null值
            sqlSessionFactory.getConfiguration().setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);//输出执行SQL
        }
    }
}
  • 사용자 정의 ConfigurationCustomizer 객체
@Configuration
public class MybatisConfig {
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> {
            configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());
            configuration.setCallSettersOnNulls(true);//返回查询中的null值
            configuration.setMapUnderscoreToCamelCase(false);//表字段使用下划线命名
            configuration.setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);//输出执行SQL
        };
    }
}

트랜잭션 지원 활성화 여부

@EnableTransactionManagement 주석은 활성화 트랜잭션을 표시하며 트랜잭션을 추가하지 않으면 작동하지 않으며 세션 객체의 롤백()을 수동으로 호출하는 것도 유효하지 않습니다.

@EnableTransactionManagement
@SpringBootApplication()
public class StartApplication {
	public static void main(String[] args) {
		SpringApplication.run(StartApplication.class, args);
	}
}

플랫폼 관리자가 맞나요?

트랜잭션 관리자 인스턴스를 주입하고 이를 인쇄하여 인스턴스 개체가 현재 ORM과 일치하는지 확인합니다.

@Autowired
PlatformTransactionManager platformTransactionManager;

@Test
public String testTran() {
        System.out.print(">>>>>事务管理器:"+platformTransactionManager);
        ....
}

트랜잭션 실행 프로세스를 보기 위해 실행된 SQL을 콘솔에 인쇄합니다.

打开Mybatis执行SQL日志,方便滚出事务执行情况;
>>>>>事务管理器:org.springframework.jdbc.datasource.DataSourceTransactionManager@2fbbdcaCreating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6]  --事务
JDBC Connection [ProxyConnection[PooledConnection[org.postgresql.jdbc.PgConnection@14fc5be1]]] will be managed by Spring
==>  Executing: select * from mytable; 
<==      Total: 2
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6]  --事务
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] from current transaction  --事务
==>  Preparing: insert into mytable(log_id, create_date) VALUES ('1678872584952_1',now()); 
==> Parameters: 
<==    Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6]  --事务
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] from current transaction  --事务
==>  Preparing: insert into mytable(log_id, create_date) VALUES ('1678872584952_2',now()); 
==> Parameters: 
<==    Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6]  --事务
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] from current transaction  --事务
==>  Preparing: insert into mytable(log_id, create_date) VALUES ('1678872584952_2',now()); 
==> Parameters: 
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6]  --事务
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6]  --事务
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6]  --事务

거래 실패를 일으키는 여러 구덩이

피트 1: Spring 트랜잭션 호스팅이 활성화되지 않았습니다.

@EnableTransactionManagement 주석은 시작 클래스에 표시되어야 합니다. 그렇지 않으면 트랜잭션이 적용되지 않습니다.

Pit 2: ORM 혼합 사용으로 인해 여러 플랫폼 트랜잭션 관리자가 발생함

Mybatis와 JPA 트랜잭션 관리자 간의 통신:

  • 마이바티스 -> org.springframework.jdbc.datasource.DataSourceTransactionManager@50061d56
  • JPA -> org.springframework.orm.jpa.JpaTransactionManager@204d101

구덩이 3: 기본 비즈니스 예외는 롤백되지 않습니다.

@Transactional()을 지정하지 않으면 런타임 예외만 롤백되며,

@Transactional(rollbackFor = {Exception.class})는 이때 모든 예외를 롤백합니다.

Pit 4: 예외를 발생시키지 않고 수동으로 포착하면 트랜잭션이 실패하게 됩니다.

try {
    mapper.insert(sql1);
    mapper.insert(sql2);
}catch (Exception e){
    e.printStackTrace();
    throw e;  //注释掉这行将不回滚
}

피트 5: @Transaction 주석은 public이라는 메서드에만 적용되며 다른 것들은 적용되지 않습니다.

Guess you like

Origin blog.csdn.net/xxj_jing/article/details/129584476