设为首页 收藏本站
查看: 1111|回复: 0

[经验分享] mybatis 源码分析之 Mapper接口

[复制链接]

尚未签到

发表于 2016-11-27 10:19:22 | 显示全部楼层 |阅读模式
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//从mapperRegister找,在解析Mapper配置文件时,根据nameSpace 注册mapper接口道mapperRegister
return mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
if (!knownMappers.contains(type))
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
//返回mapper接口类代理
return MapperProxy.newMapperProxy(type, sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
  所有调接口的方法都会被代理类拦截处理我们看具体实现

@SuppressWarnings("unchecked")
public static <T> T newMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class<?>[] interfaces = new Class[]{mapperInterface};
//这个是处理的handle
MapperProxy proxy = new MapperProxy(sqlSession);
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
}
//MapperProxy 回调的入口
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!OBJECT_METHODS.contains(method.getName())) {
//找到方法所属的接口
final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
return null;
}
public Object execute(Object[] args) {
Object result = null;
if (SqlCommandType.INSERT == type) {
Object param = getParam(args);
result = sqlSession.insert(commandName, param);
} else if (SqlCommandType.UPDATE == type) {
Object param = getParam(args);
result = sqlSession.update(commandName, param);
} else if (SqlCommandType.DELETE == type) {
Object param = getParam(args);
result = sqlSession.delete(commandName, param);
} else if (SqlCommandType.SELECT == type) {
if (returnsVoid && resultHandlerIndex != null) {
executeWithResultHandler(args);
} else if (returnsList) {
result = executeForList(args);
} else if (returnsMap) {
result = executeForMap(args);
} else {
Object param = getParam(args);
result = sqlSession.selectOne(commandName, param);
}
} else {
throw new BindingException("Unknown execution method for: " + commandName);
}
return result;
}
  上面这些方法最终都会由sqlSession调用Executer来执行
  来看个查询返回List例子

private List executeForList(Object[] args) {
List result;
Object param = getParam(args);
if (rowBoundsIndex != null) {
RowBounds rowBounds = (RowBounds) args[rowBoundsIndex];
result = sqlSession.selectList(commandName, param, rowBounds);
} else {
result = sqlSession.selectList(commandName, param);
}
return result;
}
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
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();
}
}
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
if (ms != null) {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
cache.getReadWriteLock().readLock().lock();
try {
if (ms.isUseCache() && resultHandler == null) {
CacheKey key = createCacheKey(ms, parameterObject, rowBounds);
final List cachedList = (List) cache.getObject(key);
if (cachedList != null) {
return cachedList;
} else {
List list = delegate.query(ms, parameterObject, rowBounds, resultHandler);
tcm.putObject(cache, key, list);
return list;
}
} else {
return delegate.query(ms, parameterObject, rowBounds, resultHandler);
}
} finally {
cache.getReadWriteLock().readLock().unlock();
}
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler);
}

  从上面我们可以看到先回从缓存找找不到在从delegate类找,delete就是普通的Executor,
  普通Executor3类,他们都继承于BaseExecutorBatchExecutor专门用于执行批量sql操作,ReuseExecutor会重用statement执行sql操作,SimpleExecutor只是简单执行sql没有什么特别的。下面以SimpleExecutor为例:

public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler);
stmt = prepareStatement(handler);
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}

StatementHandler
  当Executor将指挥棒交给StatementHandler后,接下来的工作就是StatementHandler的事了。我们先看看StatementHandler是如何创建的。


创建


    publicStatementHandler newStatementHandler(Executor executor, MappedStatementmappedStatement,  
ObjectparameterObject, RowBounds rowBounds, ResultHandler resultHandler) {  
StatementHandler statementHandler = newRoutingStatementHandler(executor, mappedStatement,parameterObject,rowBounds, resultHandler);  
statementHandler= (StatementHandler) interceptorChain.pluginAll(statementHandler);  
returnstatementHandler;  
}  
 
  可以看到每次创建的StatementHandler都是RoutingStatementHandler,它只是一个分发者,他一个属性delegate用于指定用哪种具体的StatementHandler。可选的StatementHandlerSimpleStatementHandlerPreparedStatementHandlerCallableStatementHandler三种。选用哪种在mapper配置文件的每个statement里指定,默认的是PreparedStatementHandler。同时还要注意到StatementHandler是可以被拦截器拦截的,和Executor一样,被拦截器拦截后的对像是一个代理对象。由于mybatis没有实现数据库的物理分页,众多物理分页的实现都是在这个地方使用拦截器实现的,本文作者也实现了一个分页拦截器,在后续的章节会分享给大家,敬请期待。


初始化

  StatementHandler创建后需要执行一些初始操作,比如statement的开启和参数设置、对于PreparedStatement还需要执行参数的设置操作等。代码如下:

    private StatementprepareStatement(StatementHandler handler) throwsSQLException {  
Statementstmt;  
Connectionconnection = transaction.getConnection();  
stmt =handler.prepare(connection);  
handler.parameterize(stmt);  
return stmt;  
}  
 
  statement的开启和参数设置没什么特别的地方,handler.parameterize倒是可以看看是怎么回事。handler.parameterize通过调用ParameterHandlersetParameters完成参数的设置,ParameterHandler随着StatementHandler的创建而创建,默认的实现是DefaultParameterHandler

    publicParameterHandler newParameterHandler(MappedStatement mappedStatement, ObjectparameterObject, BoundSql boundSql) {  
ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,parameterObject,boundSql);  
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);  
returnparameterHandler;  
}  
 
  ExecutorStatementHandler一样,ParameterHandler也是可以被拦截的。


参数设置

  DefaultParameterHandler里设置参数的代码如下:

[java] view plaincopy
publicvoidsetParameters(PreparedStatement ps) throwsSQLException {  
ErrorContext.instance().activity("settingparameters").object(mappedStatement.getParameterMap().getId());  
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  
if(parameterMappings != null) {  
MetaObject metaObject = parameterObject == null ? null :configuration.newMetaObject(parameterObject);  
for (int i = 0; i< parameterMappings.size(); i++) {  
ParameterMapping parameterMapping = parameterMappings.get(i);  
if(parameterMapping.getMode() != ParameterMode.OUT) {  
Object value;  
String propertyName = parameterMapping.getProperty();  
PropertyTokenizer prop = newPropertyTokenizer(propertyName);  
if (parameterObject == null) {  
value = null;  
} elseif (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){  
value = parameterObject;  
} elseif (boundSql.hasAdditionalParameter(propertyName)){  
value = boundSql.getAdditionalParameter(propertyName);  
} elseif(propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)  
&& boundSql.hasAdditionalParameter(prop.getName())){  
value = boundSql.getAdditionalParameter(prop.getName());  
if (value != null) {  
value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));  
}  
} else {  
value = metaObject == null ? null :metaObject.getValue(propertyName);  
}  
TypeHandler typeHandler = parameterMapping.getTypeHandler();  
if (typeHandler == null) {  
thrownew ExecutorException("Therewas no TypeHandler found for parameter " + propertyName  + " of statement " + mappedStatement.getId());  
}  
typeHandler.setParameter(ps, i + 1, value,parameterMapping.getJdbcType());  
}  
}  
}  
}  
 
  这里面最重要的一句其实就是最后一句代码,它的作用是用合适的TypeHandler完成参数的设置。那么什么是合适的TypeHandler呢,它又是如何决断出来的呢?BaseStatementHandler的构造方法里有这么一句:
  this.boundSql= mappedStatement.getBoundSql(parameterObject);
  它触发了sql 的解析,在解析sql的过程中,TypeHandler也被决断出来了,决断的原则就是根据参数的类型和参数对应的JDBC类型决定使用哪个TypeHandler。比如:参数类型是String的话就用StringTypeHandler,参数类型是整数的话就用IntegerTypeHandler等
  参数设置完毕后,执行数据库操作(update或query)。如果是query最后还有个查询结果的处理过程。

结果处理
  结果处理使用ResultSetHandler来完成,默认的ResultSetHandler是FastResultSetHandler,它在创建StatementHandler时一起创建,代码如下

    publicResultSetHandler newResultSetHandler(Executor executor, MappedStatementmappedStatement,  
RowBoundsrowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSqlboundSql) {  
ResultSetHandler resultSetHandler =mappedStatement.hasNestedResultMaps() ? newNestedResultSetHandler(executor, mappedStatement, parameterHandler,resultHandler, boundSql, rowBounds): new FastResultSetHandler(executor,mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);  
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);  
returnresultSetHandler;  
}  
 
  可以看出ResultSetHandler也是可以被拦截的,可以编写自己的拦截器改变ResultSetHandler的默认行为。

 


  • ResultSetHandler内部一条记录一条记录的处理,在处理每条记录的每一列时会调用TypeHandler转换结果,如下:


  •     protectedbooleanapplyAutomaticMappings(ResultSet rs, List<String> unmappedColumnNames,MetaObject metaObject) throws SQLException {  
    booleanfoundValues = false;  
    for (StringcolumnName : unmappedColumnNames) {  
    final Stringproperty = metaObject.findProperty(columnName);  
    if (property!= null) {  
    final ClasspropertyType =metaObject.getSetterType(property);  
    if (typeHandlerRegistry.hasTypeHandler(propertyType)) {  
    final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propertyType);  
    final Object value = typeHandler.getResult(rs,columnName);  
    if (value != null) {  
    metaObject.setValue(property, value);  
    foundValues = true;  
    }  
    }  
    }  
    }  
    returnfoundValues;  
    }  
     


  从代码里可以看到,决断TypeHandler使用的是结果参数的属性类型。因此我们在定义作为结果的对象的属性时一定要考虑与数据库字段类型的兼容性。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-306083-1-1.html 上篇帖子: 基于Mybatis的通用Service层实现 下篇帖子: Struts2+Mybatis+Spring整合增删改查实例
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表