项目——通过自动回复机器人学Mybatis(七)
2017-09-03 20:39
417 查看
接口式编程
以下是sqlSession执行sql的一般方式SqlSession session = sqlSessionFactory.openSession(); try { Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); } finally { session.close(); }
诚然这种方式能够正常工作,并且对于使用旧版本 MyBatis 的用户来说也比较熟悉,不过现在有了一种更直白的方式。使用对于给定语句能够合理描述参数和返回值的接口(比如说BlogMapper.class),你现在不但可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换。
例如:
SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlog(101); } finally { session.close(); }
这种方式就是接口式编程
定义一个与Command.xml相对应的接口ICommand
修改Command.xml的namespace:
<mapper namespace="com.csdn.dao.ICommand">这样就形成了对应关系
等Mybatis+Spring时,会充分发挥接口式编程
首先Configuration.xml中的数据源将托付给Spring容器管理,sqlSession也一样,DBAccess将消失,还有CommandDao中sqlSession调用的接口式编程的代码统统由Spring自动实现,CommandDao也将消失,而ICommand将取代它成为新的Dao层
接口式编程原理
这部分的原理其实就是动态代理,详情见我关于设计模式——"代理模式"的博客
现在跟踪一下源码,看看它内部是怎么实现的
DBAccess:public SqlSession getSqlSession() throws Exception{ //通过配置文件获取数据库连接信息 Reader reader = Resources.getResourceAsReader("com/csdn/config/Configuration.xml"); //通过配置信息构建sqlSessionFactory SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //通过sqlSessionFactory打开一个数据库会话 SqlSession sqlSession = sessionFactory.openSession(); return sqlSession; }看了一下build()源码,发现这里的sqlSessionFactory创建的是DefaultSqlSession的实例
DefaultSqlSession的GetMapper()方法:
@Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }我们传入的参数是ICommand.class,由于泛型的作用,getMapper返回值类型是ICommand
继续看getMapper方法的实现
@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); } }可以看到是mapperProxyFactory代理工厂生产了一个代理对象
而代理工厂的来历:final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
type是ICommand.class,看看knownMappers的来历:
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
由于addMapper方法,knownMappers填充了key为type,MapperProxyFactory为value的键值对
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 { 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); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
回到上面的mapperProxyFactory.newInstance(sqlSession);
看newInstance源码:
@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); }先调用下面那个的newInstance方法,在调用上面那个newInstance方法,它的返回值T就是我们最后得到的ICommand对象
这是JDK提供的代理类方法
Proxy.newProxyInstance
当我们调用iCommand.queryCommandList(comm)时将执行mapperProxy中的invoke()
MapperProxy:
public class MapperProxy<T> implements InvocationHandler, Serializable { .................略.............. @Override 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); } 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; } }iCommand.queryCommandList(comm)时invoke()方法中传入的参数method就是queryCommandList(),args就是comm
但是这段代码不回执行
if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }因为Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy),学过动态代理的人应该知道此方法的第一个应该是:被代理实例.getClassLoader()而不是:接口.getClassLoader(),
所以if判断为false
执行的将只有
final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args);
进入mapperMethod类:
public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); } 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); 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; }................................因为我们执行的方法是select类型,且返回值是List
将进入:
else if (method.returnsMany()) { result = executeForMany(sqlSession, args);
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else { result = sqlSession.<E>selectList(command.getName(), param); } // issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; }
最后在这里看到了
sqlSession.<E>selectList......
相关文章推荐
- 项目——通过自动回复机器人学Mybatis(五)
- 项目——通过自动回复机器人学Mybatis(深入解析读取xml源码)(九)
- 项目——通过自动回复机器人学Mybatis(自己添加ajax代码优化)(三)
- 项目——通过自动回复机器人学Mybatis(六)
- 项目——通过自动回复机器人学Mybatis(二)
- 项目——通过自动回复机器人学Mybatis(深入解析拦截器源码)(八)
- 项目——通过自动回复机器人学MyBatis(一)
- 项目——通过自动回复机器人学Mybatis(ajax优化)(四)
- 【Mybatis】通过自动回复机器人学Mybatis---基础版(4下)
- 【Mybatis】通过自动回复机器人学Mybatis---基础版(4上)
- 通过自动回复机器人学Mybatis:代码重构(分层)
- 通过自动回复机器人学Mybatis:OGNL+log4j.properties
- 通过自动回复机器人学Mybatis——基础版——慕课网
- 【Mybatis】通过自动回复机器人学Mybatis---基础版(5-6结束)
- 【Mybatis】通过自动回复机器人学Mybatis---基础版(1-3)
- 通过自动回复机器人学Mybatis 笔记:接口式编程
- 通过自动回复机器人学Mybatis——加强版——慕课网
- 通过自动回复机器人学Mybatis:原始版本(包括JDBC、pom.xml等)
- 通过自动回复机器人学Mybatis—基础版
- 通过自动回复机器人学Mybatis---基础版