您的位置:首页 > 移动开发

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