DefaultSqlSession第一讲query解析

MyBatis执行流程解析
本文详细解析了MyBatis的执行流程,包括SqlSession如何根据执行器类型创建Executor,查询过程中涉及的缓存机制,以及如何通过StatementHandler进行参数设置和结果集处理。
上一篇文章:我们说过DefaultSqlSession,DefaultSqlSessionFactory根据执行器类型,事务级别,是否提交等信息,来构建执行器Executor,然后根据执行器和configuration构建SqlSession,默认为DefaultSqlSession,从DefaultSqlSession的类信息方法来看,DefaultSqlSession的查询则委托给executor的query,插入,更新,删除,则委托给executor的update,提交,回滚,清除缓存,刷新Statement,关闭SqlSession,都是委托给Executor的相应方法。这一节节我们来Executor
//执行器的构建
//根据数据源,事务级别,是否自动提交,创建执行器
org.apache.ibatis.executor.Executor executor = configuration.newExecutor(tx, execType, autoCommit);

//Configuration
public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit)
{
executorType = executorType != null ? executorType : defaultExecutorType;
executorType = executorType != null ? executorType : ExecutorType.SIMPLE;
Executor executor;
if(ExecutorType.BATCH == executorType)
executor = new BatchExecutor(this, transaction);
else
if(ExecutorType.REUSE == executorType)
executor = new ReuseExecutor(this, transaction);
else
executor = new SimpleExecutor(this, transaction);
if(cacheEnabled)
executor = new CachingExecutor(executor, autoCommit);
//将执行器插入到拦截器链中
executor = (Executor)interceptorChain.pluginAll(executor);
return executor;
}

从上面configuration的创建执行器方法,可以看出执行器有BatchExecutor,ReuseExecutor,SimpleExecutor
根据执行器类型,创建执行器,如果缓存启用,则包装executor为CachingExecutor;
//BatchExecutor
public class BatchExecutor extends BaseExecutor
{
public static final int BATCH_UPDATE_RETURN_VALUE = -2147482646;
private final List statementList = new ArrayList();
private final List batchResultList = new ArrayList();
private String currentSql;
private MappedStatement currentStatement;
public BatchExecutor(Configuration configuration, Transaction transaction)
{
super(configuration, transaction);
}
}

//SimpleExecutor
public class SimpleExecutor extends BaseExecutor
{
public SimpleExecutor(Configuration configuration, Transaction transaction)
{
super(configuration, transaction);
}
}


//CachingExecutor
public class CachingExecutor
implements Executor
{
private Executor _flddelegate;
private boolean autoCommit;
private TransactionalCacheManager tcm;
private boolean dirty;
public CachingExecutor(Executor delegate)
{
this(delegate, false);
}
public CachingExecutor(Executor delegate, boolean autoCommit)
{
tcm = new TransactionalCacheManager();
_flddelegate = delegate;
this.autoCommit = autoCommit;
}
public Transaction getTransaction()
{
return _flddelegate.getTransaction();
}
//更新
public int update(MappedStatement ms, Object parameterObject)
throws SQLException
{
//刷新刷新
flushCacheIfRequired(ms);
//调用代理更新
return _flddelegate.update(ms, parameterObject);
}
//查询
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException
{
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//查询
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException
{
Cache cache;
cache = ms.getCache();
if(cache == null)
break MISSING_BLOCK_LABEL_183;
flushCacheIfRequired(ms);
if(!ms.isUseCache() || resultHandler != null)
break MISSING_BLOCK_LABEL_183;
ensureNoOutParams(ms, key, parameterObject, boundSql);
if(dirty)
break MISSING_BLOCK_LABEL_147;
cache.getReadWriteLock().readLock().lock();
List list1;
List cachedList = (List)cache.getObject(key);
if(cachedList == null)
break MISSING_BLOCK_LABEL_105;
list1 = cachedList;
cache.getReadWriteLock().readLock().unlock();
cache.getReadWriteLock().readLock().unlock();
cache.getReadWriteLock().readLock().unlock();
//调用代理查询
List list = _flddelegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list);
return list;
return _flddelegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

public List flushStatements()
throws SQLException
{
return _flddelegate.flushStatements();
}

public void commit(boolean required)
throws SQLException
{
_flddelegate.commit(required);
tcm.commit();
dirty = false;
}

public void rollback(boolean required)
throws SQLException
{
_flddelegate.rollback(required);
dirty = false;
if(required)
tcm.rollback();
break MISSING_BLOCK_LABEL_43;
if(required)
tcm.rollback();
throw exception;
}
public void clearLocalCache()
{
_flddelegate.clearLocalCache();
}
}

从上可以看出CachingExecutor是SimpleExecutor,BatchExecutor等执行器代理执行器;
事务的获取,清除缓存,更新,查询操作实际上,是调用Executor的相应方法。SimpleExecutor,
BatchExecutor为实际执行器,继承BatchExecutor;
下面根据DefaultSqlSession,来看执行器的query,update,提交,回滚,清除缓存,刷新Statement,
关闭SqlSession
public class DefaultSqlSession
implements SqlSession
{
private Configuration configuration;
private Executor executor;
private boolean dirty;
public DefaultSqlSession(Configuration configuration, Executor executor)
{
this.configuration = configuration;
this.executor = executor;
dirty = false;
}
//查询
public Object selectOne(String statement)
{
return selectOne(statement, null);
}
public Object selectOne(String statement, Object parameter)
{
List list = selectList(statement, parameter);
if(list.size() == 1)
return list.get(0);
if(list.size() > 1)
throw new TooManyResultsException((new StringBuilder()).append("Expected one result (or null) to be returned by selectOne(), but found: ").append(list.size()).toString());
else
return null;
}
public List selectList(String statement)
{
return selectList(statement, null);
}

public List selectList(String statement, Object parameter)
{
return selectList(statement, parameter, RowBounds.DEFAULT);
}
public List selectList(String statement, Object parameter, RowBounds rowBounds)
{
List list;
try
{
org.apache.ibatis.mapping.MappedStatement ms = configuration.getMappedStatement(statement);
//调用executor的查询方法
List result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
list = result;
}
}
//插入
public int insert(String statement)
{
return insert(statement, null);
}

public int insert(String statement, Object parameter)
{
//委托给update方法
return update(statement, parameter);
}

public int update(String statement)
{
return update(statement, null);
}

public int update(String statement, Object parameter)
{
int i;
try
{
dirty = true;
org.apache.ibatis.mapping.MappedStatement ms = configuration.getMappedStatement(statement);
//委托给executor的update
i = executor.update(ms, wrapCollection(parameter));
}
}
//删除
public int delete(String statement)
{
//委托给update
return update(statement, null);
}

public int delete(String statement, Object parameter)
{
return update(statement, parameter);
}
//提交
public void commit()
{
commit(false);
}

public void commit(boolean force)
{
try
{
//委托executor的commit
executor.commit(isCommitOrRollbackRequired(force));
dirty = false;
}
}
//回滚
public void rollback()
{
rollback(false);
}

public void rollback(boolean force)
{
try
{
//委托executor的rollback
executor.rollback(isCommitOrRollbackRequired(force));
dirty = false;
}
}
//清除缓存
public void clearCache()
{
////委托executor的clearLocalCache
executor.clearLocalCache();
}
//刷新Statements
public List flushStatements()
{
List list;
try
{
//委托executor的flushStatements
list = executor.flushStatements();
}
}
//关闭SqlSession
public void close()
{
//委托executor的close
executor.close(isCommitOrRollbackRequired(false));
dirty = false;
}
}

先来看SqlSession
public List selectList(String statement, Object parameter, RowBounds rowBounds)
{
List list;
try
{
org.apache.ibatis.mapping.MappedStatement ms = configuration.getMappedStatement(statement);
//调用executor的查询方法
List result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
list = result;
}
}

代理执行器调用实际执行器BatchExecutor,ReuseExecutor,SimpleExecutor的query的方法
//CachingExecutor
//查询
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException
{
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//查询
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException
{
List list = _flddelegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list);
return list;
return _flddelegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

查看BatchExecutor,ReuseExecutor,SimpleExecutor的query的方法,实际上是在BaseExecutor
//BaseExecutor
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException
{
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//委托给query(ms, parameter, rowBounds, resultHandler, key, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException
{
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if(closed)
throw new ExecutorException("Executor was closed.");
if(queryStack == 0 && ms.isFlushCacheRequired())
clearLocalCache();
List list;
queryStack++;
list = resultHandler != null ? null : (List)localCache.getObject(key);
if(list != null)
//从本地获取查询结果
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
else
//从数据库查询结果
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
return list;
}
//从数据库查询结果
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException
{
localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
//委托给doQuery
List list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
localCache.removeObject(key);
break MISSING_BLOCK_LABEL_53;
Exception exception;
exception;
localCache.removeObject(key);
throw exception;
localCache.putObject(key, list);
if(ms.getStatementType() == StatementType.CALLABLE)
localOutputParameterCache.putObject(key, parameter);
return list;
}
//doQuery为抽象方法,待子类扩展
protected abstract List doQuery(MappedStatement mappedstatement, Object obj, RowBounds rowbounds, ResultHandler resulthandler, BoundSql boundsql)
throws SQLException;

这里我们来看一下SimpleExecutor
//SimpleExecutor
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException
{
Statement stmt = null;
List list;
Configuration configuration = ms.getConfiguration();
//构造StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
//获取Statement
stmt = prepareStatement(handler, ms.getStatementLog());
//执行查询
list = handler.query(stmt, resultHandler);
//关闭statement
closeStatement(stmt);
return list;
Exception exception;
exception;
closeStatement(stmt);
throw exception;
}

//构造StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);

//configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
{
//创建RoutingStatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//拦截器处理statementHandler,
statementHandler = (StatementHandler)interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

//RoutingStatementHandler
public class RoutingStatementHandler
implements StatementHandler
{
private final StatementHandler _flddelegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
{
static class _cls1
{

static final int $SwitchMap$org$apache$ibatis$mapping$StatementType[];

static
{
$SwitchMap$org$apache$ibatis$mapping$StatementType = new int[StatementType.values().length];
try
{
$SwitchMap$org$apache$ibatis$mapping$StatementType[StatementType.STATEMENT.ordinal()] = 1;
}
catch(NoSuchFieldError ex) { }
try
{
$SwitchMap$org$apache$ibatis$mapping$StatementType[StatementType.PREPARED.ordinal()] = 2;
}
catch(NoSuchFieldError ex) { }
try
{
$SwitchMap$org$apache$ibatis$mapping$StatementType[StatementType.CALLABLE.ordinal()] = 3;
}
catch(NoSuchFieldError ex) { }
}
}
//根据StatementType,创建对应的StatementHandler
switch(_cls1..SwitchMap.org.apache.ibatis.mapping.StatementType[ms.getStatementType().ordinal()])
{
case 1: // '\001'
_flddelegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;

case 2: // '\002'
_flddelegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;

case 3: // '\003'
_flddelegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;

default:
throw new ExecutorException((new StringBuilder()).append("Unknown statement type: ").append(ms.getStatementType()).toString());
}
}

public Statement prepare(Connection connection)
throws SQLException
{
return _flddelegate.prepare(connection);
}

public void parameterize(Statement statement)
throws SQLException
{
_flddelegate.parameterize(statement);
}

public void batch(Statement statement)
throws SQLException
{
_flddelegate.batch(statement);
}

public int update(Statement statement)
throws SQLException
{
return _flddelegate.update(statement);
}

public List query(Statement statement, ResultHandler resultHandler)
throws SQLException
{
return _flddelegate.query(statement, resultHandler);
}

public BoundSql getBoundSql()
{
return _flddelegate.getBoundSql();
}

public ParameterHandler getParameterHandler()
{
return _flddelegate.getParameterHandler();
}
}

从RoutingStatementHandler可以看出,更新,查询,预编译都是通过代理StatementHandler,具体根据StatementType创建对应的StatementHandler,SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler
//获取Statement
stmt = prepareStatement(handler, ms.getStatementLog());
private Statement prepareStatement(StatementHandler handler, Log statementLog)
throws SQLException
{
//获取连接,这里实际上调用的为BaseExector的getConnection,从执行器的事务管理器获取连接
java.sql.Connection connection = getConnection(statementLog);
//预编译
Statement stmt = handler.prepare(connection);
//参数设置
handler.parameterize(stmt);
return stmt;
}

//预编译
Statement stmt = handler.prepare(connection);

实际为RoutingStatementHandler,通过本地代理,预编译Statement,
public abstract class BaseStatementHandler
implements StatementHandler
{
//获取PrepareStatement
public Statement prepare(Connection connection)
throws SQLException
{
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;
protected BoundSql boundSql;
Statement statement;
ErrorContext.instance().sql(boundSql.getSql());
statement = null;
//实例化Statement
statement = instantiateStatement(connection);
setStatementTimeout(statement);
setFetchSize(statement);
return statement;
}
//带子类扩展
protected abstract Statement instantiateStatement(Connection connection)
throws SQLException;

//设置statement的超时时间
protected void setStatementTimeout(Statement stmt)
throws SQLException
{
Integer timeout = mappedStatement.getTimeout();
Integer defaultTimeout = configuration.getDefaultStatementTimeout();
if(timeout != null)
stmt.setQueryTimeout(timeout.intValue());
else
if(defaultTimeout != null)
stmt.setQueryTimeout(defaultTimeout.intValue());
}
//设置statement的FetchSize
protected void setFetchSize(Statement stmt)
throws SQLException
{
Integer fetchSize = mappedStatement.getFetchSize();
if(fetchSize != null)
stmt.setFetchSize(fetchSize.intValue());
}
}

我们来看statement实例化:
//PreparedStatementHandler,这里我们来看PreparedStatementHandler的实例化
public class PreparedStatementHandler extends BaseStatementHandler
{

public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
{
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
//实例化Statement
protected Statement instantiateStatement(Connection connection)
throws SQLException
{
//获取sql
String sql = boundSql.getSql();
if(mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator)
{
String keyColumnNames[] = mappedStatement.getKeyColumns();
if(keyColumnNames == null)
return connection.prepareStatement(sql, 1);
else
//从connection返回PreparedStatement
return connection.prepareStatement(sql, keyColumnNames);
}
if(mappedStatement.getResultSetType() != null)
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), 1007);
else
return connection.prepareStatement(sql);
}
}

我们再看一下
//CallableStatementHandler
public class CallableStatementHandler extends BaseStatementHandler
{
protected Statement instantiateStatement(Connection connection)
throws SQLException
{
String sql = boundSql.getSql();
if(mappedStatement.getResultSetType() != null)
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), 1007);
else
//从connection返回CallableStatement
return connection.prepareCall(sql);
}
}

从上面可以看出构造StatementHandler,实际上为RoutingStatementHandler,
RoutingStatementHandler更新,查询,预编译都是通过代理StatementHandler,具体根据StatementType创建对应的StatementHandler,SimpleStatementHandler,
PreparedStatementHandler,CallableStatementHandler,
由具体的StatementHandler,创建Statement
//参数设置
handler.parameterize(stmt);

//PreparedStatementHandler
public void parameterize(Statement statement)
throws SQLException
{
parameterHandler.setParameters((PreparedStatement)statement);
}

parameterHandler在BaseStatementHandler中
public abstract class BaseStatementHandler
implements StatementHandler
{
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;
protected BoundSql boundSql;
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
{
configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
typeHandlerRegistry = configuration.getTypeHandlerRegistry();
objectFactory = configuration.getObjectFactory();
if(boundSql == null)
{
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
//configuration创建ParameterHandler
parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
}

来看一下
//configuration创建ParameterHandler
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql)
{
//从mappedStatement获取LanguageDriver,创建ParameterHandler
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
//拦截器链处理ParameterHandler
parameterHandler = (ParameterHandler)interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}

//XMLLanguageDriver
public class XMLLanguageDriver
implements LanguageDriver
{
public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql)
{
return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}

//DefaultParameterHandler
public class DefaultParameterHandler
implements ParameterHandler
{
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private BoundSql boundSql;
private Configuration configuration;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql)
{
this.mappedStatement = mappedStatement;
configuration = mappedStatement.getConfiguration();
typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
//设置PreparedStatement的参数
public void setParameters(PreparedStatement ps)
throws SQLException
{
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从boundSql获取参数映射
List parameterMappings = boundSql.getParameterMappings();
if(parameterMappings != null)
{
MetaObject metaObject = parameterObject != null ? configuration.newMetaObject(parameterObject) : null;
for(int i = 0; i < parameterMappings.size(); i++)
{
//获取parameterMapping
ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
if(parameterMapping.getMode() == ParameterMode.OUT)
continue;
String propertyName = parameterMapping.getProperty();
Object value;
if(boundSql.hasAdditionalParameter(propertyName))
value = boundSql.getAdditionalParameter(propertyName);
else
if(parameterObject == null)
value = null;
else
if(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()))
value = parameterObject;
else
value = metaObject != null ? metaObject.getValue(propertyName) : null;
//获取具体的TypeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if(typeHandler == null)
throw new ExecutorException((new StringBuilder()).append("There was no TypeHandler found for parameter ").append(propertyName).append(" of statement ").append(mappedStatement.getId()).toString());
JdbcType jdbcType = parameterMapping.getJdbcType();
if(value == null && jdbcType == null)
jdbcType = configuration.getJdbcTypeForNull();
//typeHandler设置PreparedStatement参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}

}
}
}

//BaseTypeHandler
public abstract class BaseTypeHandler extends TypeReference
implements TypeHandler
{
public void setParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
throws SQLException
{
if(parameter == null)
{
if(jdbcType == null)
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
try
{
ps.setNull(i, jdbcType.TYPE_CODE);
}
} else
{
//设置参数
setNonNullParameter(ps, i, parameter, jdbcType);
}
}
//待父类扩展
public abstract void setNonNullParameter(PreparedStatement preparedstatement, int i, Object obj, JdbcType jdbctype)
throws SQLException;
}

BaseTypeHandler的子类有Integer,Float,Double,Boolean,Day,TimeStamp,Clob,StringTypeHandler
这里我们来看StringTypeHandler
public class StringTypeHandler extends BaseTypeHandler
{
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException
{
ps.setString(i, parameter);
}
}

从上面可以看出,参数初始化实际上,是通过ParameterHandler来处理,ParameterHandler默认为DefaultParameterHandler,
DefaultParameterHandler初始化参数实际上,
是从boundSql获取statement参数映射parameterMappings,遍历parameterMappings
根据具体的parameterMapping,从parameterMapping获取TypeHandler
(Integer,Float,Double,Boolean,Day,TimeStamp,Clob,StringTypeHandler)
;最后由TypeHandler,去设置statement参数
//执行查询
l
ist = handler.query(stmt, resultHandler);

//PreparedStatementHandler
public List query(Statement statement, ResultHandler resultHandler)
throws SQLException
{
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}

//CallableStatementHandler
public List query(Statement statement, ResultHandler resultHandler)
throws SQLException
{
CallableStatement cs = (CallableStatement)statement;
cs.execute();
List resultList = resultSetHandler.handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}

//关闭statement
closeStatement(stmt);

//BaseStatementHandler
protected void closeStatement(Statement statement)
{
if(statement != null)
try
{
statement.close();
}
catch(SQLException e) { }
}

总结:
[color=blue]Sqlsession查询,通过CachingExecutor代理,CachingExecutor是SimpleExecutor,BatchExecutor等执行器代理执行器;
事务的获取,清除缓存,更新,查询操作实际上,是调用Executor的相应方法。SimpleExecutor,BatchExecutor为实际执行器,继承BatchExecutor。SimpleExecutor查询,如果有缓存,则利用缓存,没有则用从数据库中查询,查询过程,构造StatementHandler,获取Statement,执行查询,关闭statement。[/color]
2025-03-08 22:47:09 [XNIO-1 task-2] DEBUG c.r.f.i.PlusWebInvokeTimeInterceptor - [PLUS]开始请求 => URL[GET /workflow/process/todoList],参数类型[param],参数:[{"pageSize":["10"],"pageNum":["1"]}] 2025-03-08 22:47:09 [XNIO-1 task-2] ERROR o.f.c.e.i.interceptor.CommandContext - Error while closing command context org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: dm.jdbc.driver.DMException: -2007 第 24 行, 第 47 列[LINK]附近出现错误: 语法分析出错 ### The error may exist in org/flowable/task/service/db/mapping/entity/Task.xml ### The error may involve org.flowable.task.service.impl.persistence.entity.TaskEntityImpl.selectTaskCountByQueryCriteria ### The error occurred while executing a query ### SQL: select count(distinct RES.ID_) from ACT_RU_TASK RES WHERE RES.SUSPENSION_STATE_ = 1 and (RES.ASSIGNEE_ = ? or ( RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = RES.ID_ and LINK.TYPE_ = 'candidate' and (LINK.USER_ID_ = ? or ( LINK.GROUP_ID_ IN ( ? , ? ) ) )))) ### Cause: dm.jdbc.driver.DMException: -2007 第 24 行, 第 47 列[LINK]附近出现错误: 语法分析出错 at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:153) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:145) at org.apache.ibatis.session.defaults.DefaultSqlSes
03-10
<think>好的,我现在需要帮助用户解决在MyBatis查询中遇到的DM JDBC驱动错误,具体错误是“dm.jdbc.driver.DMException: -2007 第24行第47列[LINK]附近出现错误: 语法分析出错”。用户提到是在执行TaskEntityImpl.selectTaskCountByQueryCriteria方法时发生的。首先,我需要理解这个错误的原因以及可能的解决方案。 首先,错误代码-2007通常与SQL语法错误有关。用户提到错误发生在第24行第47列的[LINK]附近,这可能意味着在SQL语句中使用了LINK关键字或标识符,但语法不正确。根据用户提供的引用,他们可能在使用达梦数据库(DM)的DBLINK连接到Oracle数据库。引用[3]中提到了创建DBLINK的语法,例如: ```sql create link "LINKORA" connect 'oracle' with "SCOTT" identified by "oracle" using '192.168.15.10:1521/orcl'; ``` 这可能意味着用户在查询中使用了DBLINK,但语法存在问题。达梦数据库的DBLINK语法可能与标准SQL或其他数据库(如Oracle)有所不同,需要确认是否正确使用了达梦的特定语法。 接下来,考虑用户的MyBatis查询。MyBatis中的XML映射文件可能包含动态SQL,例如使用了`<if>`标签或其他动态元素,这可能导致生成的SQL语句在达梦数据库中出现语法问题。例如,动态拼接的表名或字段名可能没有正确转义,或者使用了达梦不支持的语法结构。 另外,达梦数据库对标识符的大小写处理可能与Oracle不同。引用[3]提到创建DBLINK时需要注意用户名密码的大小写,这可能暗示达梦在某些情况下对大小写敏感。检查SQL语句中是否所有标识符(如表名、列名、别名)的大小写与数据库中的定义一致,或者是否正确使用引号。 错误信息中的“LINK”附近出错,可能是指DBLINK的名称或使用方式不正确。例如,在查询中引用DBLINK时可能需要使用特定的语法,如`表名@LINK名称`。例如,正确的引用方式可能是: ```sql SELECT * FROM table@LINKORA; ``` 如果用户在使用DBLINK时遗漏了@符号或链接名不正确,就会导致语法错误。 此外,检查SQL语句是否有其他语法错误,如缺少逗号、括号不匹配、错误的关键字顺序等。特别是在动态SQL中,条件判断可能导致某些部分被错误地拼接,例如在WHERE子句中可能多出或缺少AND/OR。 还有可能是JDBC驱动版本问题,如果使用的达梦JDBC驱动版本过旧,可能不支持某些语法或存在解析bug。建议用户确认驱动版本,并考虑升级到最新版本。 总结可能的原因: 1. DBLINK使用语法错误,例如缺少@符号或链接名不正确。 2. SQL语句中存在达梦不支持的语法或关键字。 3. 动态SQL拼接导致语法结构错误。 4. 标识符大小写问题。 5. JDBC驱动版本问题。 接下来,用户需要检查他们的MyBatis映射文件中的SQL语句,特别是涉及DBLINK的部分,确保符合达梦的语法要求。同时,启用MyBatis的日志功能,查看实际生成的SQL语句,以便更准确地定位错误位置。此外,验证DBLINK的配置是否正确,是否能够正常连接到目标数据库。</think>根据错误信息`dm.jdbc.driver.DMException: -2007`和上下文中的`LINK`关键字,问题可能涉及达梦数据库(DM)的DBLINK配置或跨库查询语法错误。以下是逐步解决方案: --- ### 1. **检查DBLINK语法与配置** 达梦数据库使用DBLINK需遵循特定语法(如引用[3]所示): ```sql create link "LINKORA" connect 'oracle' with "SCOTT" identified by "oracle" using '192.168.15.10:1521/orcl'; ``` - **验证LINK名称大小写**:达梦DBLINK名称区分大小写,需确保SQL中引用的LINK名称与创建时完全一致[^3]。 - **跨库查询语法**:使用DBLINK时需通过`表名@LINK名称`格式,例如: ```sql SELECT * FROM oracle_table@LINKORA WHERE id = 1; ``` --- ### 2. **排查SQL语句语法错误** 错误提示的`第24行第47列[LINK]附近`表明问题出现在SQL语句的LINK相关部分: - **动态SQL拼接问题**:若MyBatis XML中使用了`<if>`动态标签,需检查生成的SQL是否符合达梦语法。例如: ```xml <select id="selectTaskCountByQueryCriteria"> SELECT COUNT(*) FROM task@${linkName} WHERE status = #{status} </select> ``` - **避免直接拼接变量**:`@${linkName}`可能导致语法异常,建议通过参数传递或预编译方式处理。 --- ### 3. **检查JDBC驱动与依赖** - **驱动版本兼容性**:确认`dm.jdbc.driver`版本与达梦数据库版本匹配,旧版本驱动可能无法解析某些语法[^2]。 - **依赖冲突**:检查项目中是否存在多个JDBC驱动冲突(如Oracle与DM驱动并存)。 --- ### 4. **启用MyBatis日志定位具体SQL** 在`application.properties`中开启SQL日志: ```properties logging.level.org.mybatis=DEBUG ``` 通过日志获取实际执行的SQL语句,直接粘贴到达梦客户端验证语法。 --- ### 5. **验证达梦SQL语法特性** 达梦数据库不完全兼容Oracle语法,需注意: - **引号与别名**:表别名需使用双引号而非单引号,例如: ```sql SELECT t.name FROM table@LINKORA t; -- 错误 SELECT t.name FROM table@LINKORA "t"; -- 正确 ``` - **保留字冲突**:避免使用`LINK`作为字段或表名,或使用双引号转义。 --- ### 示例修正 假设原SQL为: ```sql SELECT COUNT(*) FROM task@LINK WHERE project_id = 10; ``` 修正后: ```sql SELECT COUNT(*) FROM "TASK"@LINKORA WHERE "PROJECT_ID" = 10; ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值