视频素材库在哪里找,赣州网站优化,wordpress怎么进,建筑工程公司取名免费一、前言
日常工作中#xff0c;我们用到mybatis的时候#xff0c;都是写一个Mapper接口xml文件/注解形式#xff0c;然后就可以在业务层去调用我们在Mapper接口中定义的CRUD方法#xff0c;很方便#xff0c;但一直都没有去研究过执行逻辑#xff0c;下面附一篇我自己研…一、前言
日常工作中我们用到mybatis的时候都是写一个Mapper接口xml文件/注解形式然后就可以在业务层去调用我们在Mapper接口中定义的CRUD方法很方便但一直都没有去研究过执行逻辑下面附一篇我自己研究的过程。
二、注入过程分析
平时我们在使用时都是直接注解标在Mapper接口上从而去注入一个实例但我们是并没有实现过这个Mapper接口的就很容易想到必然是有一个代理类(MapperProxy)来帮我们执行真正的过程而且既然我们定义了接口那大概率就是JDK动态代理了。
注入的过程也比较简单在我们使用时会注明一个mapper扫描的包路径然后在SqlSessionFactory初始化的过程中会去解析每个mapper接口并将其放在Configuration的MapperRegistry中实际存放位置是在MapperRegistry中MapClass?, MapperProxyFactory? knownMappers。
接下来我们在使用mapper接口时会从knownMappers中去获取到对应的MapperProxyFactory从而去实例化真正的代理类代码如下 public T void addMapper(ClassT type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException(Type type is already known to the MapperRegistry.);}boolean loadCompleted false;try {knownMappers.put(type, new MapperProxyFactoryT(type));// Its 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 wont try.MapperAnnotationBuilder parser new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}} public T T getMapper(ClassT type, SqlSession sqlSession) {final MapperProxyFactoryT mapperProxyFactory (MapperProxyFactoryT) knownMappers.get(type);if (mapperProxyFactory null) {throw new BindingException(Type type is not known to the MapperRegistry.);}try {//关键步骤里面会使用JDK动态代理创建一个MapperProxyreturn mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException(Error getting mapper instance. Cause: e, e);}}
public class MapperProxyFactoryT {private final ClassT mapperInterface;private final MapMethod, MapperMethod methodCache new ConcurrentHashMapMethod, MapperMethod();public MapperProxyFactory(ClassT mapperInterface) {this.mapperInterface mapperInterface;}public ClassT getMapperInterface() {return mapperInterface;}public MapMethod, MapperMethod getMethodCache() {return methodCache;}SuppressWarnings(unchecked)protected T newInstance(MapperProxyT mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}public T newInstance(SqlSession sqlSession) {final MapperProxyT mapperProxy new MapperProxyT(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}}
三、执行过程分析
综上来看其实我们真正的方法是由MapperProxy来实现的接下来看看它的逻辑我们主要分析查询类的执行流程 /*** 执行入口*/Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}//缓存方法final MapperMethod mapperMethod cachedMapperMethod(method);//真正的执行逻辑return mapperMethod.execute(sqlSession, args);}/**
* 根据sql类型执行不同的分支
* convertArgsToSqlCommandParam会转换传入的参数
*/
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT://如果是void方法并且自定义了ResultMap等映射则执行此逻辑if (method.returnsVoid() method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result null;} else if (method.returnsMany()) {//返参为集合类型result executeForMany(sqlSession, args);} else if (method.returnsMap()) {//返参为Mapresult executeForMap(sqlSession, args);} else if (method.returnsCursor()) {//返参为游标result executeForCursor(sqlSession, args);} else {//返参为单对象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());}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;}
上面的查询方法都会走到下面这两行代码
//获取sql执行的映射过程对象包含了参数、数据源、返参等
MappedStatement ms configuration.getMappedStatement(statement);
//执行查询方法
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
接着会走到执行器executor主要有两类实现 org.apache.ibatis.executor.BaseExecutor基本执行器CachingExecutor带缓存的执行器 public E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {//获取BoundSql对象包含了解析动态SQl生成的sql语句以及参数映射的封装BoundSql boundSql ms.getBoundSql(parameter);//生成缓存keyCacheKey key createCacheKey(ms, parameter, rowBounds, boundSql);//查询return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}
public BoundSql getBoundSql(Object parameterObject) {//调用sqlSource获取BoundSqlsqlSource默认为DynamicSqlSource类型动态SQLBoundSql boundSql sqlSource.getBoundSql(parameterObject);ListParameterMapping parameterMappings boundSql.getParameterMappings();//若参数映射为空手动创建boundSqlif (parameterMappings null || parameterMappings.isEmpty()) {boundSql new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);}// check for nested result maps in parameter mappings (issue #30)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;} public BoundSql getBoundSql(Object parameterObject) {//获取上下文对象并将传入的入参对象及数据源标识放入bindingsDynamicContext context new DynamicContext(configuration, parameterObject);//基于动态sql解析成的ListSqlNode contents遍历赋值参数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 sqlSource.getBoundSql(parameterObject);for (Map.EntryString, Object entry : context.getBindings().entrySet()) {boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());}return boundSql;} 准备好BoundSql后就该执行真正的查询了主要链路如下