zoukankan      html  css  js  c++  java
  • 深入浅出mybatis

       mybatis是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

    mybatis关键类

     通过源码了解mybatis

     1 @Slf4j
     2 public class MybatisTest {
     3 
     4   //一级缓存
     5   @Test
     6   public void test() throws IOException {
     7 
     8     String resource = "mybatis-config.xml";
     9     InputStream inputStream = Resources.getResourceAsStream(resource);
    10     SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);
    11     SqlSession sqlSession = sqlSessionFactory.openSession();
    12     sqlSession.selectOne("com.jiagouedu.mybatis.UserMapper.selectUser",3);
    13     //log.info("user1:{}", sqlSession.selectOne("com.jiagouedu.mybatis.UserMapper.selectUser", 3));
    14     //log.info("user2:{}", sqlSession.selectOne("com.jiagouedu.mybatis.UserMapper.selectUser", 3));
    15     //sqlSession.commit();
    16    // log.info("user3:{}", sqlSession.selectOne("com.jiagouedu.mybatis.UserMapper.selectUser", 3));
    17    // log.info("user4:{}", sqlSession.selectOne("com.jiagouedu.mybatis.UserMapper.selectUser", 3));
    18   }
    19 }

      我们使用的是常用的xml形式进行配置mybatis相关属性及SQL编写。

    mybatis-config.xml
     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE configuration
     3         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     4         "http://mybatis.org/dtd/mybatis-3-config.dtd">
     5 <configuration>
     6    <!-- <settings>
     7         <setting name="cacheEnabled" value="true" />
     8     </settings>-->
     9     <typeAliases>
    10         <typeAlias type="com.jiagouedu.pojo.User" alias="user"></typeAlias>
    11     </typeAliases>
    12 
    13     <environments default="development">
    14         <environment id="development">
    15             <transactionManager type="JDBC"/>
    16             <dataSource type="POOLED">
    17                 <property name="driver" value="com.mysql.jdbc.Driver"/>
    18                 <property name="url" value="jdbc:mysql://localhost:3306/tl-vip"/>
    19                 <property name="username" value="root"/>
    20                 <property name="password" value="20152974"/>
    21             </dataSource>
    22         </environment>
    23     </environments>
    24 
    25     <mappers>
    26       <mapper resource="mybatis/UserMapper.xml"/>
    27     </mappers>
    28 </configuration>
    View Code
     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE mapper
     3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
     5 <mapper namespace="com.jiagouedu.mybatis.UserMapper">
     6     <!--<cache eviction="LRU" type="com.jiagouedu.cache.MybatisRedisCache"/>-->
     7     <select id="selectUser" parameterType="integer" resultType="user">
     8         select * from user where id = #{id}
     9     </select>
    10 
    11 </mapper>
    UserMapper.xml

    我们先看一下build方法,主要进行构建xml并进行解析parse()方法。

     1 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
     2         SqlSessionFactory var5;
     3         try {
     4             XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
     5             var5 = this.build(parser.parse());
     6         } catch (Exception var14) {
     7             throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
     8         } finally {
     9             ErrorContext.instance().reset();
    10 
    11             try {
    12                 inputStream.close();
    13             } catch (IOException var13) {
    14                 ;
    15             }
    16 
    17         }
    18 
    19         return var5;
    20     }
    我们再来看一下parser.parse()方法
    1 public Configuration parse() {
    2         if (this.parsed) {
    3             throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    4         } else {
    5             this.parsed = true;
    6             this.parseConfiguration(this.parser.evalNode("/configuration"));
    7             return this.configuration;
    8         }
    9     }
    this.parser.evalNode("/configuration")大家应该猜到它是在找xml文件中的configuration节点,如果不确认大家可以进到this.parseConfiguration方法看一下
     1 private void parseConfiguration(XNode root) {
     2         try {
     3             this.propertiesElement(root.evalNode("properties"));
     4             Properties settings = this.settingsAsProperties(root.evalNode("settings"));
     5             this.loadCustomVfs(settings);
     6             this.typeAliasesElement(root.evalNode("typeAliases"));
     7             this.pluginElement(root.evalNode("plugins"));
     8             this.objectFactoryElement(root.evalNode("objectFactory"));
     9             this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
    10             this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
    11             this.settingsElement(settings);
    12             this.environmentsElement(root.evalNode("environments"));
    13             this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    14             this.typeHandlerElement(root.evalNode("typeHandlers"));
    15             this.mapperElement(root.evalNode("mappers"));
    16         } catch (Exception var3) {
    17             throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
    18         }
    19     }
    properties、settings、typeAliases。。。。这些属性大家都知道怎么去配置吧,所以受builder方法主要就是进行xml文件的读取并加载到内存当中。
    解析完第一步的源码后,然后进行我们的第二步sqlSessionFactory.openSession()的源码,看看它做了哪些工作?
     1  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
     2         Transaction tx = null;
     3 
     4         DefaultSqlSession var8;
     5         try {
     6             Environment environment = this.configuration.getEnvironment();
     7             TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
     8             tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
     9             Executor executor = this.configuration.newExecutor(tx, execType);
    10             var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    11         } catch (Exception var12) {
    12             this.closeTransaction(tx);
    13             throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    14         } finally {
    15             ErrorContext.instance().reset();
    16         }
    17 
    18         return var8;
    19     }
    new DefaultSqlSession(this.configuration, executor, autoCommit);这是关键的一步,前面就是取我们xml中的配置并开启数据库事务。最后返回我们的sqlsession。
    我们再看一下第三步,就是我们
    sqlSession.selectOne("com.jiagouedu.mybatis.UserMapper.selectUser",3);取数据的那句,
     1  public <T> T selectOne(String statement, Object parameter) {
     2         List<T> list = this.selectList(statement, parameter);
     3         if (list.size() == 1) {
     4             return list.get(0);
     5         } else if (list.size() > 1) {
     6             throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
     7         } else {
     8             return null;
     9         }
    10     }
     1 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
     2         List var5;
     3         try {
     4             MappedStatement ms = this.configuration.getMappedStatement(statement);
     5             var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
     6         } catch (Exception var9) {
     7             throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
     8         } finally {
     9             ErrorContext.instance().reset();
    10         }
    11 
    12         return var5;
    13     }
    this.configuration.getMappedStatement(statement);主要就是取出我们写的mapper。xml对象,以便后续进行sql拼写等操作。
    this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);这个操作比较多,我们还是直接看源码
    1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    2         BoundSql boundSql = ms.getBoundSql(parameterObject);
    3         CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
    4         return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    5     }

      boundSql 就是下列对象,大家肯定不陌生就是我们自己写的SQL,并且参数它也拿到了。

     this.createCacheKey方法比较厉害,我们还是看源码

     1 public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
     2         if (this.closed) {
     3             throw new ExecutorException("Executor was closed.");
     4         } else {
     5             CacheKey cacheKey = new CacheKey();
     6             cacheKey.update(ms.getId());
     7             cacheKey.update(rowBounds.getOffset());
     8             cacheKey.update(rowBounds.getLimit());
     9             cacheKey.update(boundSql.getSql());
    10             List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    11             TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    12             Iterator var8 = parameterMappings.iterator();
    13 
    14             while(var8.hasNext()) {
    15                 ParameterMapping parameterMapping = (ParameterMapping)var8.next();
    16                 if (parameterMapping.getMode() != ParameterMode.OUT) {
    17                     String propertyName = parameterMapping.getProperty();
    18                     Object value;
    19                     if (boundSql.hasAdditionalParameter(propertyName)) {
    20                         value = boundSql.getAdditionalParameter(propertyName);
    21                     } else if (parameterObject == null) {
    22                         value = null;
    23                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
    24                         value = parameterObject;
    25                     } else {
    26                         MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
    27                         value = metaObject.getValue(propertyName);
    28                     }
    29 
    30                     cacheKey.update(value);
    31                 }
    32             }
    33 
    34             if (this.configuration.getEnvironment() != null) {
    35                 cacheKey.update(this.configuration.getEnvironment().getId());
    36             }
    37 
    38             return cacheKey;
    39         }
    40     }
    cacheKey.update(ms.getId());cacheKey.update(rowBounds.getOffset());cacheKey.update(rowBounds.getLimit());cacheKey.update(boundSql.getSql());
    这就是大家所说的mybatis的一级缓存,用的是id+offset+limit+sql组合的key,然后我们在看
    this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);方法
     1 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
     2         Cache cache = ms.getCache();
     3         if (cache != null) {
     4             this.flushCacheIfRequired(ms);
     5             if (ms.isUseCache() && resultHandler == null) {
     6                 this.ensureNoOutParams(ms, parameterObject, boundSql);
     7                 List<E> list = (List)this.tcm.getObject(cache, key);
     8                 if (list == null) {
     9                     list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    10                     this.tcm.putObject(cache, key, list);
    11                 }
    12 
    13                 return list;
    14             }
    15         }
    16 
    17         return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    18     }

    我们看到,执行sql之前他会先查看缓存中是否有数据,如果有数据将会直接将缓存中的数据返回,如果没有将执行SQL,执行完SQL之后将会再次放入以及缓存当中,我们看一下源码是不是这样做的

     1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
     2         ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
     3         if (this.closed) {
     4             throw new ExecutorException("Executor was closed.");
     5         } else {
     6             if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
     7                 this.clearLocalCache();
     8             }
     9 
    10             List list;
    11             try {
    12                 ++this.queryStack;
    13                 list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
    14                 if (list != null) {
    15                     this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    16                 } else {
    17                     list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    18                 }
    19             } finally {
    20                 --this.queryStack;
    21             }
    22 
    23             if (this.queryStack == 0) {
    24                 Iterator var8 = this.deferredLoads.iterator();
    25 
    26                 while(var8.hasNext()) {
    27                     BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
    28                     deferredLoad.load();
    29                 }
    30 
    31                 this.deferredLoads.clear();
    32                 if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
    33                     this.clearLocalCache();
    34                 }
    35             }
    36 
    37             return list;
    38         }
    39     }
    我们再看看this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
     1 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
     2         this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
     3 
     4         List list;
     5         try {
     6             list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
     7         } finally {
     8             this.localCache.removeObject(key);
     9         }
    10 
    11         this.localCache.putObject(key, list);
    12         if (ms.getStatementType() == StatementType.CALLABLE) {
    13             this.localOutputParameterCache.putObject(key, parameter);
    14         }
    15 
    16         return list;
    17     }

    正如我们想的那样,他就是在查询完之后走的缓存存放,以便下次重新查询的时候提高效率,就不用再次查询数据库,来减少数据库压力。

      ps:关注一下本人公众号,每周都有新更新哦!

     
     
  • 相关阅读:
    了解委托(Delegate)
    C#中事件的一些总结
    Devexpress Xtrareport 并排报表
    Xtrareport 交叉报表
    Xtrareport 多栏报表
    Xtrareport 报表的一些属性及控件
    UI前端开发都是做什么的以及html、css、php、js等究竟是神马关系
    url,href,src之间的区别
    join()的用法
    爬取百度百科
  • 原文地址:https://www.cnblogs.com/guoxiaoyu/p/11502767.html
Copyright © 2011-2022 走看看