How MyBatis Parses and Applies Transaction Managers: A Deep Dive
This article walks through how MyBatis parses the transaction manager factory from XML configuration, builds the Environment, and applies the transaction manager during SqlSession creation, including detailed code examples of XMLConfigBuilder, transaction factories, and the JdbcTransaction implementation.
Parse Transaction Manager Factory
1.1 Entry
// Build SqlSessionFactory from configuration file; entry
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
new SqlSessionFactoryBuilder().build(inputStream);1.2 XML Construction
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
// ...
}
}
}1.3 Parsing
public class XMLConfigBuilder extends BaseBuilder {
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// Here starts building Environments, parsing transaction manager objects in Environments node
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
}1.4 Parsing Transaction Manager
public class XMLConfigBuilder extends BaseBuilder {
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
// parse transactionManager node
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// parse dataSource node
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// Build Environment object with id, transaction manager, data source and store in Configuration
configuration.setEnvironment(environmentBuilder.build());
break;
}
}
}
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
// parse type attribute
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
// instantiate TransactionFactory via reflection
TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
}At this point, parsing and saving of the transaction manager object is complete, and it has been stored in the Configuration object.
Transaction Manager Application
SqlSession session = sqlSessionFactory.openSession();
public class DefaultSqlSessionFactory implements SqlSessionFactory {
public SqlSession openSession() {
// Get SqlSession object, default returns DefaultSqlSession
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// Transaction manager object is bound in Environment
final Environment environment = configuration.getEnvironment();
// Get transaction manager factory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// Start a new transaction, default TransactionFactory returns JdbcTransactionFactory
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
// Return the transaction manager factory obtained in step 1
return environment.getTransactionFactory();
}
}
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
// Create transaction object
return new JdbcTransaction(ds, level, autoCommit);
}
}The transaction management object JdbcTransaction essentially manages the Connection object.
public class JdbcTransaction implements Transaction {
private static final Log log = LogFactory.getLog(JdbcTransaction.class);
protected Connection connection;
protected DataSource dataSource;
protected TransactionIsolationLevel level;
protected boolean autoCommit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}
public JdbcTransaction(Connection connection) {
this.connection = connection;
}
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
@Override
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
connection.commit();
}
}
@Override
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
connection.rollback();
}
}
@Override
public void close() throws SQLException {
if (connection != null) {
resetAutoCommit();
connection.close();
}
}
protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
try {
if (connection.getAutoCommit() != desiredAutoCommit) {
connection.setAutoCommit(desiredAutoCommit);
}
} catch (SQLException e) {
// ...
}
}
protected void resetAutoCommit() {
try {
if (!connection.getAutoCommit()) {
connection.setAutoCommit(true);
}
} catch (SQLException e) {
// ...
}
}
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommit);
}
@Override
public Integer getTimeout() throws SQLException {
return null;
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
