博客
关于我
深入浅出mybatis
阅读量:416 次
发布时间:2019-03-06

本文共 13030 字,大约阅读时间需要 43 分钟。

   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 
2 5
6
9
10
11
12 13
14
15
16
17
18
19
20
21
22
23
24 25
26
27
28
View Code
1 
2 5
6
7
10 11
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 selectOne(String statement, Object parameter) { 2 List
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 
List
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 
List
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
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 
List
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
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 
List
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 
List
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:关注一下本人公众号,每周都有新更新哦!

 
 

转载地址:http://nmvuz.baihongyu.com/

你可能感兴趣的文章
2021-04-05阅读小笔记:局部性原理
查看>>
go语言简单介绍,增强了解
查看>>
架构师入门:搭建基本的Eureka架构(从项目里抽取)
查看>>
MongoDB 快速扫盲贴
查看>>
one + two = 3
查看>>
sctf_2019_easy_heap
查看>>
PyQt5之音乐播放器
查看>>
Redis进阶实践之十八 使用管道模式提高Redis查询的速度
查看>>
SQL注入
查看>>
MPI Maelstrom POJ - 1502 ⭐⭐ 【Dijkstra裸题】
查看>>
Problem 330A - Cakeminator (思维)
查看>>
LeetCode75 颜色分类 (三路快排C++实现与应用)
查看>>
调试vs2019代码的流程
查看>>
bcolz的新操作
查看>>
delete对象时会自动调用类的析构函数
查看>>
POD类型
查看>>
const与常量,傻傻分不清楚~
查看>>
Head First设计模式——迭代器模式
查看>>
MongoDB版本及存储引擎区别
查看>>
cmp命令
查看>>