Application Continuity with Spring and Hibernation

Good afternoon,

I need to ensure application continuity using ojdbc7 12.1.0.2 Oracle function. I have thousands of projects where I use Spring and Hibernate to access db for transaction management. To implement the application continuity function in my projects, I have to use this class oracle.jdbc.replay.ReplayableConnection . Therefore, I don't know how to instruct Spring (and Hibernate) to use this class to manage transactions using the @Transactional annotation .

Actually, I am doing this:

  • Data source declaration (this implementation has a ReplayableConnection class)

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource oracleDataSource() throws SQLException {
    
        OracleDataSourceImpl oracleDataSource = new OracleDataSourceImpl();
    
        oracleDataSource.setUser(datasourceUsername);
        oracleDataSource.setPassword(datasourcePassword);
        oracleDataSource.setURL(datasourceUrl);
        oracleDataSource.setDataSourceName("OracleDataSourceImpl");
    
        return oracleDataSource;
    }
    
  • Release the data source where I need it

    @Resource(name = "oracleDataSource")
    private DataSource dataSource;
    
    @Autowired
    private BdtContRepository bdtContRepository;
    
    @Override
    public void updateRow() {
        logger.info("updateRow -> finding all rows...");
        Iterable<BdtCont> bdtConts = bdtContRepository.findAll();
    
        try {
            if (this.dataSource.getConnection() instanceof oracle.jdbc.replay.ReplayableConnection) {
                ((oracle.jdbc.replay.ReplayableConnection)this.dataSource.getConnection()).beginRequest();
    
                logger.info("updateRow -> updating all rows...");
    
                StreamSupport.stream(bdtConts.spliterator(), false)
                        ...
                        });
    
                logger.info("updateRow -> done");
    
              ((oracle.jdbc.replay.ReplayableConnection)this.dataSource.getConnection()).endRequest();
    
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    
    }
    

    }

, BeginRequest EndRequest. ; .

Spring ?

30/01/2018 Spring:

@Configuration
public class DatabaseConfig {

@Value("${spring.datasource.url}")
private String datasourceUrl;

@Value("${spring.datasource.username}")
private String datasourceUsername;

@Value("${spring.datasource.password}")
private String datasourcePassword;

@Value("${spring.datasource.driver-class-name}")
private String driverClassName;

@Value("${spring.jpa.database-platform}")
private String databasePlatform;

@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource oracleDataSource() throws SQLException {

    OracleDataSourceImpl oracleDataSource = new OracleDataSourceImpl();

    oracleDataSource.setUser(datasourceUsername);
    oracleDataSource.setPassword(datasourcePassword);
    oracleDataSource.setURL(datasourceUrl);
    oracleDataSource.setDataSourceName("OracleDataSourceImpl");

    return oracleDataSource;
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
    LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    localContainerEntityManagerFactoryBean.setDataSource(oracleDataSource());
    localContainerEntityManagerFactoryBean.setPackagesToScan("com.example.demotx.domain");
    localContainerEntityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
    return localContainerEntityManagerFactoryBean;
}

@Bean
public JpaVendorAdapter jpaVendorAdapter() {
    HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
    jpaVendorAdapter.setDatabasePlatform(databasePlatform);
    jpaVendorAdapter.setGenerateDdl(true);
    jpaVendorAdapter.setShowSql(true);
    return jpaVendorAdapter;
}

@Bean
public PlatformTransactionManager jpaTransactionManager() throws SQLException {
    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
    jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
    jpaTransactionManager.setDataSource(oracleDataSource());
    return jpaTransactionManager;
}

@Bean
public OracleTransactionCoordinator transactionCoordinator() throws SQLException {
    return new OracleTransactionCoordinator(oracleDataSource(),jpaTransactionManager());
}

, node, : SQL 17410 . ( , node, )

31/01/2018 , , . , .

TransactionCoordinator,

@Component
public class OracleTransactionCoordinator {

private final static Logger LOG = LoggerFactory.getLogger(OracleTransactionCoordinator.class);

private final OracleDataSource oracleDataSource;

private final PlatformTransactionManager jpaTransactionManager;

@Autowired
public OracleTransactionCoordinator(OracleDataSource oracleDataSource, PlatformTransactionManager jpaTransactionManager) {
    this.oracleDataSource = oracleDataSource;
    this.jpaTransactionManager = jpaTransactionManager;
}

/**
 * Execute a portion of code in transaction, with transaction behaviour {@link org.springframework.transaction.TransactionDefinition} PROPAGATION_REQUIRED.
 *
 * @param context a callback support instance.
 * @param <R>     type of result.
 * @return typed result of callback.
 */
public final <R> R doInTransaction(final ExecutionContext<R> context) throws SQLException {
    return doInTransaction(context, true);
}

/**
 * Execute a portion of code in transaction.
 *
 * @param context   a callback support instance.
 * @param propagate true to use PROPAGATION_REQUIRED, false to use PROPAGATION_REQUIRES_NEW.
 * @param <R>       type of result.
 * @return typed result of callback.
 */
public final <R> R doInTransaction(final ExecutionContext<R> context, final boolean propagate) throws SQLException {
    TransactionTemplate tx = new TransactionTemplate(jpaTransactionManager);
    Connection connection = oracleDataSource.getConnection();
    try {
        ((OracleConnection) connection).beginRequest();
        if (propagate) {
            tx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        } else {
            tx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        }
        R result = tx.execute(new TransactionCallback<R>() {

            @Override
            public R doInTransaction(TransactionStatus status) {
                context.setTransactionStatus(status);
                R result = context.doInTransaction();
                context.setRollback(status.isRollbackOnly());
                return result;
            }

        });
        context.close(null);
        return result;
    } catch (RuntimeException ex) {
        LOG.error(ex.getMessage(), ex);
        context.setRollback(true);
        context.close(ex);
        return null;
    } catch (Exception ex) {
        LOG.error(ex.getMessage(), ex);
        context.setRollback(true);
        context.close(new RuntimeException("Wrapper pool exception", ex));
        return null;

    } finally {
        try {
            if (tx != null) {
                ((OracleConnection) connection).endRequest();
            }
        } catch (Exception e) {
            // ignored
        }
    }
}

/**
 * Transactional callback support class.
 *
 * @param <R> result type.
 */
public abstract static class ExecutionContext<R> {

    private final Queue<Runnable> afterCommitCommands = new LinkedList<Runnable>();
    private final Queue<Runnable> afterRollbackCommands = new LinkedList<Runnable>();
    private TransactionStatus transactionStatus = null;
    private boolean rollback = false;
    private boolean rethrow = true;

    public void setTransactionStatus(TransactionStatus transactionStatus) {
        this.transactionStatus = transactionStatus;
    }

    public void setRollback(boolean rollback) {
        this.rollback = rollback;
    }

    public void setRethrow(boolean rethrow) {
        this.rethrow = rethrow;
    }

    public void close(RuntimeException error) {
        if (!rollback) {
            for (Runnable command : afterCommitCommands) {
                try {
                    command.run();
                } catch (RuntimeException e) {
                    LOG.error("Attention an after commit command has gone in error!!! Please check it!", e);
                }
            }
            afterCommitCommands.clear();
        } else {
            for (Runnable command : afterRollbackCommands) {
                try {
                    command.run();
                } catch (RuntimeException e) {
                    LOG.error("Attention an after rollback command has gone in error!!! Please check it!", e);
                }
            }
            afterRollbackCommands.clear();
        }
        if (error != null && rethrow) {
            throw error;
        }
    }

    protected abstract R doInTransaction();

    protected void doAfterCommit(Runnable command) {
        afterCommitCommands.offer(command);
    }

    protected void doAfterRollback(Runnable command) {
        afterRollbackCommands.offer(command);
    }

    protected void rollbackTransaction() {
        transactionStatus.setRollbackOnly();
    }

    protected void flush() {
        transactionStatus.flush();
    }

}

private final static class TransactionTemplateFactory extends BasePoolableObjectFactory<TransactionTemplate> {
    private final PlatformTransactionManager transactionManager;

    public TransactionTemplateFactory(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public TransactionTemplate makeObject() {
        return new TransactionTemplate(transactionManager);
    }

}

OracleTransactionCoordinator :   :

transactionCoordinator.doInTransaction(new OracleTransactionCoordinator.ExecutionContext<Object>() {
            @Override
            protected Object doInTransaction() {
                instanceRepository.findAll().forEach(ins -> logger.info("instance name: "+ ins.toString()));
                logger.info("do In Transaction... updating all");
                bdtContRepository.updateAll();
                instanceRepository.findAll().forEach(ins -> logger.info("instance name: "+ ins.toString()));
                logger.info("FINISHED");
                return null;
            }
        });

, , node, , :

2018-01-31 13:55:54.077  INFO 8114 --- [ost-startStop-1] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
Hibernate: select instance0_.INSTANCE_NAME as INSTANCE_NAME1_1_ from CURR_INST instance0_
2018-01-31 13:55:54.422  INFO 8114 --- [ost-startStop-1] com.example.demotx.DemoTxApplication     : instance name: Instance{instanceName='LABLAR2'}
2018-01-31 13:55:54.422  INFO 8114 --- [ost-startStop-1] com.example.demotx.DemoTxApplication     : do In Transaction... updating all
Hibernate: update BDT_CONT set VAL=?
2018-01-31 13:55:55.602  WARN 8114 --- [ost-startStop-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 17410, SQLState: 08000
2018-01-31 13:55:55.602 ERROR 8114 --- [ost-startStop-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : No more data to read from socket
2018-01-31 13:55:55.602  WARN 8114 --- [ost-startStop-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 17370, SQLState: 99999
2018-01-31 13:55:55.603 ERROR 8114 --- [ost-startStop-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : Replay disabled:
2018-01-31 13:55:55.626 ERROR 8114 --- [ost-startStop-1] o.s.t.support.TransactionTemplate        : Application exception overridden by rollback exception

org.springframework.dao.DataAccessResourceFailureException: could not execute statement; nested exception is org.hibernate.exception.JDBCConnectionException: c
ould not execute statement

, , " ", , .

+4

Source: https://habr.com/ru/post/1692862/


All Articles