executor包主要功能
- 前文说了DefaultSqlSession中有个成员变量Executor,Executor属于executor包用来执行数据库操作。
- 更准确的说,Executor也并没有直接连接JDBC而是委托给了StatementHandler,根据不同类型的Statement去完成JDBC操作。
- SimpleExecutor为默认的执行器(如下代码),每执行一次update或select,就开启一个Statement对象,用完立刻关闭,ReuseExecutor执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用Statement对象,BatchExecutor可以批量执行update,将所有sql都添加到批处理中,等待统一executeBatch()(doFlushStatements方法就是先完成executeBatch()而后statementList.clear()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,逐一执行executeBatch(),由于JDBC批处理不支持select(int[] executeBatch(),批处理返回的是int数组,而select需要返回数据库字段),所以批处理只针对更新数据。
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
- StatementHandler,PreparedStatementHandler,CallableStatementHandler,分别对应JDBC中的三种Statement。
executor包主要结构
![executor包](https://i-blog.csdnimg.cn/blog_migrate/7d73aac457e2bc4dfb74eb318e1e3af3.png)
executor包设计模式
- CachingExcutor采用了装饰设计模式,每次执行sql的时候一定是首先过CachingExcutor的手,之后再委托给其它Executor执行,如果配置文件没有配置一级缓存为STATEMENT,那么就不清理一级缓存,一级缓存默认是SESSION级别,如下面代码块:
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
- RoutingStatementHandler的设计也是用到了装饰设计模式,JDBC中有Statement,PreparedStatement,CallableStatement,RoutingStatementHandler的作用就是根据不同的Statment路由到这三种类型去调用JDBC,若在Mapper.xml内没有配置statementType属性,默认就是PreparedStatement,前文在builder包中分析过,XMLStatementBuilder类中:
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
- BaseExecutor、BaseStatementHandler都是抽象类,都用了模版方法设计模式,不同的实现类,分别实现自己的抽象方法完成不同的功能,附上源码做参考:
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
......
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
- 附上源码包结构做参考:
![executor包](https://i-blog.csdnimg.cn/blog_migrate/ccb57ba9fae97fb4f3a9c58dfe5dca90.png)
优雅的异常打印
public class ErrorContext {
private static final String LINE_SEPARATOR = System.getProperty("line.separator","\n");
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>();
private ErrorContext stored;
private String resource;
private String activity;
private String object;
private String message;
private String sql;
private Throwable cause;
private ErrorContext() {
}
public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
public ErrorContext store() {
ErrorContext newContext = new ErrorContext();
newContext.stored = this;
LOCAL.set(newContext);
return LOCAL.get();
}
public ErrorContext recall() {
if (stored != null) {
LOCAL.set(stored);
stored = null;
}
return LOCAL.get();
}
public ErrorContext resource(String resource) {
this.resource = resource;
return this;
}
public ErrorContext activity(String activity) {
this.activity = activity;
return this;
}
public ErrorContext object(String object) {
this.object = object;
return this;
}
public ErrorContext message(String message) {
this.message = message;
return this;
}
public ErrorContext sql(String sql) {
this.sql = sql;
return this;
}
public ErrorContext cause(Throwable cause) {
this.cause = cause;
return this;
}
public ErrorContext reset() {
resource = null;
activity = null;
object = null;
message = null;
sql = null;
cause = null;
LOCAL.remove();
return this;
}
@Override
public String toString() {
StringBuilder description = new StringBuilder();
if (this.message != null) {
description.append(LINE_SEPARATOR);
description.append("### ");
description.append(this.message);
}
if (resource != null) {
description.append(LINE_SEPARATOR);
description.append("### The error may exist in ");
description.append(resource);
}
if (object != null) {
description.append(LINE_SEPARATOR);
description.append("### The error may involve ");
description.append(object);
}
if (activity != null) {
description.append(LINE_SEPARATOR);
description.append("### The error occurred while ");
description.append(activity);
}
if (sql != null) {
description.append(LINE_SEPARATOR);
description.append("### SQL: ");
description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());
}
if (cause != null) {
description.append(LINE_SEPARATOR);
description.append("### Cause: ");
description.append(cause.toString());
}
return description.toString();
}