解析xml讲完之后,我们开始讲调用了。因为mybatis实际使用了动态代理(阉割版),不过这里的动态代理没有实现类。有关这块设计模式可以找一下看看。
public class Test {
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取代理
PageArticlesMapper pageArticles = sqlSessionFactory.openSession().getMapper(PageArticlesMapper.class);
//查询
pageArticles.selectBlog(101);
}
}

大家在这里注意到了proxy代理了吧,因为在之前解析xml的时候已经对每个xxMapper接口都包装了一个MapperProxyFactory,当从sqlSessionFactory.openSession().getMapper(PageArticlesMapper.class);获取对应的xxMapper时,会进行代理实例化。来看一下
DefaultSqlSession#getMapper
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
MapperRegistry#getMapper
//在这里获取对应xxxMapper的代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//获取的类包装了一层代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//实例化
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
正好找到了之前对bean封装的MapperProxyFactory类
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
//如果已经存在则报错 interface com.telecom.BlogMapper
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//放入map中,并对类型包装一个代理工厂
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
继续MapperProxyFactory#newInstance
protected T newInstance(MapperProxy<T> mapperProxy) {
//通过代理进行实例化
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//包装代理
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
然后进入 java.lang.reflect.Proxy#newProxyInstance,大家可以滤过下面这个代码
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
继续到Test.java中

MapperProxy实现了InvocationHandler,那当调用这个接口的时候就会触发invoke方法。
MapperProxy#invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
//如果是从Object继承的方法,直接执行
return method.invoke(this, args);
//判断是否是默认方法,默认方法是Java8的新特性,判断逻辑与Java8 Method类的isDefault方法一样
} else if (isDefaultMethod(method)) {
//调用默认方法,这里面用到了Java7的API,有兴趣的读者可以自行了解
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
/*构建并缓存MapperMethod*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
/*执行*/
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
//从缓存中获取信息 public abstract com.telecom.Blog com.telecom.BlogMapper.selectBlog(int)
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//加入缓存
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
MapperMethod#execute
//根据type类型执行
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
/*将参数转换为sql命令的参数*/
Object param = method.convertArgsToSqlCommandParam(args);
/*插入,返回影响行数的结果*/
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
/*将参数转换为sql命令的参数*/
Object param = method.convertArgsToSqlCommandParam(args);
/*更新,返回影响行数的结果*/
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
/*将参数转换为sql命令的参数*/
Object param = method.convertArgsToSqlCommandParam(args);
/*删除,返回影响行数的结果*/
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
//下面几个分支判断的条件,我们在分析构造MapperMethod的过程时看到了这些波尔条件的赋值过程
if (method.returnsVoid() && method.hasResultHandler()) {
/*返回void并且方法包含ResultHandler的查询的执行*/
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
/*多个返回值的查询的执行*/
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
/*@MapKey注解的Map类型的返回值的查询的执行*/
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
/*Cursor游标类型的返回值的查询的执行*/
result = executeForCursor(sqlSession, args);
} else {
/*将参数转换为sql命令的参数*/
Object param = method.convertArgsToSqlCommandParam(args);
/*单个返回值的查询的执行*/
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
//命令执行结果为null,并且方法返回值是基本类型抛出异常
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
到这里,大家看到了增删改查的判断了吧。测试方法走select.
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null; //解析的参数名称为空,返回null
} else if (!hasParamAnnotation && paramCount == 1) {
//没有注解@Param的参数并且解析的参数名称只有一个,根据下标返回参数
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//添加参数命名和参数值的对应关系
param.put(entry.getValue(), args[entry.getKey()]);
//通用的参数名称,param1,param2...
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
if (!names.containsValue(genericParamName)) {
//添加通用参数名称和参数值的对应关系
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
通过selectOne一路追到DefaultSqlSession#selectList类中
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据statementId获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
//对集合类型参数的包装,就是如果参数是集合类型,会根据参数类型的不同为参数添加不同的key
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<Object>();
map.put("array", object);
return map;
}
return object;
}
这里的executor在默认配置的情况下的类型是CachingExecutor:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
/*获取解析过动态标签的sql*/
BoundSql boundSql = ms.getBoundSql(parameterObject);
//创建缓存key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
/*调用另一个重载的查询方法进行查询*/
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public BoundSql getBoundSql(Object parameterObject) {
/*获取解析过动态标签的sql*/
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
//检查参数映射中是否存在嵌套的resultMap并设置boolean标记
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
DynamicSqlSource#getBoundSql
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
//这里会根据动态标签的不同,如<if/>、<foreach/>等,解析这些节点拼接到sql中,会涉及到OGNL表达式解析的内容,有兴趣的读者可以自行了解
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
//将#{}替换成?号占位符,并构建每个占位符对应参数属性的映射
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
//将解析内容封装到BoundSql对象中返回
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}
BaseExecutor#createCacheKey
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//创建缓存
//621272139:1884823062:com.telecom.BlogMapper.selectBlog:0:2147483647:select * from Blog where id = ?
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
//获取属性名称
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
//1512233168:1884823163:com.telecom.BlogMapper.selectBlog:0:2147483647:select * from Blog where id = ?:101
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// 将环境的id更新进去
cacheKey.update(configuration.getEnvironment().getId());
}
//-1230830222:1660009398:com.telecom.BlogMapper.selectBlog:0:2147483647:select * from Blog where id = ?:101:development
return cacheKey;
}
这里的cacheKey会在一级缓存(Map)代码中作为key。查询结果为value。
CachingExecutor#query
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//获取缓存信息,不过第一次调用没有
Cache cache = ms.getCache();
//不为空,判断从缓存获取
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//缓存为空,则从数据库查询
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
BaseExecutor#query,这里的key是从cacheKey创建获得的
public <E> List<E> 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<E> list;
try {
//计数器
queryStack++;
//从缓存拿去
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//从数据库读取
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//设置缓存,占位符。防止其他连接调用击穿db
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//执行 org.apache.ibatis.executor.SimpleExecutor.doQuery
/*子类实现具体的查询逻辑*/
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
SimpleExecutor#doQuery
/*子类实现具体的查询逻辑*/
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//包装StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
/*准备Statement*/
stmt = prepareStatement(handler, ms.getStatementLog());
//查询
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
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) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根据statementType配置选择不同的处理器,在标签解析时我们看到默认为PREPARED
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
SimpleExecutor
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取数据库连接
Connection connection = getConnection(statementLog);
/*准备Statement*/
stmt = handler.prepare(connection, transaction.getTimeout());
/*为Statement设置参数*/
handler.parameterize(stmt);
return stmt;
}
BaseStatementHandler
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
/*实例化Statement*/
statement = instantiateStatement(connection);
/*设置超时时间*/
setStatementTimeout(statement, transactionTimeout);
/*设置FetchSize*/
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);//异常关闭Statement
throw e;
} catch (Exception e) {
closeStatement(statement);//异常关闭Statement
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
instantiateStatement实例化Statement就是根据配置不同调用Connection的不同重载方法来创建PreparedStatement,具体每个重载的方法的作用,请参考Java API。
protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
Integer queryTimeout = null;
//标签上配置的timeout优先级最高
if (mappedStatement.getTimeout() != null) {
queryTimeout = mappedStatement.getTimeout();
} else if (configuration.getDefaultStatementTimeout() != null) {
//标签没有配置则应用全局配置的timeout
queryTimeout = configuration.getDefaultStatementTimeout();
}
if (queryTimeout != null) {
stmt.setQueryTimeout(queryTimeout);
}
/*应用事务超时时间*/
StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
}
public static void applyTransactionTimeout(Statement statement, Integer queryTimeout, Integer transactionTimeout) throws SQLException {
if (transactionTimeout == null){
return;
}
Integer timeToLiveOfQuery = null;
//没有配置查询超时时间则应用事务配置的超时时间
if (queryTimeout == null || queryTimeout == 0) {
timeToLiveOfQuery = transactionTimeout;
} else if (transactionTimeout < queryTimeout) {
//如果事务配置的超时时间小于配置的查询超时时间,则应用事务配置的超时时间
timeToLiveOfQuery = transactionTimeout;
}
if (timeToLiveOfQuery != null) {
statement.setQueryTimeout(timeToLiveOfQuery);
}
}
BaseStatementHandler#setFetchSize
protected void setFetchSize(Statement stmt) throws SQLException {
//标签上配置的优先
Integer fetchSize = mappedStatement.getFetchSize();
if (fetchSize != null) {
stmt.setFetchSize(fetchSize);
return;
}
Integer defaultFetchSize = configuration.getDefaultFetchSize();
if (defaultFetchSize != null) {
//标签上没有配置则使用全局配置
stmt.setFetchSize(defaultFetchSize);
}
}
回到SimpleExecutor#prepareStatement并继续跟踪到PreparedStatementHandler#parameterize,直到DefaultParameterHandler#setParameters
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
//遍历参数绑定映射列表
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
//获取参数绑定的属性名称
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
//根据属性名称从参数对象中反射获取对应的值
value = metaObject.getValue(propertyName);
}
//获取类型处理器
TypeHandler typeHandler = parameterMapping.getTypeHandler();
//获取jdbcType类型
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
/*为PreparedStatement设置值*/
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
public void setParameter(PreparedStatement ps, int i, T 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);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
try {
//子类实现具体的非空值设置
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different configuration property. " +
"Cause: " + e, e);
}
}
}
根据构建ParameterMapping时设置的TypeHandler来为PreparedStatement设置对应类型的值。
RoutingStatementHandler:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//org.apache.ibatis.executor.statement.PreparedStatementHandler.query
return delegate.<E>query(statement, resultHandler);
}
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute(); //执行sql命令 返回com.mysql.jdbc.JDBC42PreparedStatement@1a677343: select * from Blog where id = 101
/*处理结果集*/
return resultSetHandler.<E> handleResultSets(ps);
}
到这里就要进入mysql驱动程序对接数据库了。然后查询结果handleResultSets在这里不讲,有兴趣的可以查看阅读以下,否则整个章节太长,难以消化。
上一篇: Mybatis之Xml解析说明
下一篇: Spring-Mybatis 注解连接器 MapperScannerRegistrar
|