標籤:
目的
系統中的業務介面需要調用MyBatis的SQL時,業務介面定義的參數不符合MyBatis自己內部的規範,那麼就需要把業務介面的參數轉換成MyBatis內部參數規,MapperProxy代理就完成了這一職責,下面就來分析一下。
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);}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;}
上面的invoke就是代理類回調的方法,而cachedMapperMethod方法,做了一個緩衝。這裡MapperMethod類的構造器與MapperMethod類的execute方法就是主要的邏輯,先來看一下MapperMethod構造器。
1 MapperMethod構造器
這裡有2個類的執行個體化,根據圖中的查詢分析一下這2個類。
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException { // cn.vansky.schedule.time.menu.dao.MenuMapper.findMenuByUserId String statementName = mapperInterface.getName() + "." + method.getName(); MappedStatement ms = null; if (configuration.hasStatement(statementName)) { ms = configuration.getMappedStatement(statementName); } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35 String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName(); if (configuration.hasStatement(parentStatementName)) { ms = configuration.getMappedStatement(parentStatementName); } } if (ms == null) { throw new BindingException("Invalid bound statement (not found): " + statementName); } // cn.vansky.schedule.time.menu.dao.MenuMapper.findMenuByUserId name = ms.getId(); // SELECT type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); }}public MethodSignature(Configuration configuration, Method method) throws BindingException { // 傳回值類型Class // interface java.util.List this.returnType = method.getReturnType(); // 有無傳回值 true:無,false:有 // false this.returnsVoid = void.class.equals(this.returnType); // 傳回型別是否是集合Collection或者是數組 // true this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); // 傳回型別是Map,擷取註解MapKey // 以上方法調用,值為null this.mapKey = getMapKey(method); // 傳回型別是否Map // false this.returnsMap = (this.mapKey != null); // 參數是否有@Param註解 // true this.hasNamedParameters = hasNamedParams(method); // null this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); // null this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); // 擷取參數列表 // 0 -> userId this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));}
SqlCommand類擷取處理的唯一標識及SQL語句類型,MethodSignature類對業務介面方法的入參類型及出參類型進行處理。
2 MapperMethed執行入口-->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()) { // 無傳回值void executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { // 集合Collection或數組 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { // Map result = executeForMap(sqlSession, args); } else { // 唯一結果 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } 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;}
增(INSERT)、改(UPDATE)、刪(DELETE),這3種操作可以歸納為一種,首先對業務介面的實際入參轉成MyBatis內部參數,然後調用SqlSession的處理方法,最後對結果進行處理,返回結果。
查(SELECT)有4中情況。
一:無傳回值
二:返回是集合Collection或者數組
三:返回是Map
四:返回一個結果
4種情況的具體細節不再分析。
總結
綜上首先需要先把業務介面的實際入參轉成MyBatis內部的參數,然後調用SqlSession相應的處理方法,最後對返回結果進行處理,在返回給業務介面。
串連MyBatis內部SqlSession與業務介面的代理類MapperProxy