赞
踩
一、框架设计
1. 接口层—和数据库交互的方式:以使用Mapper接口为例
将配置文件中的每一个<mapper>
节点抽象为一个 Mapper 接口,这个接口中声明的方法和跟Mapper.xml中的<select|update|delete|insert> 节点项对应,id值对应方法名称,parameterType 值对应方法的入参类型,而resultMap 值则对应返回值类型。
配置好后,调用SqlSession.getMapper(XXXMapper.class) 方法,MyBatis 会根据接口声明的方法信息,通过动态代理机制生成一个Mapper 实例,当调用接口方法时,根据这个方法的方法名和参数类型,确定Statement Id,底层还是通过 SqlSession.select/update( “statementId”, parameter) 等来实现对数据库的操作。
2. 数据处理层:可以说是MyBatis 的核心
a. 通过传入参数构建动态SQL语句;
b. SQL语句的执行以及封装查询结果集成List
2.1 参数映射和动态SQL语句生成
MyBatis 通过传入的参数值,使用 Ognl 来动态地构造SQL语句。
参数映射指的是java 数据和jdbc数据之间的转换:包括两个过程:查询阶段,将java类型的数据,转换成jdbc类型的数据,通过 preparedStatement.setXXX() 来设值;另一个就是对resultset查询结果集的jdbcType 数据转换成java 数据类型。
二、MyBatis的主要构件及其相互关系
mybatis底层还是采用原生jdbc来对数据库进行操作的,只是通过SqlSession, Executor, StatementHandler,ParameterHandler, ResultHandler和TypeHandler等几个处理器封装了这些过程.
注: StatementHandler通过ParameterHandler与ResultHandler分别进行参数预编译 与结果处理。而ParameterHandler与ResultHandler都使用TypeHandler进行映射。
三、从MyBatis一次select 查询语句来分析MyBatis的架构设计
3.1、数据准备
i. 准备数据库数据,创建EMPLOYEES表,插入数据
ii. 配置Mybatis的配置文件,命名为Config.xml
iii. 创建Employee实体Bean 以及配置Mapper配置文件、相应接口
iv. 编写客户端代码
3.2、SqlSession 的工作过程分析:
a) 创建SqlSession对象:
SqlSession sqlSession = factory.openSession();
MyBatis封装了对数据库的访问,把对数据库的会话和事务控制放到了SqlSession对象中。
b) 为SqlSession传递一个配置的Sql语句 的Statement Id和参数,然后返回结果
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//1.根据Statement Id,在mybatis 配置对象Configuration中查找和配置文件相对应的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//2. 将查询任务委托给MyBatis 的执行器 Executor
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
每一条<select|update|delete|insert> 会生成一个对应的MappedStatement对象,然后会以key=“namespace.id” ,value为MappedStatement对象,维护到Configuration的一个Map中。以后需要使用的时候,只需要通过key值来获取就可。
c) 执行器Executor根据SqlSession传递的参数执行query()方法
Executor的作用是:
Executor.query()方法最后会创建一个StatementHandler对象,负责设置Statement 对象中的查询参数、处理JDBC返回的resultSet,将resultSet 加工为List 集合返回。
//SimpleExecutor类的doQuery()方法实现 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 根据既有的参数,创建StatementHandler对象来执行查询操作 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 创建java.Sql.Statement对象,传递给StatementHandler对象 stmt = prepareStatement(handler, ms.getStatementLog()); // 调用StatementHandler.query()方法,返回List结果集 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; //getConnection方法经过重重调用最后会调用openConnection方法,从连接池中获得连接 Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); //对创建的Statement对象设置参数,即设置SQL 语句中 ? 设置为指定的参数 handler.parameterize(stmt); return stmt;
StatementHandler对象主要完成两个工作:
d) StatementHandler的parameterize(statement) 方法的实现:
public void parameterize(Statement statement) throws SQLException {
//使用ParameterHandler对象来完成对Statement的设值
parameterHandler.setParameters((PreparedStatement) statement);
}
ParameterHandler的setParameters(Statement)方法负责 根据我们输入的参数,对statement对象的 ? 占位符处进行赋值。
f) List query(Statement statement, ResultHandler resultHandler)方法的实现:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 1.调用preparedStatemnt.execute()方法,然后将resultSet交给ResultSetHandler处理
PreparedStatement ps = (PreparedStatement) statement;
ps.execute(); //原生JDBC执行
//2. 使用ResultHandler来处理ResultSet
return resultSetHandler.<E> handleResultSets(ps);
}
ResultSetHandler的handleResultSets(Statement) 方法会将Statement语句执行后生成的resultSet 结果集转换成List 结果集。
四、接口方式:动态代理
先介绍一下MyBatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性,它内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。
解析mappers标签时,当解析到接口时,会创建此接口对应的MapperProxyFactory对象,存入HashMap中,key = 接口的字节码对象,value = 此接口对应的MapperProxyFactory对象。
进入sqlSession.getMapper(UserMapper.class)中
//MapperRegistry中的getMapper public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //1. 从MapperRegistry中的HashMap中拿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 { //2. 通过动态代理工厂生成示例。 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } //MapperProxyFactory类中的newInstance方法 public T newInstance(SqlSession sqlSession) { // 3. 创建了JDK动态代理的Handler类 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); // 4. 调用了重载方法 return newInstance(mapperProxy); } //MapperProxy类,实现了InvocationHandler接口 public class MapperProxy<T> implements InvocationHandler, Serializable { //省略部分源码 // 构造,传入了SqlSession,说明每个session中的代理对象的不同的! 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 { try { //判断调用是是不是Object中定义的方法,toString,hashCode这类非。是的话直接放行。 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); // 重点在这:MapperMethod最终调用了执行的方法 return mapperMethod.execute(sqlSession, args); } } //5. 重载的方法,由动态代理创建新示例返回。 protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。