前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你好MyBatis 高级篇

你好MyBatis 高级篇

作者头像
止术
发布2020-09-15 10:05:57
3180
发布2020-09-15 10:05:57
举报
文章被收录于专栏:求道求道
MyBatis缓存
一级缓存

首先了解什么叫缓存,缓存可以看作是一种程序的优化机制,拿查询来说,第一次查询,会访问数据库,查询到结果之后,先将结果保存在内存中,然后在返回结果;第二次执行相同的sql时,先判断内存中是否有此查询的结果,有就直接返回不访问数据库;没有再去访问数据库;这样就保证了程序访问数据库的次数减少,防止了数据库压力过大导致崩溃。

MyBatis中的缓存是默认开启的无法关闭,当然只是单指MyBatis单独存在的情况下,后期涉及到MyBatis整合Spring,Spring会将一级缓存关闭掉,当然这是后话!但只有MyBatis的情况下,一级缓存是无法关闭的!

一级缓存的作用范围

一级缓存的作用范围是同一个session同一个sql相同的参数

当我们执行一个查询语句两次时,我们看日志会是什么情况

代码语言:javascript
复制
    @Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        List<Book> books1 = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        for (Book book : books) {
            System.out.println(book);
        }
    }

此时我们查看日志

发现,我们执行了两次查询,但是只执行了一次sql语句,为了证明缓存的存在,我们在执行一遍查询之后将缓存清除掉,在去查看日志!sqlSession.clearCache();是清除缓存

代码语言:javascript
复制
    @Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        //清除缓存
        sqlSession.clearCache();
        List<Book> books1 = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        for (Book book : books) {
            System.out.println(book);
        }
    }

日志如下

我们可以很清楚的看到,缓存清除之后,sql语句执行了两边;所以能够证明一级缓存默认开启,且无法关闭,能够优化查询效率。但是注意一点哦执行修改操作会清空缓存哦!例如 :update insert delete等

二级缓存

二级缓存的作用范围是:

一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。

开启二级缓存的方法是在Mapper文件里面加一个标签 Mapper文件如下

代码语言:javascript
复制
<?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.senior.dao.BookDao">
    <cache/>
</mapper>

完整Mapper

代码语言:javascript
复制
<?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.senior.dao.BookDao">
    <cache/>
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select * from book
        <where>
            <if test="author!=null">
                author = #{author}
            </if>
            <if test="name!=null">
              and name = #{name}
            </if>
        </where>
    </select>

    <update id="updateBook" parameterType="com.senior.entity.Book">
        update book
        <set>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="author!=null">
                author = #{author},
            </if>
            <if test="createDate!=null">
                createdate = #{createDate},
            </if>
            <if test="status !=null">
                status = #{status},
            </if>
            <if test="clazzId !=null">
                clazzid = #{clazzId}
            </if>
        </set>
        where id=#{id}
    </update>
    <select id="findBookByIds" resultType="com.senior.entity.Book">
        select * from book where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>
</mapper>

测试观看日志;为了验证二级缓存的存在,咱们查询一次之后把Session给关闭掉,在创建一个新的SqlSession看它的日志输出情况!

代码语言:javascript
复制
    @Test
    public void testQueryBookByAuthorOrName(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<Book> books = bd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);
        //关闭sqlSession
        sqlSession.close();
        //创建一个新的SqlSession
        SqlSession newSqlSession = MyBatisUtil.openSqlSession();
        BookDao newDd = newSqlSession.getMapper(BookDao.class);
        List<Book> books1 = newDd.queryBookByAuthorOrName("皇甫嗷嗷叫", null);

        for (Book book : books) {
            System.out.println(book);
        }
    }

查看日志

可以看出Sql只执行了一次,由第二个圈着的地方:是计算的缓存命中率,命中率为0.5,执行了两次sql,在缓存中只查了一次,所以它的命中率为0.5,也很直接的证明了二级缓存在起作用。

关闭二级缓存

1.不开启 二级缓存默认关闭 2.在全局配置文件中配置,全局配置文件一旦禁用二级缓存,Mapper开启二级缓存也没用,Mapper配置文件的配置如下:

代码语言:javascript
复制
    <settings>
        <setting name="cacheEnabled" value="false"/>
    </settings>

完整配置文件 注意顺序

代码语言:javascript
复制
<?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>
    <properties resource="properties/db.properties">
        <!--开启配置文件配置默认值-->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    </properties>

    <settings>
        <setting name="cacheEnabled" value="false"/>
    </settings>
    <!--配置默认使用那个环境变量  以id作为标志-->
    <environments default="test">
        <!--配置连接环境-->
        <environment id="test">
            <!--事务管理器使用JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据源   连接池使用该POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver:com.mysql.jdbc.Driver}"/>
                <property name="url" value="${mysql.url:jdbc:mysql://127.0.0.1:3306/testmybatis}"/>
                <property name="username" value="${mysql.username:root}"/>
                <property name="password" value="${mysql.password:hr}"/>
            </dataSource>
        </environment>
    </environments>
    <!--将Mapper文件在这里注册-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
        <mapper resource="mapper/StudentMapper.xml"/>
        <mapper resource="mapper/BookMapper.xml"/>
    </mappers>
</configuration>

其中可以配置缓存策略

多表查询 多表映射

在我们使用Sql进行查询时,难免有时候会遇到表字段与实体类属性不相同的情况,我们可以这样解决! select id as 你的属性名

代码语言:javascript
复制
    <select id="queryBookByAuthorOrName" resultType="com.senior.entity.Book">
        select id as `你的属性名`,name,author from book
        <where>
            <if test="author!=null">
                author = #{author}
            </if>
            <if test="name!=null">
              and name = #{name}
            </if>
        </where>
    </select>

这样做只适用于单表查询,如果时多表查询你应该怎么搞?此时就需要用到resultMap属性了 我们先创建一个类别表,以便下面进行测试

代码语言:javascript
复制
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `bookclazz`;
CREATE TABLE `bookclazz`  (
  `id` int(7) NOT NULL,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;


INSERT INTO `bookclazz` VALUES (1, '国外名著');
INSERT INTO `bookclazz` VALUES (2, '四大名著');
INSERT INTO `bookclazz` VALUES (3, 'MyBatsi');

SET FOREIGN_KEY_CHECKS = 1;

此时如果有个需求:查询一个类别下的所有书籍,此时就应该会用到表连接,其sql应该这样写!

代码语言:javascript
复制
SELECT c.`name`,b.`name`,b.createdate,b.author FROM book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id 

查询结果是这样

哪映射到Mapper文件上应该怎么写呢?此时我们应该去回想一下JAVA面向对象的思想,试想,一个类别下应该有多个书籍,所以类别实体类下应该有个Book类型的集合!一本书应该只能属于一个类别,所以书籍实体类下应该会有一个类别实体类对象,具体实体类设计应该这样搞!

代码语言:javascript
复制
package com.senior.entity;

import java.util.Date;

/**
 * 书籍实体类
 * @author 皇甫
 */
public class NewBook {
    private Integer id;
    private String name;
    private String author;
    private Date createDate;
    private Integer status;
    /**
     * 一本书一定属于一个类别,所以应该有个类别对象
     */
    private BookClazz bookClazz;

    @Override
    public String toString() {
        return "NewBook{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", createDate=" + createDate +
                ", status=" + status +
                ", bookClazz=" + bookClazz +
                '}';
    }

    public Integer getId() {

        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public BookClazz getBookClazz() {
        return bookClazz;
    }

    public void setBookClazz(BookClazz bookClazz) {
        this.bookClazz = bookClazz;
    }

    public NewBook(Integer id, String name, String author, Date createDate, Integer status, BookClazz bookClazz) {
        this.id = id;
        this.name = name;
        this.author = author;
        this.createDate = createDate;
        this.status = status;
        this.bookClazz = bookClazz;
    }

    public NewBook() {
    }
}
代码语言:javascript
复制
package com.senior.entity;

import java.util.ArrayList;
import java.util.List;

/**
 * 类别实体类
 * @author 皇甫
 */
public class BookClazz {
    private Integer id;
    private String name;
    /**
     * 每一个类别下都有很多本书籍,故而使用集合存放
     */
    private List<Book> books = new ArrayList<Book>();

    @Override
    public String toString() {
        return "BookClazz{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", books=" + books +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }

    public BookClazz(Integer id, String name, List<Book> books) {
        this.id = id;
        this.name = name;
        this.books = books;
    }

    public BookClazz() {
    }
}

编写DAO接口类

代码语言:javascript
复制
package com.senior.dao;

import com.senior.entity.BookClazz;

import java.util.List;

/**
 * @author 皇甫
 */
public interface BookClazzDao {
    /**
     * 查询所有
     * @return
     */
    public List<BookClazz> queryAllBookClazz();
}

编写Mapper文件

代码语言:javascript
复制
<?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.senior.dao.BookClazzDao">
    <!--id 随便起 type你要返回的类型  这里返回的是书籍类别-->
    <resultMap id="queryAllBookClazzMap" type="com.senior.entity.BookClazz">
        <!--column 为你数据库字段的名字 或as重名后的名字 property为对应的实体类属性的名字-->
        <!--id 为主键映射  result为普通字段映射-->
        <id property="id" column="c_id"/>
        <result property="name" column="c_name"/>
        <!--collection 代表你要映射的是实体类里面的集合属性-->
        <!--property 实体类里面集合类的名字-->
        <!--javaType 集合类的泛型种类-->
        <collection property="books" javaType="com.senior.entity.Book">
            <id property="id" column="b_id"/>
            <result property="name" column="b_name"/>
            <result property="createDate" column="b_date"/>
            <result property="author" column="b_author"/>
        </collection>
    </resultMap>
    <!--id 对应方法的名字  resultMap对应resultMap定义的id值-->
    <select id="queryAllBookClazz" resultMap="queryAllBookClazzMap">
        SELECT
          c.id as c_id,
          c.`name` as c_name,
          b.id as b_id,
          b.`name` as b_name,
          b.createdate b_date,
          b.author  b_author
        FROM
          book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id
    </select>
</mapper>

测试

代码语言:javascript
复制
    @Test
    public void testQueryAllBookClazz(){
        BookClazzDao bc = sqlSession.getMapper(BookClazzDao.class);
        List<BookClazz> bookClazzes = bc.queryAllBookClazz();
        for (BookClazz bookClazz : bookClazzes) {
            System.out.println("------------------"+bookClazz.getName()+"------------------");
            for (Book book : bookClazz.getBooks()) {
                System.out.println(book);
            }
        }
    }

结果

代码语言:javascript
复制
------------------国外名著------------------
Book{id=1, name='鲁宾孙漂流记', author='丹尼尔.迪福', createDate=Sun Feb 24 14:38:58 CST 2019, status=null, clazzId=null}
Book{id=7, name='简爱', author='夏洛蒂.勃朗特', createDate=Thu Feb 14 14:41:11 CST 2019, status=null, clazzId=null}
------------------四大名著------------------
Book{id=3, name='水浒传', author='施耐庵', createDate=Fri Feb 15 14:39:44 CST 2019, status=null, clazzId=null}
Book{id=6, name='聊斋志异', author='蒲松龄', createDate=Tue Jan 29 14:40:56 CST 2019, status=null, clazzId=null}
Book{id=2, name='红楼梦', author='曹雪芹', createDate=Tue Feb 12 14:39:27 CST 2019, status=null, clazzId=null}
Book{id=5, name='西游记', author='吴承恩', createDate=Wed Jan 30 14:40:39 CST 2019, status=null, clazzId=null}
Book{id=4, name='三国演义', author='罗贯中', createDate=Sun Feb 24 14:40:13 CST 2019, status=null, clazzId=null}
------------------MyBatsi------------------
Book{id=9, name='MyBatis中级篇章', author='皇甫嗷嗷叫', createDate=Sun Feb 24 14:55:34 CST 2019, status=null, clazzId=null}
Book{id=8, name='MyBatis入门篇章', author='皇甫嗷嗷叫', createDate=Sun Feb 24 14:54:59 CST 2019, status=null, clazzId=null}
Book{id=10, name='MyBatis高级篇章', author='皇甫嗷嗷叫', createDate=Sun Feb 24 14:56:01 CST 2019, status=null, clazzId=null}

那么当我想查询一个书属于那个类别应该如何去查呢? dao接口

代码语言:javascript
复制
    /**
     * 查询所有书籍以及类别
     * @return
     */
    public List<NewBook> findBookAndBookClazz();

Mapper

代码语言:javascript
复制
<?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.senior.dao.BookDao">
    <cache/>
    <resultMap id="findBookAndBookClazzMap" type="com.senior.entity.NewBook">
        <id property="id" column="b_id"/>
        <result property="name" column="b_name"/>
        <result property="createDate" column="b_date"/>
        <result property="author" column="b_author"/>
        <!--association 为对象类型不是集合类型-->
        <association property="bookClazz" javaType="com.senior.entity.BookClazz">
            <id property="id" column="c_id"/>
            <result property="name" column="c_name"/>
        </association>
    </resultMap>


    <select id="findBookAndBookClazz" resultMap="findBookAndBookClazzMap">
        SELECT
          c.id as c_id,
          c.`name` as c_name,
          b.id as b_id,
          b.`name` as b_name,
          b.createdate b_date,
          b.author  b_author
        FROM
          book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id
    </select>
</mapper>

测试

代码语言:javascript
复制
    @Test
    public void testFindBookAndBookClazz(){
        BookDao bd = sqlSession.getMapper(BookDao.class);
        List<NewBook> books = bd.findBookAndBookClazz();
        for (NewBook book : books) {
            System.out.println(book.getName()+"--类别:"+book.getBookClazz().getName());
        }
    }

结果

代码语言:javascript
复制
鲁宾孙漂流记--类别:国外名著
简爱--类别:国外名著
水浒传--类别:四大名著
聊斋志异--类别:四大名著
红楼梦--类别:四大名著
西游记--类别:四大名著
三国演义--类别:四大名著
MyBatis中级篇章--类别:MyBatsi
MyBatis入门篇章--类别:MyBatsi
MyBatis高级篇章--类别:MyBatsi

我们发现两次查询的SQL是一样的,但是结果却截然不同,这就是Map映射的魅力!

SQL片段

但是,我们能否更简化一点呢?既然一个SQL两个配置文件都用到了 我们能否将SQL提取出来呢

Mapper内的SQL片段提取

代码语言:javascript
复制
<?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.senior.dao.BookClazzDao">
    <!--id 随便起 type你要返回的类型  这里返回的是书籍类别-->
    <resultMap id="queryAllBookClazzMap" type="com.senior.entity.BookClazz">
        <!--column 为你数据库字段的名字 或as重名后的名字 property为对应的实体类属性的名字-->
        <!--id 为主键映射  result为普通字段映射-->
        <id property="id" column="c_id"/>
        <result property="name" column="c_name"/>
        <!--collection 代表你要映射的是实体类里面的集合属性-->
        <!--property 实体类里面集合类的名字-->
        <!--javaType 集合类的泛型种类-->
        <collection property="books" javaType="com.senior.entity.Book">
            <id property="id" column="b_id"/>
            <result property="name" column="b_name"/>
            <result property="createDate" column="b_date"/>
            <result property="author" column="b_author"/>
        </collection>
    </resultMap>

    <!--将SQL片段提提取出来-->
    <sql id="NEWBOOKANDBOOKCLAZZ">
        c.id as c_id,
          c.`name` as c_name,
          b.id as b_id,
          b.`name` as b_name,
          b.createdate b_date,
          b.author  b_author
    </sql>
    <!--id 对应方法的名字  resultMap对应resultMap定义的id值-->
    <select id="queryAllBookClazz" resultMap="queryAllBookClazzMap">
        /*SQL语句内引入SQL片段*/
        SELECT
          <include refid="NEWBOOKANDBOOKCLAZZ"/>
        FROM
          book b INNER JOIN bookclazz c on b.clazzid=c.id ORDER BY c.id
    </select>
</mapper>
查询语句中出现大于号和小于号的解决方案

查询语句中出现大于号和小于号的解决方案

业务需求,求书籍id小于2的数据

代码语言:javascript
复制
select * from book where id>2

Mapper

代码语言:javascript
复制
<!--报错   因为不允许使用`<`-->
<select id="queryBook" resultType="com.senior.entity.Book">
        select * from book where id < 2
    </select>
解决方案1
代码语言:javascript
复制
<select id="queryBook" resultType="com.senior.entity.Book">
        select * from book where id &lt; 2
    </select>
解决方案2
代码语言:javascript
复制
<select id="queryBook" resultType="com.senior.entity.Book">
    <![CDATA[
         select * from book where id < 2
    ]]>      
</select>
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JAVA程序狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MyBatis缓存
    • 一级缓存
      • 二级缓存
        • 关闭二级缓存
        • 多表查询 多表映射
        • SQL片段
        • 查询语句中出现大于号和小于号的解决方案
          • 解决方案1
            • 解决方案2
            相关产品与服务
            数据库
            云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档