City city = sqlSession.selectOne("com.hhg.jerry.dao.CityDao.getById",1L);
Mybatis通过Session执行sql操作,参数为statement(mapper的namespace+select的id)和改statement所需参数,直接用接口调用当然更好:
public class App {
public static void main( String[] args ) throws Exception{
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
City city = null;
CityDao cityDao = sqlSession.getMapper(CityDao.class);
city = cityDao.getById(1L);
// city = sqlSession.selectOne("com.hhg.jerry.dao.CityDao.getById",1L);
System.out.println(city);
}
}
CityDao只是一个接口,sqlSession.getMapper返回的肯定是个代理类,下面通过代码一看究竟。
发现是MapperRegister,提供了getMapper、hasMaper、addMapper3个方法,先不看如何add,且看怎么get:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
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);
}
}
很简单,knowMappers取到factory,factory.newInstanse就是Mapper代理类,看下newInstance方法:
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
先new了一个mapperProxy对象(注意MapperProxy的接口InvocationHandler),然后调用Proxy.newProxyInstance生成了mapperInterface(CityDao)的代理类。
简单梳理下:
1、session.getMapper ,参数为Mapper的class对象(例如:CityDao.class)
2、mapregister通过class对象获取MapperProxyFactory
3、MapperProxyFactory生成MapperProxy对象(InvokeHandler),并使用MapperProxy生成代理对象
get后,就看knowMappers如何add:
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);
}
}
}
}
addMapper这个方法很简单,方法内部用MapperAnnotationBuiler去解析注解,看来是支持注解的,mark了。
addMapper在哪被调用了呢?自己找找吧。
调用Mapper的方法过程是怎样?先看看这个InvokeHandler对象的invoke方法:
如果是Object声明的方法(toString)就直接invoke,如果isDefaultMethod(构造函数吗?没看懂)就invodeDefaultMethod,当然我们调用city.getById就会生成MapperMethod,看下MapperMethod的execute方法:
switch了command的type,一会要看看如何生成不同的type,我们的case是select,下面又又很多根据method的判断,我们是走到最后那个else,然后调用sqlSession的selectOne方法。
简单总结下:构建mybatis的mapper时根据Mapper.xml的namespace找对应接口CityDao,并生成相应动态代理对象存放在knowMappers(key为class),sqlSession.getMapper返回改代理类,代理类执行调用mapperMethod对象(有command和method属性),mapperMethod对象再去调用sqlSession的方法(sqlSession的那个方法取决于command和method),下面看看细节:SqlCommand和MethodSignature,SqlCommand很简单,通过MappedStatement获取改方法的type(select or insert or update or delete),MappedStatement如何获取知道sql type?
接着看MethodSignature:
就是获取方法的一些信息(返回类型,是否返回空,是否返回map等),由于我们只有一个方法getById且返回的是City,不建议细看MethodSignature这个类,只看和getById相关即可。注意Type,ParameterizedType,不了解的建议去研究下。
回顾下,从sqlSession或者mapper接口开始,到返回结果,整个流程已经debug过了,当然内部还有很多细节没看。Mybatis操作数据库也是借助JDBC,下面看下JDBC部分