package jnpf.dmsBusinessOccupancy;
|
|
import com.baomidou.dynamic.datasource.tx.ConnectionFactory;
|
import com.baomidou.dynamic.datasource.tx.ConnectionProxy;
|
import com.baomidou.dynamic.datasource.tx.TransactionContext;
|
import org.apache.ibatis.session.SqlSession;
|
import org.apache.ibatis.session.ExecutorType;
|
import org.mybatis.spring.SqlSessionTemplate;
|
import org.mybatis.spring.SqlSessionUtils;
|
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
|
import org.springframework.jdbc.datasource.ConnectionHolder;
|
import org.springframework.jdbc.datasource.DataSourceUtils;
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
import javax.sql.DataSource;
|
import java.sql.Connection;
|
import java.sql.SQLException;
|
import java.util.Objects;
|
import java.util.function.Function;
|
|
import static jnpf.dmsBusinessOccupancy.BusinessOccupancyException.Reason.*;
|
|
/** Verifies the SAME session connection that executes the mapper; never starts a transaction. */
|
final class BusinessOccupancyTransactions {
|
private final SqlSessionTemplate template;
|
private final DataSource dataSource;
|
|
BusinessOccupancyTransactions(SqlSessionTemplate template) {
|
this.template = Objects.requireNonNull(template);
|
var environment = template.getSqlSessionFactory().getConfiguration().getEnvironment();
|
if (!(environment.getTransactionFactory() instanceof SpringManagedTransactionFactory)
|
|| template.getExecutorType() == ExecutorType.BATCH)
|
throw new BusinessOccupancyException(UNSUPPORTED_TRANSACTION);
|
this.dataSource = environment.getDataSource();
|
}
|
|
BusinessFileOccupancyMapper reader() { return template.getMapper(BusinessFileOccupancyMapper.class); }
|
|
<T> T execute(Function<BusinessFileOccupancyMapper, T> work) {
|
String xid = TransactionContext.getXID();
|
boolean spring = TransactionSynchronizationManager.isActualTransactionActive();
|
if (xid != null && spring) throw new BusinessOccupancyException(UNSUPPORTED_TRANSACTION);
|
if (xid == null && !spring) throw new BusinessOccupancyException(TRANSACTION_REQUIRED);
|
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly())
|
throw new BusinessOccupancyException(READ_ONLY_TRANSACTION);
|
// Check BEFORE opening a session: DataSourceUtils can otherwise enlist an unrelated
|
// autocommit connection merely for synchronization, without starting a transaction on it.
|
Object bound = TransactionSynchronizationManager.getResource(dataSource);
|
if (spring && !(bound instanceof ConnectionHolder))
|
throw new BusinessOccupancyException(TRANSACTION_CONNECTION_MISMATCH);
|
SqlSession session = SqlSessionUtils.getSqlSession(template.getSqlSessionFactory(),
|
template.getExecutorType(), template.getPersistenceExceptionTranslator());
|
try {
|
Connection connection = session.getConnection();
|
if (spring) {
|
if (!DataSourceUtils.isConnectionTransactional(connection, dataSource))
|
throw new BusinessOccupancyException(TRANSACTION_CONNECTION_MISMATCH);
|
} else {
|
// A bare XID is not enough. The dynamic transaction must own this exact proxy.
|
if (!(connection instanceof ConnectionProxy proxy)
|
|| ConnectionFactory.getConnection(xid, proxy.getDs()) != connection)
|
throw new BusinessOccupancyException(TRANSACTION_CONNECTION_MISMATCH);
|
}
|
if (connection.getAutoCommit()) throw new BusinessOccupancyException(TRANSACTION_CONNECTION_MISMATCH);
|
if (connection.isReadOnly()) throw new BusinessOccupancyException(READ_ONLY_TRANSACTION);
|
if (connection.getTransactionIsolation() != Connection.TRANSACTION_READ_COMMITTED)
|
throw new BusinessOccupancyException(UNSUPPORTED_ISOLATION);
|
return work.apply(session.getMapper(BusinessFileOccupancyMapper.class));
|
} catch (SQLException e) {
|
throw new IllegalStateException("Cannot verify business occupancy transaction connection", e);
|
} finally {
|
SqlSessionUtils.closeSqlSession(session, template.getSqlSessionFactory());
|
}
|
}
|
}
|