记录Mybatis学习过程(完结,未整理版)

1.报错:Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:59) at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:83)
解决办法:是时区的错误,因此需要设置为你当前系统时区即可

"jdbc:mysql://localhost:3306/eesy_mybatis"?serverTimezone=GMT%2B8

Mybatis中用到的设计模式
(1)构建者模式,(2)工厂模式
记录Mybatis学习过程(完结,未整理版)_第1张图片
还用到了反射机制
记录Mybatis学习过程(完结,未整理版)_第2张图片
手动创建:
1.创建代理对象
2.实现查询所有
3.事务回滚之后,自增的id仍然会自增

报错:Caused by: java.lang.NullPointerException at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(DefaultSqlSessionFactory.java:95) ... 32 more

解决:

<!--配置环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">

报错:

Caused by: java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mysql.jc.jdbc.Driver
	at org.apache.ibatis.datasource.unpooled.UnpooledDataSource.initializeDriver(UnpooledDataSource.java:241)

解决:

 <property name="driver" value="com.mysql.jdbc.Driver"></property>

问题:
记录Mybatis学习过程(完结,未整理版)_第3张图片
提交事务后:
记录Mybatis学习过程(完结,未整理版)_第4张图片
OGNL表达式:
他是通过对象的取值方法来获取数据,在写法上把get给省略了。
比如:我们获取用户的名称
类中的写法:user.getUsername()
OGNL表达式写法:user.username
mybatis中为什么能直接写username,而不用user呢
因为在parameterType中已经提供了属性所属的类,所以此时不用写对象名。

传递poji包装对象
需求:根据用户名查询用户信息,查询条件放到QueryVO的user属性中。
把实体类又包装了一层。。

属性不一致问题:
Mysql数据库会把实体类中的属性userName和数据库表中的列明username为同一个!所以在设置值的时候可以封装进去。但是在Linux操作系统中是严格区分大小写的!

如果遇到数据库和实体类中的字段名不一致的现象解决办法
(1)更改sql语句给字段起别名as uid等
(2)Mybatis自己解决利用配置文件进行配置再中进行配置主 键和个字段名称property实体类属性名, column数据库字段名。
记录Mybatis学习过程(完结,未整理版)_第5张图片
然后更改resultType属性换成resultMap属性,如下:
记录Mybatis学习过程(完结,未整理版)_第6张图片
在这里插入图片描述

Dao实现CRUD:
Dao实现类执行过程分析
(1)findAll查询所有方法session.selectList()底层是怎样调用的。。
SqlSession -->defaultSqlSession–>seleList()方法执行–>executor.query(,)–>CachingExecutor.class–>query(,)方法–>else return delegate.query()方法在这里插入图片描述
然后–>SimpleExecutor.class–>类中的只有doQuery()方法–>SimpleE的父类BaseExecutor–>BaseExecutor类中的query方法–>这个方法中调用了else this.queryFromDatabase()方法–>点进去(执行了)找到了doQuery方法–>再点进去发现abstract List doQuery是抽象方法。
所以这个抽象方法被SimpleExecutor给实现了,所以最终执行的是SimpleExecutor类中的doQuery()方法。。
SimpleExecutor.doQuery()方法–>handler–>在这里插入图片描述StatementHandler—>public interface StatementHandler–>打开图标,找到打开RountingStatementHandler–>找到query方法 -->返回了delegate.query()方法在这里插入图片描述
然后找到StatementHandler接口的实现类PreparedStatementHandler–>中找到PreparedStatement是一个代理对象,对象就是我们想要的JDBC中的预处理对象。其中的execute方法既能执行查询也能进行增删改。return this.resultSetHandler.handleResultSets(ps);对结果集进行封装。–>resultSetHandler点进去是一个抽象类–>点击其中的ResultSetHandler属性,–>ResultSetHandler是一个接口–>然后去找他的diagram实现类–>就一个DefaultResultSetHandler–>上面它调了handleResultSets(ps)方法,在其中找到这个方法它调用的handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);这个方法不停的取值和赋值。
(2)分析插入删除更新方法
在实现类中直接进入insert()方法SqlSession是一个接口,直接diagrams找到它的实现类–>defaultSqlSession–>找到他的insert()方法,它最终调用的是update()方法,如下
记录Mybatis学习过程(完结,未整理版)_第7张图片
再看delect方法,最终也是执行了update()方法
在这里插入图片描述
所以说三个操作最终都会执行update()方法–>然后执行里面的excetor()方法,去调用它的update()方法
记录Mybatis学习过程(完结,未整理版)_第8张图片
接下来–>找到excutor这个接口–>但后在diagrams找到它的实现类CachingExcutor这个类–>找到其中的update()方法,如下:
在这里插入图片描述
它又调用了delegate的update()方法,然后再去找executor的实现类BaseExecutor—>中的update()方法,如下:
记录Mybatis学习过程(完结,未整理版)_第9张图片
它又调用的doUpdate()方法,点进去这个方法在里面是一个抽象的方法,–>所以要找到BaseExecutor的子类SimpleExecutor–>再找到其中的doUpdate()方法,如下:
记录Mybatis学习过程(完结,未整理版)_第10张图片
它又调用了handler的update()方法,找到这个对象所属的类StatementHandler是一个接口–>diagrams找到他的实现类RountingStatementHandler–>找到其中的update()方法
在这里插入图片描述
它又调用了delegate的update()方法,debug调试查看到它的PreparedStatementHanldler类的实例,—>继续找到StanementHandler接口的是实现类PreparedStatementHanldler类中的update()方法,如下
记录Mybatis学习过程(完结,未整理版)_第11张图片
PreparedStatement对象和execute()都已经出来了,我们传统的JDBC操作出来了。
三个方法无论是谁走到最后都是走到preparedStatement。

代理Dao执行过程分析
记录Mybatis学习过程(完结,未整理版)_第12张图片
在这里插入图片描述
创建代理对象getMapper()方法–>点进去SqlSession是一个接口diagram找到它的实现类defaultSqlSession–>找到getMapper()方法在这里插入图片描述
后–>他又调用了configuration的.getMapper()方法–>点击configuration就直接进入了Configuration.class这个类—>找到getMapper方法在这里插入图片描述
进入mapperRegistryMapperRegistry.class这个类–>找到getMapper()方法记录Mybatis学习过程(完结,未整理版)_第13张图片
然后再进入其newInstance(sqlSession)方法
在这里插入图片描述
其中的newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy)newProxyInstance(类加载器,实现类接口,如何代理)就是动态代理–>点击里面的mapperProxy也就是MapperProxy mapperP类,–>发现这个类实现了InvocationHandler, Serializabl这个接口,有invoke方法
应该是有一个返回execute方法,但是我没找到。。
我找到了一个他的内部类private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker里面的invoke()方法,返回 return this.mapperMethod.execute(sqlSession, args);–>点击execute()方法,进入MapperMethod.class类中找到execute()方法,在这个方法中找到executeForMany点进去就会看到最终要执行的selectList()方法,到此结束记录Mybatis学习过程(完结,未整理版)_第14张图片

properties标签的使用及细节:

<properties>
        <property name="password" value="123456"></property>
    </properties>
    <dataSource type="POOLED">
                <!--去引用上面的property-->
          <property name="password" value="${password}"></property>
            </dataSource>

可以把他放在一个外部文件里面

typeAliases标签和package
(1)typeAliases

<typeAliases>
        <!--typeAlias用于配置别名,type属性指定的是实体类的权限定类名,而alias属性指定别名,当指定了别名,就不区分大小写-->
        <!--<typeAlias type="com.selan.domain.User" alias="user"></typeAlias>-->

        <!--用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
        <package name="com.selan.domain"></package>

    </typeAliases>

记录Mybatis学习过程(完结,未整理版)_第15张图片
配置完之后可以把IUserDao中的parameterType="com.selan.domain.User改为如下
起的别名,不区分大小写。

<update id="updateUser" parameterType="user">
        UPDATE USER SET username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id}
    </update>

(2)package
也是在typeAliases标签中写

 <!--用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且**类 名 就 是 别 名**,不再区分大小写-->
        <package name="com.selan.domain"></package>

在mappers标签中配置

<mappers>
        <!--<mapper resource="com/selan/dao/IUserDao.xml"></mapper>-->

        <!--package用于指定dao接口所在的包,当指定完成之后,就不需要再写resource,mapper,class了-->
        <package name="com.selan.dao"></package>

    </mappers>

第三天课程笔记

1.mybatis中的连接池以及事务控制: 原理部分了解,应用部分会用
mybatis中的连接池使用及分析
mybatis事务控制分析
2.mybatis基于XML配置的动态SQL语句使用
mappers配置文件中的几个标签

3.mybatis中的多表操作
一对多
一对一
多对多
=========================================
1.连接池:
用处很广泛
因为它可以减少我们获取连接所消耗的时间 。
所谓连接池其实就是一个存储连接的容器,容器就是一个集合对象。该集合必须是线程安全的,不能两个线程拿到同一连接。该集合还必须实现队列的特性:先进先出。

2.mybatis中的连接池
mybatis连接池提供了3中方式的配置:
配置的位置:
主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池的方式。

<dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"></property>

type属性的取值

  • POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis只针对规范的实现
  • UNPOOLED 采用传统的获取连接方式,虽然也实现了Javax.sql.DataSource接口,但并没有使用池的思想。
  • JNDI 采用服务器提供的JDNDI技术实现,来获取dataSource对象,不同服务器所拿到的DateSource是不一样的,注意:如果不是web或者war工程是不能使用的。我们课程中使用的是tomcat服务器,采用的连接池就是dbcp连接池。
    POOLED
    他是从池中获取一个连接来用
    记录Mybatis学习过程(完结,未整理版)_第16张图片

UNPOOLED
每次创建一个新的连接,最后closeing掉
记录Mybatis学习过程(完结,未整理版)_第17张图片
UNPOOLED原理分析:
ctrl+shift+t查找类UnpooledDataSource和PooledDataSource这两个类都实现了DataSource。
在UnpooledDataSource类中找到getConnection()方法–>z在方法中它调用了doGetConnection(username, password);
点进去 -->方法中设置了用户名和密码,又再次调用了doGetConnectionreturn this.doGetConnection(props);–>继续点进去看到如下
注册驱动,获取连接,返回连接。
记录Mybatis学习过程(完结,未整理版)_第18张图片
记录Mybatis学习过程(完结,未整理版)_第19张图片
POOLED原理分析
PooledDataSource类中的getConnection方法调用了popConnection()方法–>点进去看到如下:
记录Mybatis学习过程(完结,未整理版)_第20张图片
最后对oldestActiveConnection进行一下设置:保证这个oldestActiveConnection是一个全新可以使用的。
记录Mybatis学习过程(完结,未整理版)_第21张图片
stateidleConnections是空闲连接的意思if (!this.state.idleConnections.isEmpty())–>点进去idleConnection
发现它是一个集合protected final List idleConnections = new ArrayList();

mybatis中的连接池put的原理图,如下
记录Mybatis学习过程(完结,未整理版)_第22张图片

mybatis中的事务
什么是事务
事务的四大特性ACID
不考虑隔离性会产生的三个问题
解决办法:四种隔离级别

mybayis中自动提交事务的设置:
它是通过sqlSession对象和commit方法和rollback方法实现事务的提交和回滚。
分析流程源码:
sqlsession.commit();点击进入commit()方法— 记录Mybatis学习过程(完结,未整理版)_第23张图片
就这样一路往下找----->找到BaseExecutor类中的commit方法,在方法中看到 this.transaction.commit();--点进入commit是一个接口Transaction,找到它的实现类JdbcTransaction其中的方法如下
记录Mybatis学习过程(完结,未整理版)_第24张图片
是一个connection对象调用的,而这个connection对象,是java.sql的connection对象,也就是传统的原始的JDBC操作。
记录Mybatis学习过程(完结,未整理版)_第25张图片

点进去测试类的openSession()方法进入人SqlSessionFactory类里面有一个方法 SqlSession openSession(boolean var1);自动提交。
所以在创建的时候把openSession方法改成一个true值,如下:

sqlsession = factory.openSession(true);

就可以不同写提交操作,依然能够实现增删改:

 // sqlsession.commit();

因为在设置提交的时候,我们把手动提交改成了自动提交true。控制台信息如下
记录Mybatis学习过程(完结,未整理版)_第26张图片

Mybatis的映射文件的SQL深入
记录Mybatis学习过程(完结,未整理版)_第27张图片
记录Mybatis学习过程(完结,未整理版)_第28张图片
记录Mybatis学习过程(完结,未整理版)_第29张图片
报错:

### The error may exist in com/selan/dao/IUserDao.xml
### The error may involve com.selan.dao.IUserDao.findUserByCondition-Inline
### The error occurred while setting parameters
### SQL: select * from user WHERE  1=1**;**                        AND username = ?
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AND username = '老王'' at line 3

	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)

解决:
记录Mybatis学习过程(完结,未整理版)_第30张图片
在抽取出代码片段sql语句时,尽量不要写分号,因为在拼接的时候后面还有语句。

Mybati中的多表表查询(重要)
表之间的关系分为: 一对多,多对一,一对一,多对多,
举例:一个用户可以下国歌订单;多个订单可以同属于一个用户;
用户和订单是一对多,订单和用户是多对一。
特例:
如果拿出每一个订单,它都只属于一个用户
所以mybatis就把多对一看成一对一。
mybais多表查询:
示例:用户和账户。一个用户可以有多个账户,一个账户只属于一个用户(多个账户也可以属于一个用户)
步骤
1.先建立两张表 用户表,账户表。让用户表和账户表之间具备一对多的关系;需要用外键在账户表中添加。
2.建立两个实体类,用户实体类和账户实体类,让用户和账户的实体类能体现出来一对多的关系
3,创建两个配置文件。用户配置文件和账户配置文件
4.实现配置:当我们查询用户时,可以同时得到用户下所包含的账户信息
;当我们查询账户时,可以同时得到账户的所属用户信息。

完成account一对一操作-通过写account子查询方式:
查询所有账户同时包含用户名和地址信息:
在这里插入图片描述
记录Mybatis学习过程(完结,未整理版)_第31张图片
在这里插入图片描述
完成account一对一操作-建立实体类关系的方式:
记录Mybatis学习过程(完结,未整理版)_第32张图片
记录Mybatis学习过程(完结,未整理版)_第33张图片
记录Mybatis学习过程(完结,未整理版)_第34张图片
完成user的一对多查询操作
记录Mybatis学习过程(完结,未整理版)_第35张图片
在这里插入图片描述
因为我们要查询所有用户及其他的账户信息,所以所有的用户信息必须查出,因此我们不能再用inner join(user,account where…)内链接查询了.我们应该用左外连接查询(返回所有左表的信息),如下:
记录Mybatis学习过程(完结,未整理版)_第36张图片
记录Mybatis学习过程(完结,未整理版)_第37张图片
分析mybatis中多对多的的应用
示例:用户和角色。一个用户可以有多个角色,一个角色可以赋予多个用户。
步骤:
1.建立两张表,用户表和角色表,让用户表和角色表有多对多的关系,需要使用中间表,中间表中包含各自的主键,在中间表中时外键。
2.建立两个实体类,用户实体类和角色实体类,让用户和角色的实体类能体现出来多对多的关系,各自包含对方一个集合的引用
3,创建两个配置文件。用户配置文件和角色配置文件
4.实现配置:当我们查询用户时,可以同时得到用户下所包含的角色信息
;当我们查询角色时,可以同时得到角色的所赋予的用户信息。
(1)查询角色获取角色下所有的用户信息
所以目标就是查询所有角色,同时获取角色的所赋予的用户,通过查看中间价表就可以看出来。
记录Mybatis学习过程(完结,未整理版)_第38张图片
记录Mybatis学习过程(完结,未整理版)_第39张图片
记录Mybatis学习过程(完结,未整理版)_第40张图片
记录Mybatis学习过程(完结,未整理版)_第41张图片
起别名:
记录Mybatis学习过程(完结,未整理版)_第42张图片
记录Mybatis学习过程(完结,未整理版)_第43张图片
(2)查询用户时,可以同时得到用户下所包含的角色信息.
同理,只需要修改select 语句即可,

select u.*,r.id as rid,r.role_name,r.role_desc from user u
left outer join user_role ur on u.id=ur.uid
left outer join role r on r.id=u`r.rid` 

得到是 :用户的全部信息和每个用户所具备的角色。
记录Mybatis学习过程(完结,未整理版)_第44张图片
记录Mybatis学习过程(完结,未整理版)_第45张图片
JNDI概念和接口:
是sun公司提供的一种标准的java命名系统接口
配置并启动tomcat服务器
在index.jsp中添加如下:进行测试
记录Mybatis学习过程(完结,未整理版)_第46张图片
学习过程中遇到报错

 Cause: javax.naming.NameNotFoundException: Name [jdbc/eesy_mybatis] is not bound in this Context. Unable to find [jdbc].
	org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)

Day04

Mybatis中的延迟加载:
问题:(1)在一对多中,一个用户有100个账户,在查询用户的时候,要不要把关联的账户查出来?
记录Mybatis学习过程(完结,未整理版)_第47张图片
(2)在查询账户的时候,要不要把关联的用户查出来?
所以在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询
所以在查询账户时,账户的所属用户用户信息应该是随着账户查询时一起查出来的。
什么是延迟加载:在真正使用数据时才发起查询,不用的时候不查询。查需加载(懒加载)
什么是立即加载:不管用不用,只要一调用方法,马上发起查询。
在对应的四种表关系中:
一对多,多对多:通常情况下使用延迟加载,
一对一的延迟加载:
记录Mybatis学习过程(完结,未整理版)_第48张图片
执行了三条语句:但并没用实现延迟功能
记录Mybatis学习过程(完结,未整理版)_第49张图片
开始进行mybatis配置
记录Mybatis学习过程(完结,未整理版)_第50张图片
记录Mybatis学习过程(完结,未整理版)_第51张图片
记录Mybatis学习过程(完结,未整理版)_第52张图片
在测试文件中把遍历进行注释:
记录Mybatis学习过程(完结,未整理版)_第53张图片
再次运行:
记录Mybatis学习过程(完结,未整理版)_第54张图片
一对多的延迟加载:
在这里插入图片描述
记录Mybatis学习过程(完结,未整理版)_第55张图片
多对一,一对一:通常都是使用立即加载
Mybatis中的缓存(重点)
什么是缓存:存在于内存中的临时数据
为什么使用缓存:减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,神魔样的数据不能使用:
适用于缓存的:经常查询的数据并且不经常改变的,数据的正确与否对最终的结果影响不大。
不适用于缓存的:数据经常改变,数据的正确与否对最终的结果影响较大的(会产生很大的损失,商品的库存,银行的汇率,股市的排价)
Mybatis中的一级缓存和二级缓存
一级缓存:指的是Mybatis中SqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一快区域中,该区域的结构式一个Map,当我们再次查询同样的数据时,mybatis会先去SqlSession中去查询有没有,有的化直接拿出来;当SqlSession对象消失时,mybatis的一级缓存也就会随之消失。
记录Mybatis学习过程(完结,未整理版)_第56张图片
为true
记录Mybatis学习过程(完结,未整理版)_第57张图片
就查询了一次,说明第二次是从缓存中拿出来的。
当SqlSession对象消失时:
记录Mybatis学习过程(完结,未整理版)_第58张图片
为false,获取了两次SqlSession对象,两个connection存在
记录Mybatis学习过程(完结,未整理版)_第59张图片
还有一种方法可以关闭一级缓存
记录Mybatis学习过程(完结,未整理版)_第60张图片
触发清空一级缓存的情况:
当调用SqlSession的修改、添加、删除、commit(),close()等方法时,。就会清空一级缓存。
记录Mybatis学习过程(完结,未整理版)_第61张图片
记录Mybatis学习过程(完结,未整理版)_第62张图片
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
在这里插入图片描述
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
记录Mybatis学习过程(完结,未整理版)_第63张图片
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
记录Mybatis学习过程(完结,未整理版)_第64张图片
第三步:让当前的操作支持二级缓存(在update/select标签中配置)
记录Mybatis学习过程(完结,未整理版)_第65张图片记录Mybatis学习过程(完结,未整理版)_第66张图片
二级缓存中存放的时对象,因为他创建了一个新的用户对象,所以两个对象不是同一个对象了,是false!
Mybatis中的注解开发
我们可以减少编写Mapper映射文件
环境搭建
单表CRUD操作(代理dao方式)
记录Mybatis学习过程(完结,未整理版)_第67张图片
记录Mybatis学习过程(完结,未整理版)_第68张图片

记录Mybatis学习过程(完结,未整理版)_第69张图片只要用到了这个不管有没有删除IUserDao都会报错
Mybatis注解解决实体类属性名和数据库列明不匹配问题
记录Mybatis学习过程(完结,未整理版)_第70张图片
记录Mybatis学习过程(完结,未整理版)_第71张图片
记录Mybatis学习过程(完结,未整理版)_第72张图片
记录Mybatis学习过程(完结,未整理版)_第73张图片
ResultMap里面有一个字符串数组,所以说它可以引用多个
@ResultMap(value = “userMap”,“dad”,“dasd”)
记录Mybatis学习过程(完结,未整理版)_第74张图片
多表查询操作
一对一的查询配置
报错小提示:
在这里插入图片描述
这种报错是因为配置文件的原因
一个账户只能对应一个用户
记录Mybatis学习过程(完结,未整理版)_第75张图片
记录Mybatis学习过程(完结,未整理版)_第76张图片
点进FethType看到:
记录Mybatis学习过程(完结,未整理版)_第77张图片
对一的时候通常选择立即加载
对多的时候通常选择延迟加载
记录Mybatis学习过程(完结,未整理版)_第78张图片
一对多的查询配置
一个用户可以对应多个账户:
配置了延迟加载,
记录Mybatis学习过程(完结,未整理版)_第79张图片
最后,无论是一对多,还是多对一,在配置中只需要关注两个属性:如下:
记录Mybatis学习过程(完结,未整理版)_第80张图片
缓存的配置
二级缓存:
记录Mybatis学习过程(完结,未整理版)_第81张图片
这样执行一定会是干了两次:
记录Mybatis学习过程(完结,未整理版)_第82张图片
开始进行配置:
记录Mybatis学习过程(完结,未整理版)_第83张图片
注解缓存的配置
记录Mybatis学习过程(完结,未整理版)_第84张图片
结果,发现只干了一次:
记录Mybatis学习过程(完结,未整理版)_第85张图片

你可能感兴趣的:(记录Mybatis学习过程(完结,未整理版))