初始化项目
- 执行命令从github上面拉取代码:git clone git@github.com:dengjili/mybatis-3-mybatis-3.4.6.git
代码结构
- 执行mysql数据库脚本:mybatis-3-mybatis-3.4.6/src/test/java/priv/mybatis/example01/sql/role.sql
- 更改mysql数据库配置:/mybatis-3-mybatis-3.4.6/src/test/resources/priv/mybatis/example01/mybatis-config.xml
<property name="url" value="jdbc:mysql://127.0.0.1/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
- 测试类:mybatis-3-mybatis-3.4.6/src/test/java/priv/mybatis/example01/MysqlTest.java
public class MysqlTest {
private static final Logger log = LoggerFactory.getLogger(MysqlTest.class);;
public static void main(String[] args) throws IOException {
String resource = "priv/mybatis/example01/mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = builder.build(reader);
log.info("create sqlSessionFactory : {}.", sqlSessionFactory);
SqlSession openSession = sqlSessionFactory.openSession();
RoleMapper mapper = openSession.getMapper(RoleMapper.class);
Role role = mapper.selectRole(1);
log.info("print role: {}.", role);
}
}
测试结果
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
INFO [main] - create sqlSessionFactory : org.apache.ibatis.session.defaults.DefaultSqlSessionFactory@768debd.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 2137589296.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7f690630]
DEBUG [main] - ==> Preparing: select * from Role where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
INFO [main] - print role: Role [id=1, name=name, desc=test].
分析MysqlTest.java执行逻辑,主要分为两部分,第一部分为加载配置文件mybatis-config.xml,创建出SqlSessionFactory对象。第二部分是通过SqlSession获取到RoleMapper对象,再通过RoleMapper对象执行方法mapper.selectRole(1)
,从数据库中数据库中查询表Role数据
接下来将着重分析一下从数据库中读取数据过程
RoleMapper mapper = openSession.getMapper(RoleMapper.class);
Role role = mapper.selectRole(1);
从数据库中读取数据过程分析
分析代码
RoleMapper mapper = openSession.getMapper(RoleMapper.class);
获取接口RoleMapper 流程分析
- RoleMapper 获取接口的实例依赖于SqlSession接口
- 图中①作用是通过Configuration转换入参
RoleMapper.class
为对应的的MapperProxyFactory
对象,这个步骤依赖于配置文件的解析,这里我们可以先忽略,配置文件章节以后再讨论 - 图中②作用是为接口
RoleMapper
创建一个子类,子类MapperProxy
为一个代理类,创建MapperProxy
对象如下。
MapperProxyFactory创建MapperProxy关键代码
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
MapperProxy
对象实现接口InvocationHandler
,采用JDK的动态代码。即RoleMapper调用子类方法,实际是执行代理类MapperProxy
的invoke
方法
总结这句代码,简化为以下两点
RoleMapper mapper = openSession.getMapper(RoleMapper.class);
- 获取子类对象:
RoleMapper mapper = new MapperProxy()
- 调用子类方法:
RoleMapper.selectRole = MapperProxy.invoke
分析代码
Role role = mapper.selectRole(1);
执行方法mapper.selectRole(1)流程分析
RoleMapper调用子类方法selectRole,实际是执行代理类MapperProxy
的invoke
方法。查看一些核心代码
MapperProxy
# invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
MapperMethod
# execute
public Object execute(SqlSession sqlSession, Object[] args) {
Object param = method.convertArgsToSqlCommandParam(args);
return result = sqlSession.selectOne(command.getName(), param);
}
代理最终执行的SqlSession
接口的方法,那么接下来我们将梳理,SqlSession
是如何访问数据库的
图中分为①②两部分,第①部分是我们访问数据库的Mybatis核心组件,第②部分是与XML配置相关,第一部分执行访问数据库,实际执行的是XML配置SQL。
Executor
MyBatis执行器,执行SQL功能的汇总StatementHandler
执行SQL语句的对象封装,可以理解为执行一次SQL,并返回执行SQL后的结果ParameterHandler
帮助StatementHandler
封装SQL,主要负责对用户传递的参数转换成SQL所需要的参数ResultSetHandler
帮助StatementHandler
返回执行SQL后的结果,主要是对JDBC ResultSet结果集转换TypeHandler
参数映射的转换,ParameterHandler
与ResultSetHandler
的实际工作的类PreparedStatement
JDBC用于执行静态 SQL 语句并返回它所生成结果的对象
JDBC实现与Mybatis组件的映射关系如下
综合上述,我们大概了解了MyBatis从数据库中读取数据过程,也对MyBatis工作的原理有了初步的认识,后续文章,我们将对MyBatis的组件各自单独描述。
拓展
SqlSession openSession = sqlSessionFactory.openSession();
RoleMapper mapper = openSession.getMapper(RoleMapper.class);
Role role = mapper.selectRole(1);
从上述分析我们可以知道,mapper.selectRole(1)
最终也是执行SqlSession 接口方法,那我们是否可以不获取RoleMapper ,直接通过SqlSession 从数据库中读取数据,答案是可以的,代码如下
SqlSession openSession = sqlSessionFactory.openSession();
Role role = openSession.selectOne("priv.mybatis.example01.dao.RoleMapper.selectRole", 1);
那我们思考一下,这样做有哪些缺点?或者说第一种方式有什么好处?
- ?
- ?
- ?
- …