上一章节中关于池化数据源的调用、执行和结果封装,都是在 DefaultSqlSession 中进行发起。这样写死不利于扩展,也不利于 SqlSession 中每一个新增定义的方法对池化数据源的调用。怎么利于扩展呢?
DefaultSqlSession#selectOne
方法中关于对数据源的调用、执行和结果封装,提供新的功能模块替代这部分编码的逻辑处理。解耦:数据源的操作硬捆绑到 DefaultSqlSession 的执行方法上。
mybatis-step-06
|-src
|-main
| |-java
| |-com.lino.mybatis
| |-binding
| | |-MapperMethod.java
| | |-MapperProxy.java
| | |-MapperProxyFactory.java
| | |-MapperRegistry.java
| |-builder
| | |-xml
| | | |-XMLConfigBuilder.java
| | |-BaseBuilder.java
| |-datasource
| | |-druid
| | | |-DruidDataSourceFacroty.java
| | |-pooled
| | | |-PooledConnection.java
| | | |-PooledDataSource.java
| | | |-PooledDataSourceFacroty.java
| | | |-PoolState.java
| | |-unpooled
| | | |-UnpooledDataSource.java
| | | |-UnpooledDataSourceFacroty.java
| | |-DataSourceFactory.java
| |-executor
| | |-resultset
| | | |-DefaultResultSetHandler.java
| | | |-ResultSetHandler.java
| | |-statement
| | | |-BaseStatementHandler.java
| | | |-PreparedStatementHandler.java
| | | |-SimpleStatementHandler.java
| | | |-StatementHandler.java
| | |-BaseExecutor.java
| | |-Executor.java
| | |-SimpleExecutor.java
| |-io
| | |-Resources.java
| |-mapping
| | |-BoundSql.java
| | |-Environment.java
| | |-MappedStatement.java
| | |-ParameterMapping.java
| | |-SqlCommandType.java
| |-session
| | |-defaults
| | | |-DefaultSqlSession.java
| | | |-DefaultSqlSessionFactory.java
| | |-Configuration.java
| | |-ResultHandler.java
| | |-SqlSession.java
| | |-SqlSessionFactory.java
| | |-SqlSessionFactoryBuilder.java
| | |-TransactionIsolationLevel.java
| |-transaction
| | |-jdbc
| | | |-JdbcTransaction.java
| | | |-JdbcTransactionFactory.java
| | |-Transaction.java
| | |-TransactionFactory.java
| |-type
| | |-JdbcType.java
| | |-TypeAliasRegistry.java
|-test
|-java
| |-com.lino.mybatis.test
| |-dao
| | |-IUserDao.java
| |-po
| | |-User.java
| |-ApiTest.java
|-resources
|-mapper
| |-User_Mapper.xml
|-mybatis-config-datasource.xml
Executor
接口定义为执行器入口,确定出事务和操作和 SQL 执行的统一标准接口。
BaseExecutor
,也就是用抽象类处理统一共用的事务和执行 SQL 的标准流程。SimpleExecutor
实现。doQuery
方法的具体操作过程。
DefaultSqlSessionFactory
开启 openSession
的时候随着构造函数参数传递给 DefaultSqlSession
中,这样在执行 DefaultSqlSession#selectOne
的时候就可以调用执行器进行处理。这也就完成 解耦 操作。Executor.java
package com.lino.mybatis.executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import java.sql.SQLException;
import java.util.List;
/**
* @description: 执行器
*/
public interface Executor {
/**
* 结果处理器
*/
ResultHandler NO_RESULT_HANDLER = null;
/**
* 查询
*
* @param ms 映射器语句
* @param parameter 参数
* @param resultHandler 结果处理器
* @param boundSql SQL对象
* @param 返回的类型
* @return List
*/
<E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);
/**
* 获取事务
*
* @return 事务对象
*/
Transaction getTransaction();
/**
* 提交
*
* @param required 是否请求执行
* @throws SQLException SQL异常
*/
void commit(boolean required) throws SQLException;
/**
* 回滚
*
* @param required 是否请求执行
* @throws SQLException SQL异常
*/
void rollback(boolean required) throws SQLException;
/**
* 关闭
*
* @param forceRollback 是否强制回滚
*/
void close(boolean forceRollback);
}
BaseExecutor.java
package com.lino.mybatis.executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.List;
/**
* @description: 执行器抽象基类
*/
public abstract class BaseExecutor implements Executor {
private org.slf4j.Logger logger = LoggerFactory.getLogger(BaseExecutor.class);
protected Configuration configuration;
protected Transaction transaction;
protected Executor wrapper;
private boolean closed;
public BaseExecutor(Configuration configuration, Transaction transaction) {
this.configuration = configuration;
this.transaction = transaction;
this.wrapper = this;
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
if (closed) {
throw new RuntimeException("Executor was closed.");
}
return doQuery(ms, parameter, resultHandler, boundSql);
}
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);
@Override
public Transaction getTransaction() {
if (closed) {
throw new RuntimeException("Executor was closed.");
}
return transaction;
}
@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new RuntimeException("Cannot commit, transaction is already closed.");
}
if (required) {
transaction.commit();
}
}
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
if (required) {
transaction.rollback();
}
}
}
@Override
public void close(boolean forceRollback) {
try {
try {
rollback(forceRollback);
} finally {
transaction.close();
}
} catch (SQLException e) {
logger.warn("Unexpected exception on closing transaction. Cause: " + e);
} finally {
transaction = null;
closed = true;
}
}
}
SimpleExecutor.java
package com.lino.mybatis.executor;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 简单执行器
*/
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
@Override
protected <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, resultHandler, boundSql);
Connection connection = transaction.getConnection();
Statement stmt = handler.prepare(connection);
handler.parameterize(stmt);
return handler.query(stmt, resultHandler);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
doQuery
。
StatementHandler.java
package com.lino.mybatis.executor.statement;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 语句处理器
*/
public interface StatementHandler {
/**
* 准备语句
*
* @param connection 链接
* @return Statement语句
*/
Statement prepare(Connection connection);
/**
* 参数化
*
* @param statement 语句
*/
void parameterize(Statement statement) throws SQLException;
/**
* 执行查询
*
* @param statement 语句
* @param resultHandler 结果处理器
* @param 泛型类型
* @return 泛型集合
*/
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
}
BaseStatementHandler.java
package com.lino.mybatis.executor.statement;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 语句处理器抽象基类
*/
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final Object parameterObject;
protected final ResultSetHandler resultSetHandler;
protected BoundSql boundSql;
public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.parameterObject = parameterObject;
this.boundSql = boundSql;
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, boundSql);
}
@Override
public Statement prepare(Connection connection) {
Statement statement = null;
try {
// 实例化 Statement
statement = instantiateStatement(connection);
// 参数设置,可以被抽取,提供配置
statement.setQueryTimeout(350);
statement.setFetchSize(10000);
return statement;
} catch (Exception e) {
throw new RuntimeException("Error prepare statement. Cause: " + e, e);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}
BaseStatementHandler#prepare
方法的处理,包括定义实例化抽象方法,这个方法交由各个具体的子类进行处理。
SimpleStatementHandler.java
package com.lino.mybatis.executor.statement;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 简单语句处理器(STATEMENT)
*/
public class SimpleStatementHandler extends BaseStatementHandler {
public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameterObject, resultHandler, boundSql);
}
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
return connection.createStatement();
}
@Override
public void parameterize(Statement statement) {
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
}
PreparedStatementHandler.java
package com.lino.mybatis.executor.statement;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 预处理语句处理器(PREPARED)
* @author: lingjian
* @createDate: 2022/11/8 14:13
*/
public class PreparedStatementHandler extends BaseStatementHandler {
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultSetHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameterObject, resultSetHandler, boundSql);
}
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
return connection.prepareStatement(sql);
}
@Override
public void parameterize(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.setLong(1, Long.parseLong(((Object[]) parameterObject)[0].toString()));
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
}
instantiateStatement
预处理 SQL、parameterize
设置参数,以及 query
查询的执行操作。parameterize
暂时是写死的处理,后续再完善。query
是执行查询和对结果的封装,结果的封装,后续再完善。定义结果处理器接口,结果集处理器接口,默认Map结果集处理器实现类
ResultHandler.java
package com.lino.mybatis.session;
/**
* @description: 结果处理器
*/
public interface ResultHandler {
/**
* 处理结果
*/
void handleResult();
}
ResultSetHandler.java
package com.lino.mybatis.executor.resultset;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
/**
* @description: 结果集处理器
*/
public interface ResultSetHandler {
/**
* 处理结果集
*
* @param stmt 语句
* @param 泛型
* @return 泛型集合
* @throws SQLException SQL异常
*/
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
}
DefaultResultSetHandler.java
package com.lino.mybatis.executor.resultset;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 默认Map结果处理器
*/
public class DefaultResultSetHandler implements ResultSetHandler {
private final BoundSql boundSql;
public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {
this.boundSql = boundSql;
}
@Override
public <E> List<E> handleResultSets(Statement stmt) throws SQLException {
ResultSet resultSet = stmt.getResultSet();
try {
return resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
List<T> list = new ArrayList<>();
try {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// 每次遍历值
while (resultSet.next()) {
T obj = (T) clazz.newInstance();
for (int i = 1; i <= columnCount; i++) {
Object value = resultSet.getObject(i);
String columnName = metaData.getColumnName(i);
String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
Method method;
if (value instanceof Timestamp) {
method = clazz.getMethod(setMethod, LocalDateTime.class);
} else {
method = clazz.getMethod(setMethod, value.getClass());
}
method.invoke(obj, value);
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
Configuration.java
package com.lino.mybatis.session;
import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import java.util.HashMap;
import java.util.Map;
/**
* @description: 配置项
* @author: lingjian
* @createDate: 2022/11/7 21:32
*/
public class Configuration {
//省略...
/**
* 生产执行器
*
* @param transaction 事务
* @return 执行器
*/
public Executor newExecutor(Transaction transaction) {
return new SimpleExecutor(this, transaction);
}
/**
* 创建语句处理器
*
* @param executor 执行器
* @param mappedStatement 映射器语句类
* @param parameter 参数
* @param resultHandler 结果处理器
* @param boundSql SQL语句
* @return StatementHandler 语句处理器
*/
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
return new PreparedStatementHandler(executor, mappedStatement, parameter, resultHandler, boundSql);
}
/**
* 创建结果集处理器
*
* @param executor 执行器
* @param mappedStatement 映射器语句类
* @param boundSql SQL语句
* @return ResultSetHandler 结果集处理器
*/
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {
return new DefaultResultSetHandler(executor, mappedStatement, boundSql);
}
}
DefaultSqlSessionFactory#openSession
的处理。DefaultSqlSession.java
package com.lino.mybatis.session.defaults;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.TransactionFactory;
import java.sql.SQLException;
/**
* @description: 默认的SqlSessionFactory实现类
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
TransactionFactory transactionFactory = environment.getTransactionFactory();
tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
// 创建执行器
final Executor executor = configuration.newExecutor(tx);
// 创建 DefaultSqlSession
return new DefaultSqlSession(configuration, executor);
} catch (Exception e) {
try {
assert tx != null;
tx.close();
} catch (SQLException ignore) {
}
throw new RuntimeException("Error opening session. Cause: " + e);
}
}
}
openSession
中开启事务传递给执行器的创建。并在执行器创建完毕后,作为参数传递给 DefaultSqlSession。DefaultSqlSession.java
package com.lino.mybatis.session.defaults;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 默认sqlSession实现类
*/
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
private Executor executor;
public DefaultSqlSession(Configuration configuration, Executor executor) {
this.configuration = configuration;
this.executor = executor;
}
@Override
public <T> T selectOne(String statement) {
return (T) ("你被代理了!" + statement);
}
@Override
public <T> T selectOne(String statement, Object parameter) {
MappedStatement ms = configuration.getMappedStatement(statement);
List<T> list = executor.query(ms, parameter, Executor.NO_RESULT_HANDLER, ms.getBoundSql());
return list.get(0);
}
@Override
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
}
DefaultSqlSession#selectOne
中获取 MappedStatement
映射语句后,则传递给执行器进行处理。ApiTest.java
@Test
public void test_SqlSessionFactoryExecutor() throws IOException {
// 1.从SqlSessionFactory中获取SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
// 2.获取映射器对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 3.测试验证
User user = userDao.queryUserInfoById(1L);
logger.info("测试结果:{}", JSON.toJSONString(user));
}
测试结果
16:40:58.484 [main] INFO c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
16:40:59.195 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 1436664465.
16:40:59.262 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
DefaultSqlSession#selectOne
中的调用,换成执行器完成整个过程的处理,解耦了部分的逻辑操作,方便后续的扩展。DefaultSqlSession#selectOne
对数据源的处理解耦到执行器中进行操作。而执行器中又包括了对 JDBC 处理的拆解,链接、准备语句、封装参数、处理结果,所有的这些过程经过解耦后的类和方法,在后续方便进行扩展。