springboot 加载mybatis的流程
一、mybatics的配置步骤
1.POM依赖包加载
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency>
2.自定义MYBATICS配置对象。
/*** mybatics的数据工厂配置。*/
@Configuration
@MapperScan(basePackages = "com.tpw.summaryday.dao", sqlSessionFactoryRef = "test2_sqlSessionFactory")
public class MybaticsSessionConf {//配置mybatis的分页插件pageHelper@Beanpublic PageHelper pageHelper() {PageHelper pageHelper = new PageHelper();Properties props = new Properties();props.setProperty("dialect", "mysql");// 表示支持从接口中读取pageNum和pageSizeprops.setProperty("supportMethodsArguments", "true");pageHelper.setProperties(props);return pageHelper;}@Bean(name = "test2_dataSource")@ConfigurationProperties(prefix = "spring.datasource.test2")public DataSource getDateSource1() {return DataSourceBuilder.create().build();}@Bean(name = "test2_sqlSessionFactory")public SqlSessionFactory getSqlSessionFactory(@Qualifier("test2_dataSource") DataSource test2DataSource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(test2DataSource);bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis-config.xml"));
// bean.setMapperLocations(
// // 设置mybatis的xml所在位置
// new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/localdb/*.xml"));return bean.getObject();}@Bean("test2_sqlSessionTemplate")public SqlSessionTemplate getSqlsessiontemplate(@Qualifier("test2_sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {try {return new SqlSessionTemplate(sqlSessionFactory);} catch (Exception e) {e.printStackTrace();}return null;}}
3.环境配置文件加载pageHelper配置
pagehelper.helperDialect = mysql
pagehelper.reasonable= true
pagehelper.supportMethodsArguments= true
pagehelper.params= count=countSql
4.mybatis-generator.xml配置,用来生成DAO,MAPPER,ENTITY
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration><context id="DB2Tables" targetRuntime="MyBatis3"><commentGenerator><property name="suppressDate" value="true"/><property name="suppressAllComments" value="true"/></commentGenerator><!--数据库链接地址账号密码--><jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://rm-wz96s5izji5099uz13o.mysql.rds.aliyuncs.com:3306/test2?"userId="" password=""></jdbcConnection><javaTypeResolver><property name="forceBigDecimals" value="false"/></javaTypeResolver><!--生成Model类存放位置--><javaModelGenerator targetPackage="com.tpw.summaryday.entity" targetProject="src/main/java"><property name="enableSubPackages" value="true"/><property name="trimStrings" value="true"/></javaModelGenerator><!--生成映射文件存放位置--><sqlMapGenerator targetPackage="com.tpw.summaryday.mapper" targetProject="src/main/java"><property name="enableSubPackages" value="true"/></sqlMapGenerator><!--生成Dao类存放位置--><!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口--><javaClientGenerator type="ANNOTATEDMAPPER" targetPackage="com.tpw.summaryday.dao" targetProject="src/main/java"><property name="enableSubPackages" value="true"/></javaClientGenerator><!--生成对应表及类名--><table tableName="fanli_directional_goods" domainObjectName="DirectionalGoods"enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"enableSelectByExample="false" selectByExampleQueryId="false"></table></context>
</generatorConfiguration>
5.mybatis-config.xml配置,开启了二级缓存
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><setting name="logImpl" value="STDOUT_LOGGING"/><!--这个配置使全局的映射器(二级缓存)启用或禁用缓存--><setting name="cacheEnabled" value="true" /></settings>
</configuration>
6.DAO层添加二级缓存配置
@CacheNamespace(implementation = RedisCache.class,flushInterval = 60L)
public interface DirectionalGoodsMapper {@Delete({"delete from fanli_directional_goods","where id = #{id,jdbcType=INTEGER}"})int deleteByPrimaryKey(Integer id);@Select("select g.*,a.img,a.price from fanli_directional_goods g,fanli_akbgoods a where g.goods_id = a.data_id and g.goods_id = #{goodsId} limit 1")DirectGoodsDto selectByGoodsId(String goodsId);@Select("select g.*,a.img,a.price from fanli_directional_goods g,fanli_akbgoods a where g.goods_id = a.data_id and g.goods_id = #{goodsId}")List<DirectGoodsDto> listByGoodsId(String goodsId);@Select("select * from fanli_directional_goods where stime >= #{stime} and etime <= #{etime}")List<DirectionalGoods> pageByStimeAndEtime(String stime, String etime/*, @Param("pageNum")int startIndex, @Param("pageSize")int pageNumIn*/);
}
二、mybatics的DAO层对象加载流程
1.在CONFIG类中加入了@MapperScan(basePackages = "com.tpw.summaryday.dao", sqlSessionFactoryRef = "test2_sqlSessionFactory"),mapperScan对象引入了@Import(MapperScannerRegistrar.class)
org.mybatis.spring.annotation.MapperScannerRegistrarpublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));List<String> basePackages = new ArrayList<String>();for (String pkg : annoAttrs.getStringArray("value")) {if (StringUtils.hasText(pkg)) {basePackages.add(pkg);}}for (String pkg : annoAttrs.getStringArray("basePackages")) {if (StringUtils.hasText(pkg)) {basePackages.add(pkg);}}for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {basePackages.add(ClassUtils.getPackageName(clazz));}scanner.registerFilters();scanner.doScan(StringUtils.toStringArray(basePackages));}
2.然后跳到org.mybatis.spring.mapper.ClassPathMapperScanner.doScan 方法,去搜索DAO目录下所有的MAPPER对象。
public Set<BeanDefinitionHolder> doScan(String... basePackages) {Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");} else {processBeanDefinitions(beanDefinitions);}return beanDefinitions;}
这里生成的BEANDEFINERD中的beanClass就是MapFactoryBean工厂类。
3.初始化数据源,sqlSessionFactory,sqlSessionTemplate对象。
4.开始创建DAO层代理对象。是根据 org.mybatis.spring.mapper.MapperFactoryBean.getObject来通过工厂类BEAN创建对象。由于是工厂BEAN创建对象,首先要创建工厂对象。
这时在进行工厂对象的实例化和初始化操作。
5.上面工厂对象的afterPropertiesSet方法中,接着会调用到org.apache.ibatis.binding.MapperRegistry中的addMapper方法,将mapper映射保存起来。
上面类中有这样一个MAPPER映射哈希表。为一个****Mapper对象一个MAPPER代理工厂对象。
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
MapperRegistry为org.apache.ibatis.session.Configuration下面的一个成员变量。Configuration也为sqlSession下一个成员。
Configuration configuration = getSqlSession().getConfiguration();
public Configuration getConfiguration() {return this.sqlSessionFactory.getConfiguration();
}
从上面可以看出,configuation,MapperRegistry为一个sqlSessionFactory全局共享。
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));loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
6.这个为 org.mybatis.spring.mapper.MapperFactoryBean.getObject方法,就是通过sqlSession去创建一个mapper对象,所以我们在外面也可以通过sqlSession获取任意的mybatics的MAPPER对象。
public T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}
7.这里面的sqlSession其实为sqlSessionTemplate对象,然后会调用MapperRegistry的
getMapper方法来创建对象。
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);}}
最终也就是调用org.apache.ibatis.binding.MapperProxyFactory.newInstance来创建代理对象。
public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}
8.我们可以看到,最终就是生成一个JDK的动态代理对象,真正的代理实现类为MapperProxy
protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}
三、mybatics的DAO层接口调用流程
1.上面说过DAO层接口对象实际上是JDK生成的动态代理对象,dao层的任何接口都会走向代理对象的invoke方法。
org.apache.ibatis.binding.MapperProxy
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {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);return mapperMethod.execute(sqlSession, args);}
2.首先创建mapperMethod对象,传入sqlSession.getConfiguration()对象。
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;}
3.然后会调用到MapperMethod.execute方法,从这里可以看到,都是通过sqlSession的接口转发。
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT: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 if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}return result;}
4.由于我们执行的是返回一个list,所以执行的是executeForMany方法
@Select("select g.*,a.img,a.price from fanli_directional_goods g,fanli_goods a where g.goods_id = a.data_id and g.goods_id = #{goodsId}") List<DirectGoodsDto> listByGoodsId(String goodsId);
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 supportif (!method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {return convertToArray(result);} else {return convertToDeclaredCollection(sqlSession.getConfiguration(), result);}}return result;}
5.这里面的sqlSession为sqlSessionTemplate对象,继而调用此类的selectList方法
public <E> List<E> selectList(String statement, Object parameter) {return this.sqlSessionProxy.<E> selectList(statement, parameter);}
这里的sqlSessionProxy又是一个JDK动态代理对象,实际的实现类为SqlSessionInterceptor this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),new Class[] { SqlSession.class },new SqlSessionInterceptor());
6.接着就会调用SqlSessionInterceptor.invoke方法,进行反射调用。这里面会先获取一个sqlSession(同一个事务就会共用,否则每次都会创建一个新的)
private class SqlSessionInterceptor implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,SqlSessionTemplate.this.executorType,SqlSessionTemplate.this.exceptionTranslator);try {Object result = method.invoke(sqlSession, args);if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {// force commit even on non-dirty sessions because some databases require// a commit/rollback before calling close()sqlSession.commit(true);}return result;} catch (Throwable t) {} finally {if (sqlSession != null) {closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);}}}}
7.创建session为org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource,注意这里会先根据mybatics的config类以及数据源开启一个JDBC的事务,并且生成一个带缓存的执行器,然后会再生成一个SQL会话,传入执行器,配置类,默认自动提交为FALSE。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
8.接着反射调用到org.apache.ibatis.session.defaults.DefaultSqlSession.selectList
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}
9.这里面的executor为带缓存的执行器,然后由于我们加入了分页插件,所以加入了
com.github.pagehelper.pageInterceptor,
public Object intercept(Invocation invocation) throws Throwable {Object var16;try {Object[] args = invocation.getArgs();MappedStatement ms = (MappedStatement)args[0];Object parameter = args[1];RowBounds rowBounds = (RowBounds)args[2];ResultHandler resultHandler = (ResultHandler)args[3];Executor executor = (Executor)invocation.getTarget();CacheKey cacheKey;BoundSql boundSql;this.checkDialectExists();List resultList;if (!this.dialect.skip(ms, parameter, rowBounds)) {resultList = ExecutorUtil.pageQuery(this.dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);} else {resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);}var16 = this.dialect.afterPage(resultList, parameter, rowBounds);} finally {this.dialect.afterAll();}return var16;}
10.由于我们是普通查询,没有分页,所以执行executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
执行到org.apache.ibatis.executor.BaseExecutor.query方法
这里的cache只是在同一个sqlSession中有效,由于spring的特性,每次都会开启一个新的会话,所以一级缓存无效。这里查询的是数据库。
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}return list;}
11.然后会执行到org.apache.ibatis.executor.SimpleExecutor.doQuery,这里最终会去创建数据源连接,然后生成一个预处理的语句,然后进行JDBC的数据源查询 。
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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);stmt = prepareStatement(handler, ms.getStatementLog());return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}
12.执行完查询结果后,就会返回到SqlSessionInterceptor.invoke方法,最终发现本方法没有开启事务,进行自动提交,返回结果,然后关闭会话。
13.至此查询流程完成。
四、mybatics的一级查询缓存在SPRING中无效,上面已经讲过,二级缓存可以开启,在同 一个命名空间中生效。
二级缓存,实现Cache接口,然后底层实现可以使用REDIS等各种分布式缓存。
springboot 加载mybatis的流程相关推荐
- springboot集成mybatis源码分析-启动加载mybatis过程(二)
springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...
- HTML页面加载和解析流程详细介绍
浏览器加载和渲染html的顺序.如何加快HTML页面加载速度.HTML页面加载和解析流程等等,在本文将为大家详细介绍下,感兴趣的朋友不要错过 浏览器加载和渲染html的顺序 1. IE下载的顺序是从上 ...
- springboot 加载配置信息(静态)
springboot 加载配置信息(静态) 举例:动态设置验证码过期时间 application-uat.properties配置(文件路径:/src/main/resources/applicati ...
- linux优化网页加载过程,HTML页面加载和解析流程 介绍
1.浏览器加载和渲染html的顺序 1.1.IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的. 1.2.在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相 ...
- html中加载解析,HTML页面加载和解析流程详细介绍
序言: 我一直都认为"网页制作"这个词是一个不怎么高端的词,在我的印象中网页制作的词是没有生命力的一个制作,我喜欢用HTML 这样简单直接,这词凸显高端,有大气漂亮的UI.一套完美 ...
- android主动显示流程,Activity加载显示基本流程
本文章是基于Android源码6.0讲解Activity加载显示基本流程 首先上一张图给大家一个直观的了解 首先一个布局页面的加载是在Activity中的setContentView(R.layout ...
- springboot 加载一个properties文件转换为对象、List和Map数据结构
springboot 加载一个properties文件转换为对象.List和Map数据结构 一.springboot 加载一个properties文件转换为对象 1.1.配置文件-blog.prope ...
- 【安卓开机启动】安卓JVM加载so库流程
安卓JVM加载so库流程 好久没有写点东西发了,工作中的事情有点杂,也找不到整块东西可以写的. 最近调查了一个问题,稍微追了一下流程,这里记录一下. 1. 问题背景 由于我们支持的设备相对比竞品,zy ...
- SpringBoot加载spring.factories的价值
SpringBoot加载spring.factories的价值 在springboot的各个依赖包下,我们经常看到META-INF/spring.factories这个文件.spring.factor ...
最新文章
- linux shell 文件 第一行插入字符串
- WPS文本域替换不完全的问题
- 项目: 图片放大缩小。
- Python 把函数视作对象
- WinDbg学习笔记(一)--认识WinDbg
- 计算机表格收入水平怎么算,怎么用excel计算工资所得税
- virtualbox android分辨率,VirtualBox 修改Android x86虚拟机的分辨率
- Android SQLite (二) 基本用法
- Sql Server参数化查询之where in和like实现之xml和DataTable传参
- 矩阵分析 第二章 lambda矩阵和Jordan标准型
- c++ 获取系统时间_错过长假,我还有时间做近视手术么?
- [2018.10.25 T3] 旅程
- OLED原理,时序和操作
- 用泰勒展开式计算sin(x)的值
- Caused by: java.lang.UnsatisfiedLinkError: Library hello-jni not found“问题解决
- SSLOJ——P1738.水洼的大小
- QT下载和安装 指南教程
- python小程序模板——阿龙的小百宝箱
- 综合应用 -- 购物车
- silhouette_matlab
热门文章
- 关系型数据库 (数据库类型)
- 10个凭证类型的速记
- smartforms句柄与以簇的方式存储数据。
- 做宠物行业“独角兽”,乖宝集团如何为宠物舌尖上的安全保驾护航?
- ado filter 多条记录_江苏气动断料锯商家,多条锯_邢台富宇来机械厂
- python穷举法搬砖_python 穷举法 算24点(史上最简短代码)-阿里云开发者社区
- linux apt qt下载,Linux如何安装 apt-get 软件管理工具
- android 引用jar的r文件,正确的方法来处理Android库的ant构建. Build从jar文件中排除R.class...
- java获取目录中最后被更改的文件_如何使用Java从目录中只获取10个最后修改过的文件?...
- 从0搭建一个Springboot+vue前后端分离项目(六)后台编写配置类与接口