mybatis之bind模块 小灰灰 2022-09-02 11:51 110阅读 0赞 # mybatis之bind模块 # mybatis通过定义接口mapper,不需要继承或实现接口,Mapper接口中的方法定义select|update|insert|delete方法,通过关联映射文件中定义的sql来执行查询。 .....org.apache.ibats ....................binding ...........................BindingException ...........................MapperMethod ...........................MapperProxy ...........................MapperProxyFactory ...........................MapperRegistry ## 执行流程 ## MybatisUse DefaultSqlSession Configuration MapperRegistry MapperProxyFactory MapperProxy MapperMethod public class MybatisUse { public static void main(String[] args) throws Exception{ //加载mybatis的配置文件 String mybatisXml = "mybatis.xml"; InputStream inputStream = Resources.getResourceAsStream(mybatisXml); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获取sqlSession的实现DefaultSqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //通过MapperRegistry获取到Mapper的代理 CleanPolicyMapper cleanPolicyMapper = sqlSession.getMapper(CleanPolicyMapper.class); // 通过MapperMethod执行查询、更新、插入、删除方法逻辑 CleanPolicy cleanPolicy = cleanPolicyMapper.selectById(292L); System.out.println("name:" + cleanPolicy.getName()); sqlSession.commit(); sqlSession.flushStatements(); sqlSession.close(); } } 来大体看看调用过程 CleanPolicyMapper cleanPolicyMapper = sqlSession.getMapper(CleanPolicyMapper.class); //DefaultSqlSession public class DefaultSqlSession implements SqlSession { @Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } } public class Configuration { public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } } public class MapperRegistry { 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); } } } //调用接口对应的方法 CleanPolicy cleanPolicy = cleanPolicyMapper.selectById(292L); //CleanPolicy ---> 对应这里的MapperProxy对象 public class MapperProxy<T> implements InvocationHanlder, Serializable { //代理调用方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); //调用执行方法 return mapperMethod.execute(sqlSession, args); } } public class MapperMethod { //根据执行选择对应的方法 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; } } classDiagram MapperRegistry *-- MapperProxyFactory MapperProxyFactory <.. MapperProxy MapperProxy *-- MapperMethod MapperProxyFactory *-- MapperMethod ## MapperRegistry ## MapperRegistry映射注册器核心两个方法getMapper和addMapper public class MapperRegistry { private final Configuration config; private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>(); // 查询mapper接口的代理对象 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的代理对象,addMapper调用的时机是解析mybatis.xml时 public <T> void addMapper(Class<T> type) { //mapper必须时接口,不能是类 if (type.isInterface()) { //mapper接口必须全局唯一,否则加载时后报错 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); //在初始化过程中提前发现问题:解析关联mapper接口中方法与映射文件对应的sql,没有则报错, parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } } ## MapperProxyFactory ## mapper代理工厂,负责实例化代理对象,解耦创建与使用 public class MapperProxyFactory<T> { //使用jdk的代理,jdk代理的目标对象必须时接口,这就是mapper必须是接口的原因 @SuppressWarnings("unchecked") 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); } } ## MapperProxy ## MapperProxy使用jdk动态代理 public class MapperProxy<T> implements InvocationHanlder, Serializable { // mapper接口 private final Class<T> mapperInterface; //缓存method方法对应的MapperMethod,加快执行速度 private final Map<Method, MapperMethod> methodCache; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { 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); //执行对应的方法 return mapperMethod.execute(sqlSession, args); } 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; } //缓存查询或创建mapperMethod方法 private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { //缓存没有,则新建mapperMethod方法 mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } } 我们这里关注一下new MapperMethod()构造函数,看看他都干了那些事儿 public class MapperMethod { public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); } public static class SqlCommand { // MapperStatement的id,全局唯一,用来关联映射文件的执行语句 private final String name; //执行语句的类型select、update、insert、delete private final SqlCommandType type; } //解析出执行方法的返回类型、参数类型 public static class MethodSignature { private final boolean returnsMany; private final boolean returnsMap; private final boolean returnsVoid; private final boolean returnsCursor; private final Class<?> returnType; private final String mapKey; private final Integer resultHandlerIndex; private final Integer rowBoundsIndex; private final ParamNameResolver paramNameResolver; } // MapperProxy动态代理调用这里执行真正的方法 public Object execute(SqlSession sqlSession, Object[] args) { Object result; //在MapperMethod的构造器已经存储了执行语句的类型.以及根据返回类型来确定执行哪个操作:selectOne、selectMany...... 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; } } 总的来说bind包下的四个类的组合使用,封装了动态sql与mapper接方法的关系以及最终调用执行的胶水代码。 核心: map、jdk动态代理、工厂模式 ## 总结 ## 1.mybatis通过定义接口mapper,不需要继承或实现接口,Mapper接口中的方法定义select|update|insert|delete方法,通过关联映射文件中定义的sql来执行查询。 2.getMapper查询接口对应的代理工厂(有保存对应的接口) 3.MapperRegistry映射注册器核心两个方法getMapper和addMapper 4.插入mapper的代理对象,addMapper调用的时机是解析mybatis.xml时 5.mapper接口必须全局唯一,否则加载时后报错 6.在初始化过程中提前发现问题:解析关联mapper接口中方法与映射文件对应的sql,没有则报错, 7.MapperProxyFactory:mapper代理工厂,负责实例化代理对象,解耦创建与使用 8.使用jdk的代理,jdk代理的目标对象必须时接口,这就是mapper必须是接口的原因 9.缓存method方法对应的MapperMethod,没有则创建以及加入缓存,加快执行速度. 10.MapperStatement的id,全局唯一,用来关联映射文件的执行语句 11.在MapperMethod的构造器已经存储了执行语句的类型.以及根据返回类型来确定执行哪个操作:selectOne、selectMany…
还没有评论,来说两句吧...