title:Mapper接口
date:2017年11月25日17:27:07
categories: Mybatis
前言
之前在说到Mybatis插件的时候说以后要好好分析下Mybatis的四大组件,之前一直在为公司忙搭建RocketMQ消息集群的事情,就耽误了一段时间,最近终于有时间来分析下了,要分析四大组件,还是要从一次完整的查询过程说起,把Mybatis的核心源码都过一遍。
从一次查询说起
我们做一次Mybatis的查询测试
@Test
public void testSelect() {
SqlSession session = null;
try {
session = MybatisUtil.getCurrentSession();
UserDao userDao = session.getMapper(UserDao.class);
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(3);
list.add(25);
List<User> userList = userDao.queryList(list);
System.out.println(JSON.toJSONString(userList));
} catch (Exception e) {
// TODO: handle exception
} finally {
if (session != null)
session.close();
}
}
对应的Mapper配置文件
<?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.wangcc.mybatis.dao.UserDao">
<resultMap type="User" id="UserMapper">
<result property="id" column="t_id"/>
<result property="name" column="t_name"/>
</resultMap>
<select id="queryList" resultMap="UserMapper">
select * from t_user where t_id in
<foreach collection="list" item="uId" index="index" open="(" separator="," close=")">#{uId}</foreach>
</select>
<insert id="insert" parameterType="User">
insert into t_user (t_name,address) values(#{name},#{address})
</insert>
<insert id="batchInsert" >
insert into t_user (t_name,address) values <foreach collection="list" item="user" separator=",">
(#{user.name},#{user.address})
</foreach>
</insert>
</mapper>
如果你是初次使用Mybatis,那么你一定会好奇为什么这里的UserDao都是接口并不是具体的实现类。
而如果你熟悉JDK动态代理,那么一看到这种接口可以直接执行方法的情况,那么肯定是用了代理,那么虽然我们能够知道为什么可以直接执行,但是我们也要了解他具体是怎么实现的。我们就以
UserDao userDao = session.getMapper(UserDao.class);
为Debug入口。
进入getMapper方法
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
我们发现还是调用了构建SqlSession的最重要的对象Configuration的方法。
DefaultSqlSession对象中保存着Configuration对象的引用。
进入到Configuration中
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
我们发现是调用的MapperRegistry对象(Configuration保存着其引用,在初始化Mybatis的配置文件的时候,就会将所有的Mapper接口注册到MapperRegistry的Map\
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//在knownMappers中查找是否已经注册了,如果没有注册直接抛出异常,注册Mapper接口是在初始化Mybatis配置文件时必须就要完成的工作,不能在调用getMapper的时候再动态去注册
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
进入newInstance方法
public T newInstance(SqlSession sqlSession) {
//封装了一个MapperProxy对象,即组合模式,为了解耦,我们一般能不用继承就不用继续,尽量使用组合,这里MapperProxy是实现了InvocationHandler接口的类
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
newInstance(mapperProxy) 得到Mapper接口的代理对象
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
到现在,如何得到Mapper接口的代理对象的流程就走完了