mybatis / spring

Spring integration for MyBatis 3

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SpringManagedTransaction is not able to reconnect when connection is closed

jquesada-CLGX opened this issue · comments

We have a defined mapper calling an oracle stored procedure, the connections are managed by Hikari pool library. In some cases we have network issues making the DB not accessible for some time. After that the network is back but when trying to call the stored procedure the connection is always closed, SpringManagedTransaction is never trying to reconnect, making mandatory to restart the application to get this method available.

We have the following dependencies:

  • com.zaxxer:HikariCP:4.0.3
  • org.mybatis:mybatis:3.5.9
  • org.mybatis:mybatis-spring:2.0.7
  • com.oracle.database.jdbc:ojdbc8:21.5.0.0

Defining the following simplified mapper:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dummy.Mapper">

	<select id="getInfo" parameterType="com.dummy.Params" statementType="CALLABLE" timeout="300" flushCache="true">
		{CALL domain1.package1.getinfo(
				#{referenceId1, mode=IN, jdbcType=VARCHAR})}
	</select>
</mapper>

Datasource and session configuration

aaa:
  datasource:
    name: db
    poolName: db
    url: jdbc:oracle:thin:@//host:1525/DB
    username: xxx
    password: xxx
    keepaliveTime: 180000
    maxLifetime: 185000
    minimumIdle: 1
    maximumPoolSize: 2
    hikari:
      driver-class-name: oracle.jdbc.driver.OracleDriver

    @Bean(name = "dataSource")
    @ConfigurationProperties("aaa.datasource")
    public HikariDataSource dataSource() {
        HikariDataSource ds = avmLogDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
        return ds;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        Resource[] resourceArgs = new Resource[1];
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        resourceArgs[0] = resourceLoader.getResource("classpath:com/dummy/Mapper.xml");
        sqlSessionFactoryFraud.setMapperLocations(resourceArgs);
        sqlSessionFactoryFraud.setTypeHandlers(new TypeHandler[]{new CalendarTypeHandler()});
        sqlSessionFactoryFraud.setDataSource(dataSource);
        return sqlSessionFactory;
    }

When the connection is available in org.mybatis.spring.transaction.SpringManagedTransaction getConnection method we can see that the connection class type is (wrapping a ojdc connection object):
HikariProxyConnection@1231378000 wrapping oracle.jdbc.driver.T4CConnection@1f5b0c71

When the network is lost the DB connection type is:
HikariProxyConnection@1231378000 wrapping com.zaxxer.hikari.pool.ProxyConnection.ClosedConnection

After restablish the network connection the DB connection still the same
HikariProxyConnection@1231378000 wrapping com.zaxxer.hikari.pool.ProxyConnection.ClosedConnection

In this line the connection is never more null but it is closed and the code never tries to reconnect https://github.com/mybatis/spring/blob/master/src/main/java/org/mybatis/spring/transaction/SpringManagedTransaction.java#L66

This could be fixed easily checking if the connection is closed at that point:

  public Connection getConnection() throws SQLException {
    if (this.connection == null || this.connection.isClosed()) {
      openConnection();
    }
    return this.connection;
  }

Or another suggestion to solve this?

Hello @jquesada-CLGX ,

I don't understand the problem, but re-opening connection inside SpringManagedTransaction may not be the right solution.
If it's a new connection, it's not the same transaction anymore, you know.

In general, if a connection is closed unexpectedly, there should be an exception.
You might have to set proper timeout depending on the situation, though (e.g. network timeout).

Close because no reply at long time. Of course you can reopen at any time. And probably, if calling the openConnection again, this behavior cannot be resolved because the real connection managed by Spring Transaction (DataSourceUtils#getConnection method return same connection instance during perform transaction).

Or another suggestion to solve this?

If you need retry, please consider to retry the transactional processing on other transaction. For example, you can use the Spring Retry feature.

SpringManagedTransaction is never trying to reconnect, making mandatory to restart the application to get this method available.

Really?? A SpringManagedTransaction instance create at beginning new transaction. Therefore, I cannot believe this behavior. If you reopen this issue, please provide a repro project. Thanks.