深入浅出mybatis

2024-06-20 11:44
文章标签 mybatis 深入浅出

本文主要是介绍深入浅出mybatis,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

mybatis关键类

 通过源码了解mybatis

 1 @Slf4j2 public class MybatisTest {3 4   //一级缓存5   @Test6   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

View Code

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:关注一下本人公众号,每周都有新更新哦!

 

                                    

这篇关于深入浅出mybatis的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1078035

相关文章

持久层 技术选型如何决策?JPA,Hibernate,ibatis(mybatis)

转自:http://t.51jdy.cn/thread-259-1-1.html 持久层 是一个项目 后台 最重要的部分。他直接 决定了 数据读写的性能,业务编写的复杂度,数据结构(对象结构)等问题。 因此 架构师在考虑 使用那个持久层框架的时候 要考虑清楚。 选择的 标准: 1,项目的场景。 2,团队的技能掌握情况。 3,开发周期(开发效率)。 传统的 业务系统,通常业

MyBatis-Plus常用注解详解与实战应用

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。它提供了大量的常用注解,使得开发者能够更方便地进行数据库操作。 MyBatis-Plus 提供的注解可以帮我们解决一些数据库与实体之间相互映射的问题。 @TableName @TableName 用来指定表名 在使用 MyBatis-Plus 实现基本的 C

MyBatis系列之分页插件及问题

概述 无论是C端产品页面,还是后台系统页面,不可能一次性将全部数据加载出来。后台系统一般都是PC端登录,用Table组件(如Ant Design Table)渲染展示数据,可点击列表的下一页(或指定某一页)查看数据。C端产品如App,在下滑时可查看更多数据,看起来像是一次性加载数据,实际上也是分批请求后台系统获取数据。而这,就是分页功能。 如果没有使用Hibernate或MyBatis这样的O

【MyBatis学习8】MyBatis中的二级缓存

1. 二级缓存的原理   前面介绍了,mybatis中的二级缓存是mapper级别的缓存,值得注意的是,不同的mapper都有一个二级缓存,也就是说,不同的mapper之间的二级缓存是互不影响的。为了更加清楚的描述二级缓存,先来看一个示意图:      从图中可以看出: sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到该UserMapper的二级缓存中

【MyBatis学习7】MyBatis中的一级缓存

缓存的作用是减轻数据库的压力,提高数据库的性能的。mybatis中提供了一级缓存和二级缓存,先来看一下两个缓存的示意图:    从图中可以看出: 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。二级缓存是mappe

springboot+vue+mybatis旅游管理+PPT+论文+讲解+售后

随着人民生活水平的提高,旅游业已经越来越大众化,而旅游业的核心是信息,不论是对旅游管理部门、对旅游企业,或是对旅游者而言,有效的获取旅游信息,都显得特别重要.旅游管理系统将使旅游相关信息管理工作规范化、信息化、程序化,提供旅游景点、旅游线路,旅游新闻等服务本文以jsp为开发技术,实现了一个旅游网站系统。旅游网站系统的主要使用者分为管理员和用户,管理员权限如下;主页、个人中心、景点分类管理、景点信息

spring(一)--spring/springmvc/spring+hibernate(mybatis)配置文件

这篇文章用来总结一下spring,springmvc,spring+mybatis,spring+hibernate的配置文件 1.web.xml 要使用spring,必须在web.xml中定义分发器等信息,基本的配置信息如下: <?xml version="1.0" encoding= "UTF-8"?><web-app version= "3.0"xmlns="http://java.

Mybatis-映射文件中select标签resultType属性的使用

数据库的最最基本操作“增删改查”,“查”是最复杂的,有各种各样的查询,所以对应到Mybatis中的select标签也是这四个操作中最复杂的 resultType属性的使用 1.返回的结果是List集合的类型 select标签里的resultType类型设置为List集合里的元素类型 2.返回一个Map集合 key是列名称,value是列对应的值 3.返回的查询结果也是Map集合

三、MyBatis实践:提高持久层数据处理效率

三、MyBatis实践:提高持久层数据处理效率 目录 一、Mybatis简介 1.1 简介1.2 持久层框架对比1.3 快速入门(基于Mybatis3方式) 二、MyBatis基本使用 2.1 向SQL语句传参 2.1.1 mybatis日志输出配置2.1.2 #{}形式2.1.3 ${}形式 2.2 数据输入 2.2.1 Mybatis总体机制概括2.2.2 概念说明2.2.3 单个简单类型

使用MyBatis Generator自动代码生成器简化Java持久层开发

在Web开发中,数据访问层(DAO层)的编码工作往往重复且繁琐,尤其是在处理数据库表与Java对象之间的映射时。MyBatis Generator是一款强大的代码生成工具,它能自动生成DAO接口、Mapper XML文件和实体类,极大地提升了开发效率。本文将详细介绍如何在Maven项目中集成MyBatis Generator,并通过一个示例演示其配置过程。 一、POM.xml中添加MyBatis