用了这么久mybatis,不了解一下执行过程怎么可以呢,是吧,今天我就带大家一起看看mybatis是怎么执行的。如果我们不集成spring的话是如何使用mybatis的?根据官网的教程我们一般都是使用以下方式吧:
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
这里获得了SqlSessionFactory,接下来就是打开一个session,也就是获得SqlSession,然后就获得一个Mapper实例(mybatis通过反射为我们生成的),然后就可以调用这个mapper接口里的方法了,如果是查询的话那么我们就可以通过对应的实例去接收,整个过程就几行代码,如果我们使用jdbc的话大家应该也知道那个代码量还是非常多的,还要自己new一个实例进行封装,现在这些事mybatis都帮我们做了,接下来我们就看看mybatis怎么帮我们做的。
通过上面的代码可以看到,入口就在build方法,这个方法内部通过XMLConfigBuilder对配置文件进行解析,解析部分就不分析了,主要对xml文件的解析,不懂xml文件解析的话可以先学习一下,我主要分析它如何获取mapper,以及如何封装参数返回给我们。
首先,如何获取mapper,毫无疑问入口就在getMapper方法,跟进去就发现是通过Configuration这个类来获取的,我们上一篇讨论了一些相关的基础类,它里面有一个MapperRegistry类,先在来看看核心是如何实现的,首先这个类里面有一个knownMappers成员变量,保存的是已知的mapper,那什么是已知的mapper?这个就是在刚才解析配置文件的时候生成的,解析配置文件的时候会解析相应的mapper文件,mapper配置里面不是要指定namespace吗?mybatis通过这个namespace生成一个Class对象作为键,值就是MapperProxyFactory<?>,addMapper方法里面有一句:
knownMappers.put(type, new MapperProxyFactory<T>(type));
可以看到就是这个时候put进去,type就是一个Class对象。扯远了,回到getMapper方法,看到以下语句:
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
这个获得一个Mapper代理的工厂,这个工厂就是用来生产代理的,生成的代理是用来干嘛的?当然是用来帮我们做事的啦,像对象封装这种事现在有代理帮我们做了。
好了,继续看,mybatis帮我们生成了一个MapperProxy类的实例,可以看到这个实例实现了InvocationHandler接口,这个是什么鬼?不就是jdk的反射调用处理接口吗?忘记反射或者不懂反射的同学自行复习或学习。在invoke方法里面通过MapperMethod这个类的execute方法执行相关的方法,来看一下核心调用的庐山真面目:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
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;
}
这个result就是我们的查询结果了,这里只是分析了一下执行的过程,并没有深入去分析每一个步骤,比如参数的封装,sql语句解析,。可以看到在上一篇中提到的很多类这里都没有出现,只出现了一个
MapperRegistry,希望大家能够有个初步了了解。