Java Mybatis

前言:自己想要一篇把自己这个星期学的mybatis全部记录下来,所以篇幅可能会很长

什么是mybatis

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。

MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。

2013年11月迁移到Github。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)

第一个mybatis学习

环境搭建

下面两种方法都可以,maven导入依赖会比较方便

github:https://github.com/mybatis/mybatis-3/releases

或者maven导入pom包也可以

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>5.1.2</version>
</dependency>

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。

SqlSessionFactory的实例可以通过 SqlSessionFactoryBuilder 获得。

而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

XML配置SqlSessionFactory实例

那么我们这里就通过XML需要预先配置一个Configuration实例

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。

简单的示例核心配置文件:

environment 元素体中包含了事务管理和连接池的配置

mappers 元素则包含了一组映射器(mapper)

环境中配置的如下核心文件内容:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/sec_sql?useSSL=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/zpchcbd/dao/UserMapper.xml"/>
    </mappers>
</configuration>

从 SqlSessionFactory 中获取 SqlSession

怎么理解这个SqlSessionFactory和SqlSession的关系,我们现在已经通过核心配置文件了,SqlSessionFactory工厂类来能根据这个配置里面的相关配置信息来返回一个对应关系的SqlSession对话,我们通过这个对话session的实例可以进行获取相关的mapper来进行调用数据库的操作!

那么肯定就会有疑问了,那到底什么是SqlSession,它能帮助我们干什么呢?

SqlSession的作用:提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句!

因为每次都需要调用SqlSession,为了方便可以写一个工具类静态方法每次调用返回!

public class MybatisUtils {
    public static SqlSessionFactory sqlSessionFactory = null;
    static{
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

编写要注册的mapper

所谓的编写mapper,其实就是写一个用来映射的xml文件,我们这里的mapper接口类跟之前学servlet一样的

然后之前的是实现类,现在就是替换为用来映射的xml文件,xml文件中直接写上对应的SQL语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zpchcbd.dao.UserMapper">
    <select id="getUserList" resultType="User">
        select * from userinfo;
    </select>
</mapper>

从 SqlSession 中获取注册的Mapper,调用Mapper获取数据

到目前我们注册的mapper也已经写好了,sqlsession也已经可以获取了,那么现在就是通过sqlsession来获取注册号的mapper,来实现数据的获取

public class Test {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //方式一
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        List<User> userList = mapper.getUserList();

        //方式二
        //List<User> userList = sqlSession.selectList("com.zpchcbd.dao.UserDao.getUserList");

        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
}

最终的结果就是如下:

public class Test {
    public static void main(String[] args) {
        test01();
    }

    // get userlist
    public static void test01(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //方式一
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();

        //方式二
        //List<User> userList = sqlSession.selectList("com.zpchcbd.dao.UserDao.getUserList");

        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
}

注意点和坑点

注意点:在核心配置文件中进行注册mapper的需要特别注意写法,会发现是用路径的方式写的,而不是.来表示,这是一个坑!

自己画了个流程图来理解过程,更详细的其实可以通过debug来走一遍,这里就不演示了,因为这里的内容对于学习代码审计漏洞不重要!

基于XML映射的增删改查

这篇笔记主要研究Mapper.xml中相关的SQL增删改查操作

有参和无参查询

目前已经学习了两个小知识点了:

namespace:命名空间,需要指向我们UserMapper接口类

resultType:结果的返回类型,跟对应的Mapper接口类的返回的数据类型相同

可以看到如下的mapper.xml是用来查询userinfo的表中的数据

我们已经学习过无参查询了,那么如果我们查询的数据需要参数该如何进行解决?这里就会学习到一个新的知识点

parameterType:表示参数的类型

此时我们的mapper.xml中的内容就如下:

调用代码如下:

结果如下:

知识点:在mybatis中,事务处理会自动开启,我们在增删改的时候最后都需要提交事务!

理解:大家学到这里应该都有一个体会,Mybatis是一个在Dao层的应用的框架,它帮助了我们便捷的处理SQL语句,我们通过对XML的编写(这里只学习了XML,不过不仅仅是XML,海可以通过注解的方法,这里还没有体现),它的作用就是帮助我们将XML映射为对应的SQL语句进行执行,十分的便捷!

这里顺便扩展下知识,还有种老方法同样也可以来进行查询,不过官方文档已经不推荐使用这种方式了,如下写法:

增加insert的语句

<insert id="addUser" parameterType="com.zpchcbd.pojo.User">
    insert into userinfo(name, age, content, address) values(#{name}, #{age}, #{content}, #{address})
</insert>
// insert user
public static void test03(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setAddress("浙江省温州市瑞安市");
    user.setAge(18);
    user.setContent("chiling@cnpanda.net");
    user.setName("池灵");
    mapper.addUser(user);
    sqlSession.commit();
}

修改update的语句

<update id="updateUserById" parameterType="com.zpchcbd.pojo.User">
    update userinfo set name = #{name} where id = #{id}
</update>
// update
public static void test04(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("池灵啊");
    user.setId(7);
    mapper.updateUserById(user);
    sqlSession.commit();
}

删除delete的语句

<delete id="deleteUserById" parameterType="int">
    delete from userinfo where id = #{id}
</delete>
// delete
public static void test05(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.deleteUserById(7);
    sqlSession.commit();
}

核心配置Configuration

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。

例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。

这个很简单只需要在核心配置中添加一份environments的配置项即可,在不同的场景进行切换十分方便!

环境配置(environment)

环境配置是哪个则取决于environments标签的default的值

具体的配置项参考:https://mybatis.org/mybatis-3/zh/configuration.html#environments

transactionManager标签:提供了两种选择, type="[JDBC|MANAGED],一般来说都会选择JDBC,因为使用了JDBC的提交和回滚机制

数据源(dataSource): dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源,它有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")映射器(mappers): 定义 SQL 映射语句了。 需要告诉 MyBatis 到哪里去找到这些语句, 所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。属性

属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换,通过这个标签我们就可以不用在核心配置中把字符串写死,而是动态的加载

写好我们的配置项,然后在核心配置中添加上我们要引入的资源文件,我们还进行改造下,让配置项的值动态的改变,这里唯一需要注意的就是字段名都需要相同

它不仅可以外部引入,我们还可以通过内部定义来进行引入,不过这样的话感觉就没啥意义了,如下同样的效果!

设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

配置项有许多许多,具体参考:https://mybatis.org/mybatis-3/zh/configuration.html#settings

这里讲一个我们日常调试需要的东西,也就是日志记录,当我们SQL语句写错了,想要看具体的SQL语句哪里错了,我们就可以通过这个来进行实现

这个logImpl主键对应的值有很多个,这里简单讲几个我们常用的

STDOUT_LOGGING :标准输出类,日志工厂类实现的一种

它会在控制台打印一大堆相关的信息,其中就包括了SQL查询的相关信息,可以看到相关的事务操作的开启,预编译的查询语句等等。。。

LOG4J :日志工厂类实现的一种

该配置项在核心配置文件中进行编写,如下所示,在自己没有学java之前,听到很多log4j的,原来是一个日志类相关类。。。

以下是标签的log4j的配置项,仅供参考

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file


#控制台输出的相关设置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n


#文件输出的相关设置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/zpchcbd.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n


#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

LOG4J的Logger类的使用:

public static Logger logger = Logger.getLogger(你当前要调试的类名.class);,比如这里我调试的当前类就是Test类,则为Test.class

类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

这样的话我们在XML中配置的时候,一些全限定类名就可以方便用一些简短的名字来进行替换

比如如下图中的resultType属性,每次都要写这么长的类,那肯定是特别麻烦的,所以我们就可以通过类型别名来进行解决!

在核心配置中进行配置别名:

此时再去进行替换为User,运行发现同样可以!

或者也可以通过指定包的别名来进行实现,此时 MyBatis 会在com.zpchcbd.pojo包名下面搜索需要的 Java Bean,比如User类

但是通过指定包的别名来进行实现的时候,需要注意 会使用 Bean 的首字母小写的非限定类名来作为它的别名

映射器(mappers)

映射mapper的方法一共有四种方法,我们就用过如下的一种:使用资源引用的方式(推荐使用)

<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

第二种:file协议指定资源(不推荐使用,自己在windows上路径会报错)

<!-- 使用完全限定资源定位符(URL) --><mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/></mappers>

第三种:映射接口实现类(一般注解开发的时候用的是这种)

<!-- 使用映射器接口实现类的完全限定类名 --><mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/></mappers>

但是这个是有问题的,他需要前提条件,也就是映射的接口类的名称需要和编写的映射xml的文件名称需要相同才行

第四种:一次性包内的所有映射接口

<!-- 将包内的映射器接口实现全部注册为映射器 --><mappers>
<package name="org.mybatis.builder"/></mappers>

但是这个是有问题的,他需要前提条件,也就是映射的接口类的名称需要和编写的映射xml的文件名称需要相同才行

XML映射器

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

在第一个Mybatis中已经学习了映射xml的SQL语句的增删改查标签了

这里还需要学习一个标签,resultMap

resultMap

用来解决:属性名和字段名不一致的时候

举个例子:此时pojo实体类User和数据库的字段不一致的情况下,如果想要映射获得结果该如何操作?

发现查询记录的时候,字段为name的数据为null

第一种解决方法:通过SQL中的as取别名的方法来进行解决

select id,name as username,age,content,address from userinfo where id = #{id}

这样子的话就相当于从数据库中取出id username age content address来放到实体类User的属性中去,那么正好是字段和属性一一对应,然后最后sout的时候,数据也是相对应的

第二种解决方法:通过resultMap(重点)

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 95% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。

<resultMap id="userResultMap" type="User">
    <id property="id" column="id" /> <!--主键 标识的作用-->
    <result property="username" column="name"/>
    <result property="age" column="age"/>
    <result property="content" column="content"/>
    <result property="address" column="address"/>
</resultMap>

需要注意的是,select标签的resultMap为resultMap标签的id值,resultMap的type则代表返回的类型

最终也可以解决这个问题

通过注解增删改查

当我们用注解进行开发的时候,mapper的注册则需要为class类型(之前用XML来查询的时候,属性都是resource),如下所示

注解的体现如下:

但是会发现一个问题,如果此时数据表的字段和实体类不同的话,那么最终查询出来的结果则为如下,所示对应Mybatis中进行映射操作主要还是用xml来进行注册映射,但是如果简单的就可以直接通过注册来进行实现,主要还是看具体情况!

之后的增删改查其实都差不多,直接写代码就好了

多对一的查询处理

下面的多对一和一对多,其实也是ResultMap的加深学习

1、一个Student的实体类有三个属性,其中有个属性为Teacher类

2、一个Teacher类有两个属性

想要在SQL语句中实现对student表进行查询,其中tid为对应的teacher表中的老师的性名,有两种方式:

第一种查询:多表查询

mysql> select a.id,a.name,b.name from student a,teacher b where a.tid=b.id;
+----+--------+-----------+
| id | name | name |
+----+--------+-----------+
| 1 | 小明 | 秦老师 |
| 2 | 小红 | 秦老师 |
| 3 | 小张 | 秦老师 |
| 4 | 小李 | 秦老师 |
| 5 | 小王 | 秦老师 |
+----+--------+-----------+
5 rows in set (0.00 sec)

那么如果想要在Mybatis中进行实现的话,则需要如下两种方法:

第一种方法:

<resultMap id="StudentTeacherMap" type="com.zpchcbd.pojo.Student">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="tid" column="tid" javaType="com.zpchcbd.pojo.Teacher" select="getTeacher" />
</resultMap>

<select id="getStudentList" resultMap="StudentTeacherMap">
    select * from student;
</select>

<select id="getTeacher" resultType="com.zpchcbd.pojo.Teacher">
    select * from teacher where id =#{id};
</select>

这里的getTeacher的#{id},这个id其实可以随便填,比如#{asdasd},都可以,我认为mybatis会自动将getStudentList的tid值拿到然后替换为这个值然后来进行查询,只是猜测,具体还是分析源码!

第二种方法:按结果嵌套查询

<resultMap id="StudentTeacherMap2" type="com.zpchcbd.pojo.Student">
    <id column="aaa" property="id"/>
    <result column="bbb" property="name"/>
    <association property="tid" javaType="com.zpchcbd.pojo.Teacher">
        <result column="ccc" property="name" />
    </association>
</resultMap>

<select id="getStudentList2" resultMap="StudentTeacherMap2">
    select a.id aaa,a.name bbb,b.name ccc from student a,teacher b where a.tid=b.id;
</select>

自己了解即可,最终目的是要分析漏洞,不是拿来开发的,不要花时间无用功!

一对多的查询处理

想要在SQL语句中实现对teacher表进行查询,其中students为对应的是student数据表中tid=id的数据,有两种方式:

mysql> select a.id aid, a.name tname,b.name sname from teacher a,student b where a.id=b.tid;
+-----+-----------+--------+
| aid | tname | sname |
+-----+-----------+--------+
| 1 | 秦老师 | 小明 |
| 1 | 秦老师 | 小红 |
| 1 | 秦老师 | 小张 |
| 1 | 秦老师 | 小李 |
| 1 | 秦老师 | 小王 |
+-----+-----------+--------+

mybatis中进行编写,同样也有两种方法!

第一种方法:当一对多的时候,就需要用到collection标签,为集合标签!

知识点: JavaType和ofType都是用来指定对象类型的,但是JavaType是用来指定pojo中属性的类型,而ofType指定的是映射到list集合属性中pojo的类型

那么JavaType和ofType什么时候用呢?比如collection集合的时候就用ofType来指向集合中存储的类型即可,而关联的时候用JavaType即可

<resultMap id="TeacherStudentMap" type="com.zpchcbd.pojo.Teacher">
    <id column="id" property="id"/>
    <result column="tname" property="name"/>
    <collection property="students" ofType="com.zpchcbd.pojo.Student">
        <result column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="tid" property="tid"/>
    </collection>
</resultMap>

<select id="getTeacherList" resultMap="TeacherStudentMap">
    select a.id id, b.id sid, b.tid tid, a.name tname, b.name sname from teacher a,student b where a.id=b.tid;
</select>

第二种方法:查询嵌套

<resultMap id="TeacherStudentMap2" type="com.zpchcbd.pojo.Teacher">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <collection property="students" ofType="com.zpchcbd.pojo.Student" select="getStudentById" column="id"/>
</resultMap>

<select id="getTeacherList2" resultMap="TeacherStudentMap2">
    select * from teacher;
</select>

<select id="getStudentById" resultType="com.zpchcbd.pojo.Student">
    select * from student where tid = #{tid};
</select>

动态SQL

什么是动态SQL?

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

直接回想下之前写的smbms超市后台系统,其中有一个获得用户列表的功能的时候,及需要根据不同的参数来进行拼接的时候,就会拼接出不同类型的语句,也就是如下的红框中的部分,那么这种在mybatis中就需要用到动态SQL!

IF标签

那么自己来试下,比如现在一个表中存在如下内容:

需求:查询作者为“狂神说”,并且如果title存在则查询指定的title的记录,写法如下:

<select id="getBook" parameterType="map" resultType="com.zpchcbd.pojo.Blog">
    select * from blog where 1=1 and author = "狂神说"
    <if test="title != null">
    and title=#{title}
    </if>
</select>

这里继续引入话题,可以看到上面我们的SQL语句开头是where 1=1的,原因就是如果我们不加where 1=1,然后在if语句中加上就会造成一个问题,比如:

select * from blog
    <if test="title != null">
    where title=#{title}
    </if>

    <if test="views != null">
    where views=#{views}
    </if>

可以看到上面的语句就会两个同时加,那么有人肯定会说那就一个呀,比如如下,其实还是一样的,如果上面的title不符合,下面的views符合,最终语句还是错误的!

select * from blog
    <if test="title != null">
    where title=#{title}
    </if>

    <if test="views != null">
    views=#{views}
    </if>

where标签

所以这里就要用到一个where标签,最终版则如下:

<select id="getBook" parameterType="map" resultType="com.zpchcbd.pojo.Blog">
select * from blog
<where>
<if test="title != null">
and title=#{title}
</if>

<if test="views != null">
and views=#{views}
</if>
</where>
</select>

如果where标签的内的有一个符合,那么where标签就会自动帮你加上where 拼接,如果一个都没有则where内不生效,也就是正常查询

set标签

set标签和where标签类似,只是set标签是应用于update语句中!

比如如下语句,有title则set title,有views则set views,有author则set author

<update id="updateBook" parameterType="map">
update blog
<set>
<if test="title != null">
title=#{title},
</if>
<if test="views != null">
views=#{views},
</if>
<if test="author != null">
author=#{author},
</if>
</set>
where id = #{id}
</update>

那么肯定有人问,为什么不可以直接写,比如如下,这样会有问题,主要原因就是 逗号,如果该最后一个字段此时没有在map中的话,那么这个倒数第二个逗号则就会导致报错,但是如果我们用了set标签的话,当最后一个字段不存在的时候,set标签帮我们自动处理,将倒数第二个,也就是views字段的逗号去除,这样就不会报错了!

<update id="updateBook" parameterType="map">
update blog
<if test="title != null">
title=#{title},
</if>
<if test="views != null">
views=#{views},
</if>
<if test="author != null">
author=#{author},
</if>
where id = #{id}
</update>

choose,when,otherwise标签

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

<select id="getBook" parameterType="map" resultType="com.zpchcbd.pojo.Blog">
    select * from blog where author=#{author}
    <choose>
        <when test="title != null">
            and title=#{title}
        </when>

        <when test="views != null">
            and views=#{views}
        </when>

        <otherwise>
            and id=#{id}
        </otherwise>
    </choose>
</select>

三选一,要么第一个title,要么views,如果title和views都没有则就是id,存在优先级从上到下,如果匹配到了一个则下面的都不匹配!

测试代码:

foreach标签

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。

SQL例子:



<select id="getUserByForeach" parameterType="map" resultType="com.zpchcbd.pojo.User">
    select * from userinfo where id in
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

缓存

什么是缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

一级缓存

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存,这种也就是一级缓存!其默认的清除策略是 LRU。

这同样也就在一级缓存下说明了只有单个sqlsession的会话的数据进行缓存,如下所示,可以看到单个sqlsession进行查询的时候就直接出数据,没有重新进行请求!

但是需要注意的是如下:

1、映射语句文件中的所有 select 语句的结果将会被缓存。

2、映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。(所以说如果中间又进行了增删改操作的话,那么缓存就会消失,也就是被刷新)

3、缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

4、缓存不会定时进行刷新(也就是说,没有刷新间隔)。

5、缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。

6、缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

总结下缓存失效的情况如下:

二级缓存

二级缓存就被称作为全局缓存!

虽然二级缓存默认是开启的,但是在开发中如果要用,还需要显式的说明,需要注意的是这里的二级缓存默认开始,并不是真正的开启,如果要真正的开启需要在对应的SQL映射文件中添加一行,如下所示

先看没有开启二级缓存的情况,如下,发现两个sqlsession会话的mapper进行查询的时候,总共进行了两次查询

那么如果此时要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

但是你会发现还是查询了两次,原因是如下所示,因为此时二级缓存还没进行缓存,二级缓存的前提条件就是需要一个sqlsession进行关闭,mapper才会将缓存存入二级缓存中,最后在查询的时候才会查询二级缓存!

此时的结果就只需要一次即可!

缓存原理

自定义缓存

除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

<cache type="com.domain.something.MyCustomCache"/>

这个示例展示了如何使用一个自定义的缓存实现。type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。 这个接口是MyBatis 框架中许多复杂的接口之一,但是行为却非常简单。

public interface Cache {
  String getId();
  int getSize();
  void putObject(Object key, Object value);
  Object getObject(Object key);
  boolean hasKey(Object key);
  Object removeObject(Object key);
  void clear();
}
posted @ 2021-05-05 23:14  zpchcbd  阅读(321)  评论(0)    收藏  举报