Mybatis配置和接口映射原理
2016-11-15 00:00
363 查看
Mybatis可以把Mapper.xml文件直接映射到对应的接口,调用接口方法会自动去Mapper.xml文件中找到对应的标签,这个功能就是利用java的动态代理在binding包中实现的。
最终执行sql会进入到MapperMethod中execute方法:
上面就是根据接口、方法、配置参数找到对应的执行sql,并构造参数,解析执行结果,具体sql执行在sqlSession流程里面,后面再看。
一、注册Mapper
在初始化时会把获取到的Mapper接口注册到MapperRegistry,注册的时候创建一个Mapper代理工厂,这个工厂通过JDK的代理创建一个执行对象,创建代理需要的InvocationHandler为MapperProxy//接口注册 public class MapperRegistry { public <T> void addMapper(Class<T> type) { //如果是接口 if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { //放到map中, value为创建代理的工厂 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. //这里是解析Mapper接口里面的注解 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } }
二、获取接口对象
从knownMappers中根据接口类型取出对应的代理创建工厂,用该工厂创建代理。public class MapperRegistry { public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //取出MapperProxyFactory 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 class MapperProxyFactory<T> { /** * 需要创建代理的接口 */ private final Class<T> mapperInterface; /** * 执行方法的缓存,不需要每次都创建MapperMethod */ private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); 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) { //创建代理, InvocationHanderl是MapperProxy return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } /** * 传人sqlSession创建代理 * @param sqlSession * @return */ public T newInstance(SqlSession sqlSession) { //把代理执行需要用到的对象传入 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
三、调用接口方法
调用代理方法会进入到MapperProxy的public Object invoke(Object proxy, Method method, Object[] args)方法public class 3ff0 MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果方法是Object里面的则直接调用方法 if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //获取执行方法的封装对象 final MapperMethod mapperMethod = cachedMapperMethod(method); //里面就是找到对应的sql 执行sql语句 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; } }
最终执行sql会进入到MapperMethod中execute方法:
//具体的根据接口找到配置文件标签的类 public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { //SqlCommand封装该接口方法需要执行sql的相关属性,如:id(name), 类型 this.command = new SqlCommand(config, mapperInterface, method); //执行方法特性进行封装,用于构造sql参数,判断执行sql逻辑走哪条分支 this.method = new MethodSignature(config, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; //先找到对应的执行sql类型, sqlSession会调用不同方法 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); result = sqlSession.selectOne(command.getName(), param); } } 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; } }
上面就是根据接口、方法、配置参数找到对应的执行sql,并构造参数,解析执行结果,具体sql执行在sqlSession流程里面,后面再看。
相关文章推荐
- Mybatis配置和接口映射原理
- Mybatis配置和接口映射原理
- MyBatis Spring整合配置映射接口类与映射xml文件
- MyBatis Spring整合配置映射接口类与映射xml文件
- MyBatis Spring整合配置映射接口类与映射xml文件
- 使用mybatis-generator自动生成实体类,接口实现类和Mapper映射配置文件
- mybatis逆向工程自动生成实体类、接口以及映射Mapper.xml配置文件
- Java的MyBatis框架中Mapper映射配置的使用及原理解析
- (三)mybatis之通过接口加载映射配置文件
- MyBatis Spring整合配置映射接口类与映射xml文件
- MyBatis Spring整合配置映射接口类与映射xml文件
- MyBatis Spring整合配置映射接口类与映射xml文件
- MyBatis框架核心之(二)Mapper配置文件使用接口映射
- Mybatis SQL语句的两种映射方式 mapper的xml配置和接口两种方式
- MyBatis中Mapper接口映射到数据库原理分析
- 框架 day65 Mybatis入门(基础知识:框架原理,入门[curd],开发dao层,全局与映射配置)
- Java的MyBatis框架中Mapper映射配置的使用及原理解析
- Java的MyBatis框架中Mapper映射配置的使用及原理解析
- Mybatis-3动态代理来映射配置文件原理
- mybatis一般映射语句配置