Mybatis源码分析——Mapper与接口绑定源码分析
2019-06-13 08:29
1576 查看
源码分析前简介
Mybatis 是一个面向 sql的持久层框架,它可实现动态拼装 sql,极其灵活,同时避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集,其插件机制允许在已映射语句执行过程中的某一点进行拦截调用等等,让我忍不住想要看看它的源码。我们都知道 Mapper 是一个接口,它的每个方式是我们与数据库交互的入口,每个 Mapper 都有与之相对应的一个 XML 文件,我们可以在 XML 里面自由快活地写 sql,当然我们也可以用注解的形式写在接口方法上,但终究还是没 XML 灵活,那么问题来了,Mybatis 是如何注册与绑定 Mapper 的呢?下面我带你揭开这个神秘的面纱。
首先我们来看看用 Mybatis 执行 sql 的两种方法
直接操作 SqlSession 方法
public User findUserById(Integer userId) { SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession(); try { // namespace + statementId return sqlSession.selectOne("com.xuyu.mybatis.UserMapper.findUserById", userId); } finally { sqlSession.close(); } }
通过 Mapper 接口
public User findUserById(Integer userId) { SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); return userMapper.findUserById(userId); } finally { sqlSession.close(); } }
public class UserMapper { User findUserById(@Param("userId") String userId); }
<?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.objcoding.mybatis.UserMapper"> <select id="findUserById" resultType="com.xuyu.mybatis.User"> SELECT * FROM user WHERE user_id=#{userId} </select> </mapper>
很明显,第二种方法可以大大降低了手工写 namespace 出现错误的概率,且用 Mapper 可以直接操作方法来实现数据链接,看起来优雅很多。
源码分析流程图
1.从MapperRegistry获取查询对应绑定接口
2.动态生成代理对象
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); } }
3.调用MapperProxyFactory去帮我们创建代理类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //检查看下有没有注册mapper接口 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); } }
创建代理类
public T newInstance(SqlSession sqlSession) { //创建代理类 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
4.执行这条语句
UserEntity user = mapper.getUser(2);
5.跳转到MapperProxy的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //这里源码分析缓存 final MapperMethod mapperMethod = cachedMapperMethod(method); //这里源码分析执行 return mapperMethod.execute(sqlSession, args); }
6.执行到这条语句
final MapperMethod mapperMethod = cachedMapperMethod(method);
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; }
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { //这里构建sql,源码分析 this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); }
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) { String statementName = mapperInterface.getName() + "." + method.getName(); MappedStatement ms = null; if (configuration.hasStatement(statementName)) { ms = configuration.getMappedStatement(statementName); } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35 String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName(); if (configuration.hasStatement(parentStatementName)) { ms = configuration.getMappedStatement(parentStatementName); } } if (ms == null) { if(method.getAnnotation(Flush.class) != null){ name = null; type = SqlCommandType.FLUSH; } else { throw new BindingException("Invalid bound statement (not found): " + statementName); } } else { name = ms.getId(); type = ms.getSqlCommandType(); if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); } } }
回到invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); //这里源码分析开始 return mapperMethod.execute(sqlSession, args); }
7.进入到MapperMethod的execute方法就能简单找到SqlSession的删除、更新、查询、选择方法
public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { 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 { Object param = method.convertArgsToSqlCommandParam(args); //底层执行SQL语句,封装User结果集 result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { 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; }
版权@须臾之余https://my.oschina.net/u/3995125
本文参考蚂蚁课堂:http://www.mayikt.com
(adsbygoogle = window.adsbygoogle || []).push({});相关文章推荐
- 深度Mybatis源码分析——SqlSessionFactoryBuilder(建造者模式),Mapper接口绑定原理(代理模式)
- SprignMVC+myBatis整合+mybatis源码分析+动态代理实现流程+如何根据mapper接口生成其实现类
- mybatis源码学习之执行过程分析(3)——mapper接口的获取
- Mybatis 源码解析三、Mapper接口与mapper.xml文件绑定
- Mybatis Mapper接口是如何找到实现类的-源码分析
- 【Mybatis】- mapper.java接口如何操作数据库-源码分析
- MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
- mybatis简单案例源码详细【注释全面】——Dao层接口(UserMapper.java)
- 如何整合Spring和Mybatis的思路分析(使用面向接口的mapper代理,不用再去写Dao的实现类)
- MyBatis-3.4.2-源码分析12:XML解析之mapperElement(root.evalNode("mappers"))
- Mybatis3源码分析(04)-加载Configuration-XMLMapperBuilder加载ResultMap
- Mybatis3源码分析(19)-Mapper生成过程-示例
- Mybatis3源码分析(21)-Mapper实现-动态代理
- MyBatis 源码分析——SqlSession接口和Executor类
- Mybatis源码解析-MapperRegistry注册mapper接口
- MyBatis中Mapper接口映射到数据库原理分析
- MyBatis 源码分析——生成Statement接口实例
- Mybatis3源码分析(21)-Mapper实现-动态代理
- Mybatis执行dao接口方法的流程梳理及源码分析
- MyBatis接口(Bean)与配置信息(Mapper)绑定