mybatis

查询缓存

13.1一级缓存:同一个SqlSession对象

  • MyBatis默认开启一级缓存,如果用同样的SqlSession查询相同的数据,则只会在第一次查询时向数据库发送sql语句,并将查询的结果放入到SqlSession(作为缓存存在);后续再次查询该同样的对象时,则直接从缓存中查询该对象即可(即省略了数据库的访问)。

13.2二级缓存

  • MyBatis默认情况下没有开启二级缓存,需要手工打开。

    1. config.xml

      1
      <setting name="cacheEnabled" value="true"/>
    2. 在具体的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”

    • 清理:

      1. 执行commit();一级二级一样都使用这个(执行增删改时,会清理缓存;设计的原因是为了防止脏数据的产生)

        在二级缓存 中,commit()不能是查询自身的commit。

        commit会清理一级和二级缓存:但是清理二级缓存时,不能是查询自身的commit;

      2. 在select标签中增加属性:flushCache=”true”

  • 三方提供的二级缓存:

    • echcache、memcache

    • 要想整合三方提供的二级缓存(或者自定义缓存),必须实现Cache接口,该接口的默认实现类为PerpetualCache

    • 整合echcache二级缓存:

      1. 导入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>
      1. 编写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>
      2. 开启EhCache二级缓存

        在xxxMapper.xml中开启

        1
        2
        3
        4
        <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <!--自定义Ehcache中的全局参数-->
        <property name="" value=""/>
        </cache>

14.逆向工程

  • 表、类、接口、mapper.xml四者密切相关,因此,当知道一个的时候,其他三个应该可以自动生成。

    表——–>其他三个

  • 实现步骤:

    1. 添加Mave n依赖

      1
      2
      3
      4
      5
      <dependency>
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-core</artifactId>
      <version>1.3.2</version>
      </dependency>
    2. 逆向工程的配置文件generator.xml

    3. 执行

mybatis学习进阶

1.数据库的环境切换

2.注解方式

​ 推荐使用xml

​ a.将sql语句写在接口的方法前面

​ b.将接口的全类名写入到,让mybatis知道sql语句此时是存储在接口中的。

注解和XML都支持批量引入

4.事务提交方式

  • 手动提交:
1
2
SqlSession session = sessionFactory.openSession();
session.commit();//执行完commit时,手动提交 事务
  • 自动提交 :每一个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
    5
    void 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
2
3
4
5
//使用#{empInfo.name,jdbcType=null}这个就可以了
<insert id="insertEmpInfo">
insert into empinfo(id,name,age,job,phone)
values (#{sId},#{empInfo.name,jdbcType=null},#{empInfo.age},#{empInfo.job},#{empInfo.phone})
</insert>

b.配置mybatis全局配置文件conf.xml

1
2
3
 <settings>
<setting name="jdbcTypeForNull" value="NUll"/>
</settings>

7.返回值为HashMap的情况

  • 注意:在mysql中@MapKey(“id”)字段为小写
  • 在oracle中为全大写
  • map:
    • key:id value:Student
  • 程序根据select的返回值,知道map的value就是EmpInfo,根据@MapKey(“id”)知道Map的key是id
1
2
@MapKey("id")
HashMap<String,EmpInfo> queryAllEmp();
1
2
3
<select id="queryAllEmp" resultType="HashMap">
select * from empinfo
</select>

8.ResultMap:字段和属性名的相应

9.别名问题

1
2
3
4
5
6
7
<!--设置单个/多个别名-->
<typeAliases>
<!--单个别名(别名 忽略大小写)-->
<!--<typeAlias type="com.itt.entity.Student" alias="student"/>-->
<!--批量定义别名,以下会自动将该包中的所有类 批量定义别名:别名就是类名(不带包名的类名)-->
<package name="com.itt.entity"/>
</typeAliases>
  • 当这个包下的子包 与Student重名时,必须使用注解的方式重新命名
1
2
3
4
@Alias("myempinfo")
public class EmpInfo {

}

10.处理where子句后面的and的三种方式

  • trim标签可以去掉后面的 and
  • 可以处理拼接sql中【开头或结尾】第一个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
<trim>
<if test="id != null and id != 0">
id = #{id} and
</if>
<if test="name != null and name != ''">
name like '%${name}%' and
</if>
<if test="phone != null and phone != ''">
phone = #{phone} and
</if>
</trim>
</select>
  • prefix=”where”:在整个语句中加where
  • prefix=”set”:在整个语句中加set,可用于更新
  • prefixOverrides=”and”:智能处理前面的and
  • suffixOverrides=”and”:智能处理后面的and
  • suffixOverrides=”,”:智能处理后面的,常用于更新
1
2
3
4
5
6
7
8
9
10
11
<trim prefix="where" prefixOverrides="and">
<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>
</trim>
  • 标签可以去掉前面的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
2
3
4
5
6
7
8
9
10
11
12
<select id="queryByINP" parameterType="EmpInfo" resultType="EmpInfo">
select * from empinfo where 1 = 1
<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>
</select>

11.内置参数

  • _paramter:代表Mybatis的输入参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="queryByINP" parameterType="EmpInfo" resultType="EmpInfo">
select * from empinfo
<trim prefix="where" prefixOverrides="and">
<if test="id != null and id != 0">
and id = #{id}
</if>
<if test="name != null and name != ''">
and ename like '%${name}%'
</if>
//_parameter替代parameterType这个输入的对象
<if test="_parameter.phone != null and _parameter.phone != ''">
and phone = #{_parameter.phone}
</if>
</trim>
</select>
  • _databaseId:代表当前数据库的名字

12.模糊查询三种方式

12.1

  • ${}原样输出
    1
    stuName like '%${stuName}%'
  • #{}自动拼接’ ‘:可以防止SQL注入

12.2 传值时,直接 传 %x%

1
student.setStuName("%s%");
1
stuName like #{stuName}

12.3 bind参数

1
2
3
4
<bind name="_queryName" value="'%'+name+'%'"/>
<if test="name != null and name != ''">
and name like #{_queryName}
</if>
1
I:\MyBatisIdea\MyBatisGenerator\src\main\java

13.逆向工程

1.导入依赖

2.xml模板文件(修改生成路径、表名)

3.根据java模板类一键生成

根据学生表->学生类、学生Mapper接口、studentMapper.xml

4.如何使用

​ 增加Mybatis配置文件 conf.xml

对于like模糊查询,逆向工程需要在传值时 写入%x%

逆向工程的实例:

使用的是StudentExample

  • 测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Test
public void queryAllEmpById() throws Exception{
//Connection - SqlSession操作myBatis

//config.xml -> reader
Reader reader = Resources.getResourceAsReader("conf.xml");

//reader ->SqlSession
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();

StudentMapper mapper = session.getMapper(StudentMapper.class);

StudentExample example = new StudentExample();
StudentExample.Criteria criteria = example.createCriteria();
criteria.andStunameLike("%d%");



StudentExample example1 = new StudentExample();
StudentExample.Criteria criteria1 = example1.createCriteria();
//模糊查询
criteria1.andStunoEqualTo(10);
criteria1.andStunameLike("%k%");

//使用exampl整合的方式实现or操作
example.or(criteria1);
//Example中的Criteria:为查询的条件
List<Student> students = mapper.selectByExample(example);

System.out.println(students);


session.close();
}

14.MyBatis架构和源码解析

MyBatis中步骤:

  • 1.获取SqlSessionFactory对象
  • 2.获取SqlSession对象
  • 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的语句