站在巨人的肩膀上

https://blog.csdn.net/xiaokang123456kao/article/details/76228684

一、概述

我们知道,Mybatis实现增删改查需要进行XML的配置,其基本的配置如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>

上面的配置表示我们在命名空间org.mybatis.example.BlogMapper下定义一个ID为selectBlog的查询操作,

其操作结果集为Blog,对应的语句为select * from Blog where id = #{id}

那么在实际使用时,我们可以采用如下形式:

SqlSession session = sqlSessionFactory.openSession();
try {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
session.close();
}

这种方式有很明显的缺点就是通过字符串去调用标签定义的SQL,

第一容易出错,

第二是当XML当中的id修改过以后你不知道在程序当中有多少个地方使用了这个ID,需要手工查找并一一修改。

在Mybatis的新版本中做了一些改进,支持这种方式调用:定义一个接口 方法名,参数需要与XML定义保持一致。

1、新建一个com.kang.mapper的包,定义map接口,接口名任意,这里是UserMapper。

package com.kang.mapper;  

import java.util.List;
import com.kang.pojo.User;
public interface UserMapper {
//根据用户id查询用户信息
public User findUserById(int id) throws Exception;
//查询用户列表
public List<User> findUserByUsername(String username) throws Exception;
//添加用户信息
public void insertUser(User user)throws Exception;
}
  • 2、配置xml文件 UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kang.mapper.UserMapper">
<!-- 注意这里的 namespace必须对应着map接口的全类名-->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select> <select id="findUserByUsername" parameterType="java.lang.String"
resultType="user">
select * from user where username like '%${value}%'
</select> <insert id="insertUser" parameterType="user">
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
select LAST_INSERT_ID()
</selectKey>
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>

注意到配置文件中的id属性值和map接口中的方法名是一一对应的。

  • 3、在SqlMapConfig.xml中加入映射文件
<!-- 加载 映射文件 -->
<mappers>
<mapper resource="map/UserMapper.xml" />
</mappers>
  • 4、调用方法
        //获取session
SqlSession session = sqlSessionFactory.openSession();
//获取mapper接口的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//调用代理对象方法
User user = userMapper.findUserById(27);
System.out.println(user);
//关闭session
session.close();
System.out.println("---------执行完毕-----------");

这样以来当我们修改了XML的ID以后,只需要修改接口中的方法就可以了,编译器会在其他使用该接口的地方报错,很容易进行修改。当然好处还不只这些,还可以通过与spring进行无缝集成,动态注入 等等。
关于Mybatis的Mapp接口使用方法请参考这篇博文Mybatis之Mapper动态代理
在上面的例子当中呢,UserMapper是一个接口 它并没有实现类,为什么接口可以直接使用呢? 那是因为MyBbatis使用了JDK动态代理机制动态生成了代理类,那么代理类又是如何多SqlSession进行封装的呢?带着这些疑问,让我们通过分析源代码的方式来解释这些问题。

二、源码解析

Mybatis关于包装Mapper的代码都在org.apache.ibatis.binding 这个包下面。其中有4个类: 
MapperRegistry 类是注册Mapper接口与获取代理类实例的工具类。其源码如下:

package org.apache.ibatis.binding;
import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//这个类通过名字就可以看出 是用来注册Mapper接口与获取生成代理类实例的工具类
public class MapperRegistry {
//全局配置文件对象
private Configuration config;
//一个HashMap Key是mapper的类型对象, Value是MapperProxyFactory对象
//这个MapperProxyFactory是创建Mapper代理对象的工厂 我们一会再分析
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
//获取生成的代理对象
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//通过Mapper的接口类型 去Map当中查找 如果为空就抛异常
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
//否则创建一个当前接口的代理对象 并且传入sqlSession
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
} public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
//注册Mapper接口
public <T> void addMapper(Class<T> 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 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);
}
}
}
}
public Collection<Class<?>> getMappers() {
return Collections.unmodifiableCollection(knownMappers.keySet());
}
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
//通过包名扫描下面所有接口
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
} }

类的getMapper方法里面最后会去调用MapperProxyFactory类的newInstance方法。

从上面的源码可以看出来,在调用getMapper方法前会初始化MapperProxyFactory,它是创建Mapper代理对象的工厂,其源码如下:

package org.apache.ibatis.binding;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.ibatis.session.SqlSession;
//这个类负责创建具体Mapper接口代理对象的工厂类
public class MapperProxyFactory<T> {
//具体Mapper接口的Class对象
private final Class<T> mapperInterface;
//该接口下面方法的缓存 key是方法对象 value是对接口中方法对象的封装
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
//构造参数没啥好说的
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//创建了一个代理类并返回
//关于Proxy的API 可以查看java官方的API
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
//在这里传入sqlSession 创建一个Mapper接口的代理类
public T newInstance(SqlSession sqlSession) {
//在这里创建了MapperProxy对象 这个类实现了JDK的动态代理接口 InvocationHandler
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
//调用上面的方法 返回一个接口的代理类
return newInstance(mapperProxy);
}
}

上述代码中的关键代码是

final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);

  

在这里创建了MapperProxy对象 这个类实现了JDK的动态代理接口 InvocationHandler,其源码如下:

package org.apache.ibatis.binding;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
//实现了JDK动态代理的接口 InvocationHandler
//在invoke方法中实现了代理方法调用的细节
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
//SqlSession
private final SqlSession sqlSession;
//接口的类型对象
private final Class<T> mapperInterface;
//接口中方法的缓存 有MapperProxyFactory传递过来的。
private final Map<Method, MapperMethod> methodCache;
//构造参数
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//接口代理对象所有的方法调用 都会调用该方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   // method.getDeclareingClass()返回申请此方法的对象,任何对象都是集成Object的,所以会包含Object中的基础方法。
// 基础方法无需代理实现
// 判断是不是基础方法 比如toString() hashCode()等,这些方法直接调用不需要处理
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
//这里进行缓存
final MapperMethod mapperMethod = cachedMapperMethod(method);
//调用mapperMethod.execute 核心的地方就在这个方法里,这个方法对才是真正对SqlSession进行的包装调用
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;
}
}

从源码的意思:从缓存中获得执行方法对应的MapperMethod类实例。如果MapperMethod类实例不存在的情况,创建加入缓存并返回相关的实例。

最后调用MapperMethod类的execute方法。
到这里我们可以看到,getMapper方法就是用来获得相关的数据操作类接口。而事实数据操作类邦定了动态代理。

所以操据操作类执行方法的时候,会触动每个方法相应的MapperProxy类的invoke方法。所以invoke方法返回的结果就是根据操作类执行方法的结果。

这样子我们就知道最后的任务交给了MapperMethod类实例。
那么MapperMethod的详细源码如下:

&&&&&&&&&&&&&&&&&&最精华精华精华的部分&&&&&&&&&&&&&&&&&&&&&&&&&&&

package org.apache.ibatis.binding;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
//这个类是整个代理机制的核心类,对Sqlsession当中的操作进行了封装
public class MapperMethod {
//一个内部封 封装了SQL标签的类型 insert update delete select
private final SqlCommand command;
//一个内部类 封装了方法的参数信息 返回类型信息等
private final MethodSignature method;
//构造参数
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, method);
}
//这个方法是对SqlSession的包装调用
public Object execute(SqlSession sqlSession, Object[] args) {
//定义返回结果
Object result;
//如果是INSERT操作
if (SqlCommandType.INSERT == command.getType()) {
//处理参数
Object param = method.convertArgsToSqlCommandParam(args);
//调用sqlSession的insert方法
result = rowCountResult(sqlSession.insert(command.getName(), param));
//如果是UPDATE操作 同上
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
//如果是DELETE操作 同上
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
//如果是SELECT操作 那么情况会多一些 但是也都和sqlSession的查询方法一一对应
} else if (SqlCommandType.SELECT == command.getType()) {
//如果返回void 并且参数有resultHandler
//则调用 void select(String statement, Object parameter, ResultHandler handler);方法
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//如果返回多行结果这调用 <E> List<E> selectList(String statement, Object parameter);
//executeForMany这个方法调用的
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//如果返回类型是MAP 则调用executeForMap方法
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else {
//否则就是查询单个对象
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
//如果全都不匹配 说明mapper中定义的方法不对
throw new BindingException("Unknown execution method for: " + command.getName());
}
//如果返回值为空 并且方法返回值类型是基础类型 并且不是VOID 则抛出异常
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;
}
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
result = (long) rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
result = (rowCount > 0);
} else {
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
}
return result;
}
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
if (void.class.equals(ms.getResultMaps().get(0).getType())) {
throw new BindingException("method " + command.getName()
+ " needs either a @ResultMap annotation, a @ResultType annotation,"
+ " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
}
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
} else {
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
}
}
//返回多行结果 调用sqlSession.selectList方法
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
//如果参数含有rowBounds则调用分页的查询
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
//没有分页则调用普通查询
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
Object collection = config.getObjectFactory().create(method.getReturnType());
MetaObject metaObject = config.newMetaObject(collection);
metaObject.addAll(list);
return collection;
}
@SuppressWarnings("unchecked")
private <E> E[] convertToArray(List<E> list) {
E[] array = (E[]) Array.newInstance(method.getReturnType().getComponentType(), list.size());
array = list.toArray(array);
return array;
}
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Map<K, V> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
public static class ParamMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -2212268410512043556L;
@Override
public V get(Object key) {
if (!super.containsKey(key)) {
throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
}
return super.get(key);
}
}

MapperMethod类里面有俩个成员:SqlCommand类和MethodSignature类。从名字上我们大概的能想到一个可能跟SQL语句有关系,一个可能跟要执行的方法有关系。事实也是如此。
上面代码使用一个内部类SqlCommand来封装底层的增删改查操作,确切来讲这一部分的内容跟XxxMapper的XML配置文件里面的select节点、delete节点等有关。我们都会知道节点上有id属性值。那么MyBatis框架会把每一个节点(如:select节点、delete节点)生成一个MappedStatement类。要找到MappedStatement类就必须通过id来获得。有一个细节要注意:代码用到的id = 当前接口类 + XML文件的节点的ID属性。其源码如下:

//一个内部类 封装了具体执行的动作
public static class SqlCommand {
//xml标签的id
private final String name;
//insert update delete select的具体类型
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {
//拿到全名 比如 org.mybatis.example.BlogMapper.selectBlog
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
//获取MappedStatement对象 这个对象封装了XML当中一个标签的所有信息 比如下面
//<select id="selectBlog" resultType="Blog">
//select * from Blog where id = #{id}
//</select>
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // 这里是一个BUG
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);
}
name = ms.getId();
type = ms.getSqlCommandType();
//判断SQL标签类型 未知就抛异常
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
}
//内部类 封装了接口当中方法的 参数类型 返回值类型 等信息
public static class MethodSignature {
//是否返回多调结果
private final boolean returnsMany;
//返回值是否是MAP
private final boolean returnsMap;
//返回值是否是VOID
private final boolean returnsVoid;
//返回值类型
private final Class<?> returnType;
//mapKey
private final String mapKey;
//resultHandler类型参数的位置
private final Integer resultHandlerIndex;
//rowBound类型参数的位置
private final Integer rowBoundsIndex;
//用来存放参数信息
private final SortedMap<Integer, String> params;
//是否存在命名参数
private final boolean hasNamedParameters;
//在这里对上面的属性进行初始化 就不一一详细说明了 具体实现细节可以看下面的代码。
public MethodSignature(Configuration configuration, Method method) throws BindingException {
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));
}
public Object convertArgsToSqlCommandParam(Object[] args) {
final int paramCount = params.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasNamedParameters && paramCount == 1) {
return args[params.keySet().iterator().next()];
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : params.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// issue #71, add param names as param1, param2...but ensure backward compatibility
final String genericParamName = "param" + String.valueOf(i + 1);
if (!param.containsKey(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
public boolean hasRowBounds() {
return (rowBoundsIndex != null);
}
public RowBounds extractRowBounds(Object[] args) {
return (hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null);
}
public boolean hasResultHandler() {
return (resultHandlerIndex != null);
}
public ResultHandler extractResultHandler(Object[] args) {
return (hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null);
}
public String getMapKey() {
return mapKey;
}
public Class<?> getReturnType() {
return returnType;
}
public boolean returnsMany() {
return returnsMany;
}
public boolean returnsMap() {
return returnsMap;
}
public boolean returnsVoid() {
return returnsVoid;
}
private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
Integer index = null;
final Class<?>[] argTypes = method.getParameterTypes();
for (int i = 0; i < argTypes.length; i++) {
if (paramType.isAssignableFrom(argTypes[i])) {
if (index == null) {
index = i;
} else {
throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
}
}
}
return index;
}
private String getMapKey(Method method) {
String mapKey = null;
if (Map.class.isAssignableFrom(method.getReturnType())) {
final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
if (mapKeyAnnotation != null) {
mapKey = mapKeyAnnotation.value();
}
}
return mapKey;
}
private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) {
final SortedMap<Integer, String> params = new TreeMap<Integer, String>();
final Class<?>[] argTypes = method.getParameterTypes();
for (int i = 0; i < argTypes.length; i++) {
if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) {
String paramName = String.valueOf(params.size());
if (hasNamedParameters) {
paramName = getParamNameFromAnnotation(method, i, paramName);
}
params.put(i, paramName);
}
}
return params;
}
private String getParamNameFromAnnotation(Method method, int i, String paramName) {
final Object[] paramAnnos = method.getParameterAnnotations()[i];
for (Object paramAnno : paramAnnos) {
if (paramAnno instanceof Param) {
paramName = ((Param) paramAnno).value();
}
}
return paramName;
}
private boolean hasNamedParams(Method method) {
boolean hasNamedParams = false;
final Object[][] paramAnnos = method.getParameterAnnotations();
for (Object[] paramAnno : paramAnnos) {
for (Object aParamAnno : paramAnno) {
if (aParamAnno instanceof Param) {
hasNamedParams = true;
break;
}
}
}
return hasNamedParams;
}
}
}

看到这里的时候,我们就可以回头去找一找在什么时候增加了MappedStatement类。上面之所以可以执行是建立在XML配置信息都被加载进来了。所以MappedStatement类也一定是在加载配置的时候就进行的。请读者们自行查看一下MapperBuilderAssistant类的addMappedStatement方法——加深理解。SqlCommand类的name成员和type成员我们还是关注一下。name成员就是节点的ID,type成员表示查寻还是更新或是删除。至于MethodSignature类呢。他用于说明方法的一些信息,主要有返回信息。

												

MyBatist庖丁解牛(二)的更多相关文章

  1. MyBatist庖丁解牛(五)

    很多时候我们在自己的每个service中没有中注入SqlSessionTemplate; 但是我们直接调用mapper接口方法就直接能够操作数据库 这个是为什么??下面开始解惑: Mybatis Sq ...

  2. MyBatist庖丁解牛(四)

    什么是MyBatis-Spring? MyBatis-Spring就是帮助你将MyBatis代码无缝的整合到Spring中.Spring将会加载必要的sqlSessionFactory类和sessio ...

  3. MyBatist庖丁解牛(三)

    从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个: SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能: Ex ...

  4. MyBatist庖丁解牛(一)

    站在巨人的肩膀上,感谢! https://www.jianshu.com/p/ec40a82cae28?utm_campaign=maleskine&utm_content=note& ...

  5. Kubernetes学习之路(二十)之K8S组件运行原理详解总结

    目录 一.看图说K8S 二.K8S的概念和术语 三.K8S集群组件 1.Master组件 2.Node组件 3.核心附件 四.K8S的网络模型 五.Kubernetes的核心对象详解 1.Pod资源对 ...

  6. Gradle 庖丁解牛(构建生命周期核心托付对象创建源代码浅析)

    [工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 上一篇<Gradle 庖丁解牛(构建源头源代码浅析)> ...

  7. ARM“庖丁解牛”之存储器管理单元MMU

    转:http://blog.sina.com.cn/s/blog_a07635070101bcbt.html 最近笔者详细地学习了由杜春雷老师编写的<ARM体系结构与编程>.对ARM存储管 ...

  8. 三十二、http与www服务介绍

    一.用户访问百度(www.baidu.com) 用户访问在url中输入地址后,首先会访问本地的缓存和hosts文件,如果没有,会访问本地DNS,在就是根域和顶级域名等,在前面已经说过了,这里不再赘述. ...

  9. 干货 | 45张图庖丁解牛18种Queue,你知道几种?

    在讲<21张图讲解集合的线程不安全>那一篇,我留了一个彩蛋,就是Queue(队列)还没有讲,这次我们重点来看看Java中的Queue家族,总共涉及到18种Queue.这篇恐怕是市面上最全最 ...

随机推荐

  1. Nothing but the key 属性全部依赖于主键 third norm form

    全依赖 Designs that Violate 1NF CustomerCustomer ID First Name Surname Telephone Number123 Pooja Singh ...

  2. RobotFramework教程使用笔记——初识RobotFramework

    1.创建项目 File->New Project 创建测试项目 Type选择Directory 右键项目创建测试套件,也可以理解为创建不同测试逻辑或者是测试业务 右键测试套件创建case 注:如 ...

  3. hdu-4081 Qin Shi Huang's National Road System(最小生成树+bfs)

    题目链接: Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: ...

  4. Chapter3——进入Android Dalvik虚拟机(二)

    Dalvik汇编语言基础 Dalvik虚拟机为自己设计了一套指令集,并制定了自己的指令格式和调用规范. 位描述约定如下: 每16位的字采用空格分隔开来 每个字母表示4位,每个字母按顺序从高字节开始,排 ...

  5. maven的常用构建命令

    mvn clean:删除项目根目录中的target目录

  6. yolo原理学习

    1.[yolov1]    第一步:将图像划分为S*S的栅格(grid cell),这里分成了7*7的grid cell.栅格的任务是:检测中心落在该栅格中的物体(注意,栅格中心未必与物体的中心重合, ...

  7. Spring 注释标签@Resource @Autowired 和@Inject的区别

    一些spring的开发人员在使用这三个标签进行注入的时候感到困惑.我来尝试解释一下这三个注解的主要区别.事实上,这三者非常相似,只存在一些微小的差别.在稍后的文章中会进行解释. @Resource-在 ...

  8. 《Kubernetes权威指南第2版》学习(一) Kubernetes是什么

    1.1 Kubernetes是什么? 首先,它是一个全新的基于容器技术的分布式架构领先方案.是谷歌的Borg(大规模集群管理系统)的一个开源版本. 其次,如果系统设计遵循了Kubernetes的设计思 ...

  9. 爬虫库之BeautifulSoup学习(一)

    Beautiful Soup的简介 简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据. 官方解释如下: Beautiful Soup提供一些简单的.pytho ...

  10. Maven安装,配置及更改本地资源库

    安装 1.确保已安装JDK,和配置JAVA_HOME环境变量 . 注:Maven 3.2 要求 JDK 1.6 或以上版本, 而 Maven 3.0/3.1 需要 JDK 1.5 或以上,这里安装的是 ...