美好的一天,
我需要使用Oracle ojdbc7 12.1.0.2中的功能确保应用程序的连续性。我有成千上万的项目,我使用Spring和Hibernate访问数据库管理事务。 要在我的项目中实现应用程序连续性功能,我必须使用此类 oracle.jdbc.replay.ReplayableConnection 。所以实际上我不知道如何指示Spring(和Hibernate)使用这个类来使用 @Transactional 注释来管理事务。
实际上,我在做的是:
声明数据源(此实现具有ReplayableConnection类)
@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;
}
自动装配我需要的数据源
@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());
}
如果我使用当前配置启动应用程序,当我关闭oracle节点时,它会说:SQL异常17410没有更多数据要从套接字读取。 (实际上我有两个节点,我关闭它连接的节点)
更新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;
}
});
Acually,如果我启动它并关闭它所连接的oracle节点,则说:
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
正如你所看到的那样,“禁止重播”,我无法弄清楚原因。