从以下一段代码开始,去看mybatis的工作流程及原理
从执行开始
public static void main(String[] args) {
String resource="mybatis-config.xml";
InputStream inputStream=null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory=null;
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession=null;
try {
sqlSession=sqlSessionFactory.openSession();
RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
Role role=roleMapper.getRole(1L);
System.out.println(role.getId()+":"+role.getRoleName()+":"+role.getNote());
sqlSession.commit();
} catch (Exception e) {
// TODO Auto-generated catch block
sqlSession.rollback();
e.printStackTrace();
}finally {
sqlSession.close();
}
}
获取sqlSessionFactory
读取mybatis-config.xml
这段配置文件是对mybatis进行配置,跟configuration有重要关联
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<!-- <setting name="aggressiveLazyLoading" value="false"/> -->
</settings>
<typeAliases>
<typeAlias alias="role" type="org.apache.ibatis.test.Role"/>
</typeAliases>
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="string" handler="org.apache.ibatis.test.MyStringHandler"/>
</typeHandlers>
<!-- 定义数据库的信息,默认使用development数据库构建环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 定义映射器 -->
<mappers>
<package name="org.apache.ibatis.test.mapper"/>
<!-- <mapper resource="org.apache.ibatis.test.mapper.RoleMapper.xml"/>-->
</mappers>
</configuration>
获取到mybatis-config.xml文件,转为了input流,重点看
sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
这句的build
,进去可以看到是public SqlSessionFactory build(InputStream inputStream)
这个方法,实际上这里是运用建造模式,所有的以inputStream进去都会转化到这个方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
再看
return build(parser.parse());
这里有两个重要地方,一是返回返回了一个默认的DefaultSqlSessionFactory
,以及这个方法.parse(),这里是创建Configuration非常重要的地方,里面有个
parser.evalNode("/configuration")
是读取节点/configuration
的内容,对应的是上面mybais-config.xml里的对应<configuration>
包裹内容
重点看mapperElement(root.evalNode("mappers"));这个方法
其中if ("package".equals(child.getName()))
对应mybatis-config.xml
里面的package标签里的信息内容
else
走的则是读取mapper内容
二者逻辑其实都差不多,最后都是扫描sql对应的xml文件并且解析
这里我们走
configuration.addMappers(mapperPackage);
然后走到
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
获取了所有的mapper接口的calss,然后for遍历解析xml
重点看这个方法
里面的loadXmlResource()
;
xmlParser.parse();
在解析mapper接口对应的xml文件,也就是实际sql,这里在解析节点内容,和xml里的节点对应
重点看里面的解析statement对象并加入Configuration里
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
再进入
buildStatementFromContext(list, null);
简单来说,这里是把一个sql解析成一个statementParser,并加入configuration
至此sqlSessionFactory构建完毕
获取SqlSession对象
从sqlSessionFactory的构建里有说,其实构建的是一个默认的sqlSessionFactory(DefaultSqlSessionFactory),所以直接看对应的
可以看到对应的,事物构建、执行器、然后返回默认的sqlSession(DefaultSqlSession)
获取mapper对象
从获取Sqlsession可以知道,返回的是默认的sqlSession,所以直接看里面的getMapper
方法
获取MapperProxyFactory,这个在addMapper的时候有添加,然后看
return mapperProxyFactory.newInstance(sqlSession);
里面运用了动态代理
学过动态代理的应该知道,被代理对象执行方法会执行动态代理时的InvocationHandler的对应invoke,所以主要看MapperProxy.invoke
cachedMapperMethod是根据MappedStatement解析sql的名称及类型(枚举类型SqlCommandType),MethodSignature获取方法返回类型的相关信息(返回值是否多个、返回值是不是map、返回值是否 void等)
最后是return mapperMethod.execute(sqlSession, args);根据之前解析的SqlCommand去对应处理并返回处理结果