本文共 6053 字,大约阅读时间需要 20 分钟。
在上一篇文章我们已经得到了mapper的代理对象,接下来我们对demoMapper.getDemo(1)这种语句进行分析。 由于返回的mapper是个代理对象,因此会进入invoke方法,接下来我们来看看MapperProxy的invoke方法。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }
Object.class.equals(method.getDeclaringClass())的意思是如果定义方法的类是个具体类就使用具体类的实现,如果是接口则往下执行。
private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }
methodCache是个Map对象,第一次取时为空会进入MapperMethod构造方法。
public MapperMethod(Class mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); }
public SqlCommand(Configuration configuration, Class mapperInterface, Method method) { String statementName = mapperInterface.getName() + "." + method.getName(); MappedStatement ms = null; if (configuration.hasStatement(statementName)) { ms = configuration.getMappedStatement(statementName); } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35 String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName(); if (configuration.hasStatement(parentStatementName)) { ms = configuration.getMappedStatement(parentStatementName); } } if (ms == null) { if(method.getAnnotation(Flush.class) != null){ name = null; type = SqlCommandType.FLUSH; } else { throw new BindingException("Invalid bound statement (not found): " + statementName); } } else { name = ms.getId(); type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); } } }
这里主要做了这几件事,根据方法名以及接口名的组合从configuration中取得对应的MappedStatement,然后从中取出name和type。
MethodSignature构造方法如下
public MethodSignature(Configuration configuration, Method method) { this.returnType = method.getReturnType(); this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); this.mapKey = getMapKey(method); this.returnsMap = (this.mapKey != null); this.hasNamedParameters = hasNamedParams(method); this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters)); }
这里主要是标记下入参中的RowBounds、ResultHandler类型参数,以及对返回值进行些标记。 MethodSignature与SqlCommand初始化后MapperMethod也就构造完成,然后methodCache将method与mapperMethod关系保留。 接下来就是屌用mapperMethod的execute方法来执行。
public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { 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; }
我们的mapper方法定义如下,返回的不是集合也不为空,因此进入convertArgsToSqlCommandParam方法。 convertArgsToSqlCommandParam对入参进行转换,如果没有入参返回null如果一个入参对象则直接返回,多个入参则封装成个map对象返回。
public Demo getDemo(long id);
现在进入到sqlSession的selectOne方法,使用ibatis的同学应该相当熟悉。 在selectOne内部调用了selectList方法,然后返回集合对象的第一个元素,如果集合对象大于1个则抛错。
@Override publicList 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(); } }
rowBounds是用来分页的,暂时不管该对象,默认的初始值如下
public static final int NO_ROW_OFFSET = 0;public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
selectList也是根据statement从configuration中取得mappedStatement,然后交由executor来执行,sqlSessionFactory构建的时候默认使用的是simpleExecutor
到这里我们分析了mapper接口的方法最终是交到executor来执行。
转载地址:http://cqizo.baihongyu.com/