一、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的流程相关推荐

  1. springboot集成mybatis源码分析-启动加载mybatis过程(二)

    springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...

  2. HTML页面加载和解析流程详细介绍

    浏览器加载和渲染html的顺序.如何加快HTML页面加载速度.HTML页面加载和解析流程等等,在本文将为大家详细介绍下,感兴趣的朋友不要错过 浏览器加载和渲染html的顺序 1. IE下载的顺序是从上 ...

  3. springboot 加载配置信息(静态)

    springboot 加载配置信息(静态) 举例:动态设置验证码过期时间 application-uat.properties配置(文件路径:/src/main/resources/applicati ...

  4. linux优化网页加载过程,HTML页面加载和解析流程 介绍

    1.浏览器加载和渲染html的顺序 1.1.IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的. 1.2.在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相 ...

  5. html中加载解析,HTML页面加载和解析流程详细介绍

    序言: 我一直都认为"网页制作"这个词是一个不怎么高端的词,在我的印象中网页制作的词是没有生命力的一个制作,我喜欢用HTML 这样简单直接,这词凸显高端,有大气漂亮的UI.一套完美 ...

  6. android主动显示流程,Activity加载显示基本流程

    本文章是基于Android源码6.0讲解Activity加载显示基本流程 首先上一张图给大家一个直观的了解 首先一个布局页面的加载是在Activity中的setContentView(R.layout ...

  7. springboot 加载一个properties文件转换为对象、List和Map数据结构

    springboot 加载一个properties文件转换为对象.List和Map数据结构 一.springboot 加载一个properties文件转换为对象 1.1.配置文件-blog.prope ...

  8. 【安卓开机启动】安卓JVM加载so库流程

    安卓JVM加载so库流程 好久没有写点东西发了,工作中的事情有点杂,也找不到整块东西可以写的. 最近调查了一个问题,稍微追了一下流程,这里记录一下. 1. 问题背景 由于我们支持的设备相对比竞品,zy ...

  9. SpringBoot加载spring.factories的价值

    SpringBoot加载spring.factories的价值 在springboot的各个依赖包下,我们经常看到META-INF/spring.factories这个文件.spring.factor ...

最新文章

  1. linux shell 文件 第一行插入字符串
  2. WPS文本域替换不完全的问题
  3. 项目: 图片放大缩小。
  4. Python 把函数视作对象
  5. WinDbg学习笔记(一)--认识WinDbg
  6. 计算机表格收入水平怎么算,怎么用excel计算工资所得税
  7. virtualbox android分辨率,VirtualBox 修改Android x86虚拟机的分辨率
  8. Android SQLite (二) 基本用法
  9. Sql Server参数化查询之where in和like实现之xml和DataTable传参
  10. 矩阵分析 第二章 lambda矩阵和Jordan标准型
  11. c++ 获取系统时间_错过长假,我还有时间做近视手术么?
  12. [2018.10.25 T3] 旅程
  13. OLED原理,时序和操作
  14. 用泰勒展开式计算sin(x)的值
  15. Caused by: java.lang.UnsatisfiedLinkError: Library hello-jni not found“问题解决
  16. SSLOJ——P1738.水洼的大小
  17. QT下载和安装 指南教程
  18. python小程序模板——阿龙的小百宝箱
  19. 综合应用 -- 购物车
  20. silhouette_matlab

热门文章

  1. 关系型数据库 (数据库类型)
  2. 10个凭证类型的速记
  3. smartforms句柄与以簇的方式存储数据。
  4. 做宠物行业“独角兽”,乖宝集团如何为宠物舌尖上的安全保驾护航?
  5. ado filter 多条记录_江苏气动断料锯商家,多条锯_邢台富宇来机械厂
  6. python穷举法搬砖_python 穷举法 算24点(史上最简短代码)-阿里云开发者社区
  7. linux apt qt下载,Linux如何安装 apt-get 软件管理工具
  8. android 引用jar的r文件,正确的方法来处理Android库的ant构建. Build从jar文件中排除R.class...
  9. java获取目录中最后被更改的文件_如何使用Java从目录中只获取10个最后修改过的文件?...
  10. 从0搭建一个Springboot+vue前后端分离项目(六)后台编写配置类与接口