查询缓存
13.1一级缓存:同一个SqlSession对象
- MyBatis默认开启一级缓存,如果用同样的SqlSession查询相同的数据,则只会在第一次查询时向数据库发送sql语句,并将查询的结果放入到SqlSession(作为缓存存在);后续再次查询该同样的对象时,则直接从缓存中查询该对象即可(即省略了数据库的访问)。
13.2二级缓存
MyBatis默认情况下没有开启二级缓存,需要手工打开。
config.xml
1
<setting name="cacheEnabled" value="true"/>
在具体的mapper.xml中声明开启(studentMappper.xml)
1
2
3<mapper namespace="com.itt.mapper.StudentMapper">
<!--声明此namespace开启二级缓存-->
<cache/>根据 异常提示:NotSerializableException可知,MyBatis是将对象放入到硬盘文件中
——->序列化:内存—–>硬盘
——->反序列化:硬盘 —–>内存
准备缓存的对象,必须实现了序列化接口(如果开启了缓存 namespace=”com.itt.mapper.StudentMapper”),可知序列化对象为Student,因此要将Student对象序列化(序列化Stuent类,以及Student的级联属性和父类)
触发将对象写入二级缓存的时机:SqlSession对象的close()方法。
Mybatis自带二级缓存:【同一个namespace】生成的mapper对象
回顾:namespace的值,就是接口的全类名(包名.类名),通过接口可以生成动态代理对象(studentMapper对象)
- ———->namespace决定了studentMapper对象的产生
结论:只要产生的xxxMapper对象来自于同一个namespace,则这些对象共享二级缓存
注意:二级缓存的范围是同一个namespace,如果有多个xxxMapper.xml的namespace值相同,则通过这些xxxMapper.xml产生的xxxMapper对象,仍然共享二级缓存。
禁用:select标签中userCache=”false”
清理:
执行commit();一级二级一样都使用这个(执行增删改时,会清理缓存;设计的原因是为了防止脏数据的产生)
在二级缓存 中,commit()不能是查询自身的commit。
commit会清理一级和二级缓存:但是清理二级缓存时,不能是查询自身的commit;
在select标签中增加属性:flushCache=”true”
三方提供的二级缓存:
echcache、memcache
要想整合三方提供的二级缓存(或者自定义缓存),必须实现Cache接口,该接口的默认实现类为PerpetualCache
整合echcache二级缓存:
- 导入Maven依赖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<!--mybatis使用ehacche-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>编写ehcache配置文件Ehcache.xml
1
2
3
4
5
6
7
8
9
10
11
12
13<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="F:\Ehcache"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="1000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="100"
timeToLiveSeconds="100"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>开启EhCache二级缓存
在xxxMapper.xml中开启
1
2
3
4<cache type="org.mybatis.caches.ehcache.EhcacheCache">
<!--自定义Ehcache中的全局参数-->
<property name="" value=""/>
</cache>
14.逆向工程
表、类、接口、mapper.xml四者密切相关,因此,当知道一个的时候,其他三个应该可以自动生成。
表——–>其他三个
实现步骤:
添加Mave n依赖
1
2
3
4
5<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>逆向工程的配置文件generator.xml
执行
mybatis学习进阶
1.数据库的环境切换
2.注解方式
推荐使用xml
a.将sql语句写在接口的方法前面
b.将接口的全类名写入到
注解和XML都支持批量引入
4.事务提交方式
- 手动提交:
1 | SqlSession session = sessionFactory.openSession(); |
- 自动提交 :每一个dml语句自动提交
1 | SqlSession session = sessionFactory.openSession(true); |
5.参数问题
目前将多个参数封装到一个javabean对象(pojo),然后使用该对象传递
a.传入多个参数时,不用在mapper.xml中编写parameterType
异常提示:
stuNo不能使用,使用的是:【arg3,arg2,arg1,arg0,param3,param2,param1】
1
2
3
4<insert id="insertEmpInfo">
insert into empinfo(id,name,age,job,phone)
values (#{arg0},#{arg1},#{arg2},#{arg3},#{arg4})
</insert>b. 命名参数的方式
可以在接口中通过@Param(“”)来指定sql中参数的名字
1
2
3
4
5void insertEmpInfo(@Param("sId") String id,
@Param("sName") String name,
@Param("sAge") String age,
@Param("sJob") String job,
@Param("sPhone") String phone);1
2
3
4<insert id="insertEmpInfo">
insert into empinfo(id,name,age,job,phone)
values (#{sId},#{sName},#{sAge},#{sJob},#{sPhone})
</insert>c. 综合使用的情况
1
2//一个是简单类型 ,一个是对象类型
void insertEmpInfo(@Param("sId") String id,@Param("empInfo") EmpInfo empInfo);1
2
3
4<insert id="insertEmpInfo">
insert into empinfo(id,name,age,job,phone)
values (#{sId},#{empInfo.name},#{empInfo.age},#{empInfo.job},#{empInfo.phone})
</insert>
6.增加null
oracle:如果插入的 字段是null,提示错误:Other 而不是gnull
mysql:如果插入的字段是null,可以正常执行(没有约束)
原因:
各个数据库在Mybatis中对各种数据类型的默认值不一致。
mybatis中,jdbcTypeForNull(如果是null),则默认值OTHER。
在mysql中,mybatis将Other当做NULL来处理了,但是oracle不行。
解决:
oracle:null -> OTHER 手工告诉oracle:other -> null
a.当某个数据类型oracle无法处理进,告诉它用默认值null 来处理
1 | //使用#{empInfo.name,jdbcType=null}这个就可以了 |
b.配置mybatis全局配置文件conf.xml
1 | <settings> |
7.返回值为HashMap的情况
- 注意:在mysql中@MapKey(“id”)字段为小写
- 在oracle中为全大写
- map:
- key:id value:Student
- 程序根据select的返回值,知道map的value就是EmpInfo,根据@MapKey(“id”)知道Map的key是id
1 | "id") ( |
1 | <select id="queryAllEmp" resultType="HashMap"> |
8.ResultMap:字段和属性名的相应
9.别名问题
1 | <!--设置单个/多个别名--> |
- 当这个包下的子包 与Student重名时,必须使用注解的方式重新命名
1 | "myempinfo") ( |
10.处理where子句后面的and的三种方式
- trim标签可以去掉后面的 and
可以处理拼接sql中【开头或结尾】第一个and
1 | <select id="queryByINP" parameterType="EmpInfo" resultType="EmpInfo"> |
- prefix=”where”:在整个语句中加where
- prefix=”set”:在整个语句中加set,可用于更新
- prefixOverrides=”and”:智能处理前面的and
- suffixOverrides=”and”:智能处理后面的and
- suffixOverrides=”,”:智能处理后面的,常用于更新
1 | <trim prefix="where" prefixOverrides="and"> |
标签可以去掉前面的and 1
2
3
4
5
6
7
8
9
10
11
12
13
14<select id="queryByINP" parameterType="EmpInfo" resultType="EmpInfo">
select * from empinfo
<where>
<if test="id != null and id != 0">
and id = #{id}
</if>
<if test="name != null and name != ''">
and name like '%${name}%'
</if>
<if test="phone != null and phone != ''">
and phone = #{phone}
</if>
</where>
</select>直接 在where后面加:1 = 1
1 | <select id="queryByINP" parameterType="EmpInfo" resultType="EmpInfo"> |
11.内置参数
- _paramter:代表Mybatis的输入参数
1 | <select id="queryByINP" parameterType="EmpInfo" resultType="EmpInfo"> |
- _databaseId:代表当前数据库的名字
12.模糊查询三种方式
12.1
- ${}原样输出
1
stuName like '%${stuName}%'
- #{}自动拼接’ ‘:可以防止SQL注入
12.2 传值时,直接 传 %x%
1 | student.setStuName("%s%"); |
1 | stuName like #{stuName} |
12.3 bind参数
1 | <bind name="_queryName" value="'%'+name+'%'"/> |
1 | I:\MyBatisIdea\MyBatisGenerator\src\main\java |
13.逆向工程
1.导入依赖
2.xml模板文件(修改生成路径、表名)
3.根据java模板类一键生成
根据学生表->学生类、学生Mapper接口、studentMapper.xml
4.如何使用
增加Mybatis配置文件 conf.xml
对于like模糊查询,逆向工程需要在传值时 写入%x%
逆向工程的实例:
使用的是StudentExample
- 测试类
1 |
|
14.MyBatis架构和源码解析
MyBatis中步骤:
- 1.获取SqlSessionFactory对象
- 2.获取SqlSession对象
- 3.获取xxxMapper对象(代理接口 中的方法、mapper.xml中的
- 4.执行标签中定义的SQL语句
1.获取SqlSessionFactory对象
parse解析器
Mybatis将所有的配置信息存放到了Configuration configuration对象中
MapperStatement -》存在于Configuration中
environment -》存在于Configuration中
所有的配置信息、增删改标签全部存在于Configuration中
Configuration又存在于DefaultSqlSessionFactory对象中(SqlSessionFactory)
SqlSessionFactory对象—》DefaultSqlSessionFactory—》Configuration—》所有的配置信息
2.获取SqlSession对象
1 | executor = (Executor) interceptorChain.pluginAll(executor); |
通过装饰模式,将刚才产生的SimpleExecutor包装成一个更加强大 的executor
作用:以后如果我们要给MyBatis写自己的插件,就可以通过拦截器实现
SqlSession ->openSession()—>openSessionFromDataSource() –>DefaultSqlSession对象
SqlSession -> DefaultSqlSession对象 -> 执行SQL
3.获取xxxMapper对象(代理接口 中的方法、mapper.xml中的
4.执行标签中定义的SQL语句
- 插件
select * from student –》拦截器
目标对象target的包装后的产物 —>metaObject.getValue(“可以从target中获取”)
通过打印语句,可知,target就是 RoutingStatementHandler
———-》
metaObject.getValue(“可以从RoutingStatementHandler中获取”)
可以从RoutingStatementHandler获得:getBoundSql,getParameterHandler
——->—>metaObject.getValue(“parameterHandler”)
- metaObject.getValue(“parameterHandler.parameterObject”)//xxxMapper.xml中的语句中的参数值
- metaObject.getValue(“parameterHandler.boundSql”)//xxxMapper.xml中sql的语句