Mybatis中Mapper接口如何和xml中sql关联(二)

继续上一篇,了解到,MyBatis中利用的JDK的动态代理,实现了接口管联xml中的sql,这里呢,只是知道了用什么方式,但是具体的怎么关联不清楚,需要再次学习其中的内容,go。

知道了动态代理后,先看MapperProxy类,他实现了InvocationHandler,那么也重写了invoke方法,找个这个方法
MapperProxy.class 中

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            MapperMethod mapperMethod = this.cachedMapperMethod(method);
            return mapperMethod.execute(this.sqlSession, args);
        }
    }

都知道执行ActivityCzMapper 接口的方法,会调用这里MapperProxy的invoke方法,看到第一行代码if判断

Object.class.equals(method.getDeclaringClass())

这里的method肯定是我们调用ActivityCzMapper 接口的方法,getDeclaringClass()肯定返回null, 因为我们的Mapper接口就没有实现类,不可能走下去,所以执行else里的代码,else里面执行了cachedMapperMethod()方法,我们看一下

private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
        if(mapperMethod == null) {
            mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
            this.methodCache.put(method, mapperMethod);
        }

        return mapperMethod;
    }

首先在methodCache的map中get传来的method(就是访问Mapper接口的方法)key,结果肯定是null,执行下面代码初始化MapperMethod三个参数的构造方法,第一个参数Mapper接口,第二个接口的方法,第三个是一个sqlSession的getConfiguration方法代表sqlsession的所有信息,在上一篇中传递的那个sqlsession,初始化MapperProxy类的构造方法时。

现在跳到这个MapperMethod的构造方法查看
MapperMethod.class中

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, method);
    }

看到初始化两个成员变量,command,method,在继续看SqlComman()方法是干什么的,跳到MapperProxyFactory类中的静态内部类SqlCommand,看到代码

public static class SqlCommand {
        private final String name;
        private final SqlCommandType type;

        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {
            String statementName = mapperInterface.getName() + "." + method.getName();
            MappedStatement ms = null;
            if(configuration.hasStatement(statementName)) {
                ms = configuration.getMappedStatement(statementName);
            } else if(!mapperInterface.equals(method.getDeclaringClass().getName())) {
                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);
            } else {
                this.name = ms.getId();
                this.type = ms.getSqlCommandType();
                if(this.type == SqlCommandType.UNKNOWN) {
                    throw new BindingException("Unknown execution method for: " + this.name);
                }
            }
        }

        public String getName() {
            return this.name;
        }

        public SqlCommandType getType() {
            return this.type;
        }
    }

哇,看这一条代码

String statementName = mapperInterface.getName() + "." + method.getName();

这个 mapperInterface.getName()就是Mapper接口的类名字,他必然和xml的
<mapper namespace="com.car.service.dao.ActivityCzMapper" > 一致,method.getName()得到方法的名字,
那就是mapper里的id,然后判断

MappedStatement ms = null;
if(configuration.hasStatement(statementName)) {
                ms = configuration.getMappedStatement(statementName);
            }

成立后,看下MappedStatement的意思,MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条SQL语句。
这样把他包装起来,在执行下面的代码,ms != null 后赋值,name 就是mapper xml 中sql 的id值,type就是sql的类型(sel,up,de)如果不知道,那就异常。

OK现在我们返回到MapperMethod中,说白了,传来的sqlSession到SqlCommand,在到MapperMethod。
现在在回到MapperProxy的invoke方法中走最后一步, return mapperMethod.execute(this.sqlSession, args);
查看execute方法

public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        if(SqlCommandType.INSERT == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        } else if(SqlCommandType.UPDATE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        } else if(SqlCommandType.DELETE == this.command.getType()) {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        } else {
            if(SqlCommandType.SELECT != this.command.getType()) {
                throw new BindingException("Unknown execution method for: " + this.command.getName());
            }

            if(this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if(this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if(this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
            }
        }

        if(result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

有点多,但是很明确,清楚看出来,首先判断sql的类型,up,sel,de的,在select中还有一层判断Mapper接口方法的返回值,我们查的是selectByPrimaryKey单个的,那就走selectOne()方法,例如我们的返回值List,那需要走executeForMany,可以看下这个源代码:

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
        Object param = this.method.convertArgsToSqlCommandParam(args);
        List result;
        if(this.method.hasRowBounds()) {
            RowBounds rowBounds = this.method.extractRowBounds(args);
            result = sqlSession.selectList(this.command.getName(), param, rowBounds);
        } else {
            result = sqlSession.selectList(this.command.getName(), param);
        }

        return !this.method.getReturnType().isAssignableFrom(result.getClass())?(this.method.getReturnType().isArray()?this.convertToArray(result):this.convertToDeclaredCollection(sqlSession.getConfiguration(), result)):result;
    }

恩,很稳,贴上一句关键点

 result = sqlSession.selectList(this.command.getName(), param, rowBounds);

这就不多说了,将结果返回。这样通过MapperProxy的 public Object invoke(Object proxy, Method method, Object[] args)方法
返回结果。这样就是一个执行Mapper接口方法执行sql的过程。在整个过程中还有很多地方未探索的,就比如在mybatis配置文件加载的时候干了什么,例如那个addMapper()方法到底在MyBatis哪个加载过程中add的,需要再次探索。以上是我的整个学习过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,332评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,930评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,204评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,348评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,356评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,447评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,862评论 3 394
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,516评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,710评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,518评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,582评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,295评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,848评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,881评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,121评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,737评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,280评论 2 341

推荐阅读更多精彩内容