MyBatis中binding 模块的作用是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
org.apache.ibatis.binding
封装ibatis编程模型 ibatis编程模型中,SqlSession作为sql执行的入口,实用方法为sqlSession.selectOne(namespace+id, 参数列表)的形式(例:sqlSession.selectOne("com.enjoylearning.mybatis.mapper.TUserMapper.selectByPrimaryKey", 2)),不易于维护和阅读
找到SqlSession中对应的方法(insert|update|select)
找到命名空间和方法名(两维坐标)
传递参数
MapperRegistry:mapper接口和对应的代理工厂的注册中心;是Configuration的成员变量
MapperProxyFactory:用于生成mapper接口动态代理的实例对象;保证mapper实例对象是局部变量(为什么不缓存?见文章最后)
MapperMethod:封装了Mapper接口中对应的方法信息,以继对应sql语句的信息.MapperMethod对象不记录任何状态,所以它可以再多个代理对象之间共享;sqlCommand:封装sql语句;MtehodSignature:封装mapper接口的入参和返回类型
个人理解
MapperRegistry的Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); key:mapper的Class对象 value:生产这个mapper的动态代理示例的工厂
MapperProxyFactory:Map<Method, MapperMethod> methodCache key:方法的反射类 value 方法的信息(sql语句,入参,返回值)
public class MapperRegistry { private final Configuration config;//config对象,mybatis全局唯一的 //记录了mapper接口与对应MapperProxyFactory之间的关系 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); public MapperRegistry(Configuration config) { this.config = config; } @SuppressWarnings("unchecked") 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); }
/** * * 用于生成mapper接口动态代理的实例对象; * @author Lasse Voss */ public class MapperProxyFactory<T> { //mapper接口的class对象 private final Class<T> mapperInterface; //key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享 private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public class MapperMethod { //从configuration中获取方法的命名空间.方法名以及SQL语句的类型 private final SqlCommand command; //封装mapper接口方法的相关信息(入参,返回类型); private final MethodSignature method;
public class MapperProxyFactory<T> { //mapper接口的class对象 private final Class<T> mapperInterface; //key是mapper接口中的某个方法的method对象,value是对应的MapperMethod,MapperMethod对象不记录任何状态信息,所以它可以在多个代理对象之间共享 private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { //创建实现了mapper接口的动态代理对象 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { //每次调用都会创建新的MapperProxy对象 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
两个newInstance方法生成mapper的动态代理对象MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable { ....... @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增强 return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } //从缓存中获取mapperMethod对象,如果缓存中没有,则创建一个,并添加到缓存中 final MapperMethod mapperMethod = cachedMapperMethod(method); //调用execute方法执行sql return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); }
从invoke方法可以看出 是mapperMethod调用了execute方法
public class MapperMethod { ...... public Object execute(SqlSession sqlSession, Object[] args) { Object result; //根据sql语句类型以及接口返回的参数选择调用不同的 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()) {//返回值为void executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) {//返回值为集合或者数组 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) {//返回值为map result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) {//返回值为游标 result = executeForCursor(sqlSession, args); } else {//处理返回为单一对象的情况 //通过参数解析器解析解析参数 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = OptionalUtil.ofNullable(result); } } 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 = sqlSession.selectOne(command.getName(), param); 这里使用了ibatis的用法
mapperProxy做查询依赖的还是sqlSession.按照现在的代码结构来说,如果缓存了mapperProxy,当sqlSession失效,mapperProxy也就失效了. 如果修改代结构,那么sqlSession就要作为查询参数
// 原本 TUser user = mapper.selectByPrimaryKey(2); // 修改后的写法 TUser user = mapper.selectByPrimaryKey(sqslSession,2);
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。