【Mybatis】Mybatis基础(下)

目录

1、动态SQL

1.1、搭建环境

1.2、if

1.3、trim、where、set

1.3.1、Where

1.3.2、Trim

1.3.3、Set

1.4、choose、when、otherwise

1.5、foreach

2、缓存(了解即可)

2.1、Mybatis缓存

2.2、一级缓存

2.3、二级缓存


1、动态SQL

什么是动态SQL:动态SQL就是根据不同条件生成相应的SQL语句

元素种类:

  • if

  • choose(where,set)

  • trim(where,set)

  • foreach

1.1、搭建环境

  1. 创建一张blog博客表

CREATE TABLE `blogs` (
  `blogid` char(32) NOT NULL COMMENT '博客id',
  `title` varchar(100) DEFAULT NULL COMMENT '博客标题',
  `author` varchar(50) DEFAULT NULL COMMENT '作者',
  `type` varchar(50) DEFAULT NULL COMMENT '博客类型',
  `publishDateTime` datetime DEFAULT NULL COMMENT '出版日期',
  `content` varchar(300) DEFAULT NULL COMMENT '内容',
  `isDisplay` char(1) DEFAULT NULL COMMENT '是否显示',
  `readCount` int(11) DEFAULT NULL COMMENT '阅读量',
  `image` varchar(100) DEFAULT NULL COMMENT '图片',
  PRIMARY KEY (`blogid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

数据库里面自己随机插入数据就行,推荐使用com.github.javafaker.Faker这个去导数据,这里不做详细介绍。

  1. 创建Mybatis基础工程

    1. 导包(跟前面一样)

    2. 编写配置文件(db.properties和mybatis-config.xml配置文件基本不变)

    注意:

    db.properties中的数据库名要改成当前数据库名

  2. mybatis-config.xml中要修改mapper映射器

    1. 编写实体类

    2. @Data
      public class blog {
          private String id;
          private String title;
          private String auther;
          private String type;
          private Date publishDateTime;
          private String content;
          private String isDisplay;
          private int redCount;
          private String imge;
      }
    3. 编写工具类

    //SqlSessionFactory --> SqlSession
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static{
            try {
                //使用mybatis第一步:获取sqlSessionFactory对象
                String resource = "mybatis-config.xml";
                InputStream inputStream;
                inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    ​
        //有了sqlSessionFactory就可以获取sqlSession的实例
        //sqlSession完全包含了面向数据库执行SQL命令所需的方法
        public static SqlSession getsqlSession(){
            return sqlSessionFactory.openSession(true);
        }
    }
    1. 编写实体类对应的Mapper接口和Mapper.xml文件

    2. 结构

1.2、if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如查询名为斯蒂芬且博客题目不为空的博客信息:

1.在BlogMapper接口中定义查询方法:

public interface BlogMapper {
    //搜索斯蒂芬作者是否含有标题为123的博客
    //对应的SQL语句:select * from blogs where author = '斯蒂芬' and title like '123';
    public blog selectBlogif(String title);
}

2.在BlogMapper.xml中写对应的SQL语句以及if语句

<!--在这里添加了一个解决java实体类中的变量名称中数据库名不同的问题,前面有讲过
,记得在mybatis-config.xml中添加别名配置typeAliases
-->
<resultMap id="blogResultMap" type="blog">
    <result property="id" column="blogid"/>
</resultMap>
​
<select id="selectBlog" resultMap="blogResultMap">
    select * from blogs
    where author = '斯蒂芬'
    <if test="title != null">
        and title like #{title}
    </if>
</select>

3.在测试类Test中测试

@Test
public void selectBlog(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    mapper.selectBlogif("123");
    sqlSession.close();
}

4.测试结果

1.title为空的情况

Opening JDBC Connection
    Created connection 1313532469.
    ==>  Preparing: select * from blogs where author = '斯蒂芬' and title like ? 
        ==> Parameters: (String)
<==      Total: 0
    Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4e4aea35]
    Returned connection 1313532469 to pool.

2.title为123(该博客标题)的情况

Opening JDBC Connection
    Created connection 1313532469.
    ==>  Preparing: select * from blogs where author = '斯蒂芬' and title like ? 
        ==> Parameters: 123(String)
            <==    Columns: blogid, title, author, type, publishDateTime, content, isDisplay, readCount, image
                <==        Row: e9118e01ed3d4fc49aa9cb448185df11, 123, 斯蒂芬, software, 2021-11-09 06:36:09.0, wrewqefasdfsafsadf, 1, 242, images/small-image.jpg
                    <==      Total: 1
                        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4e4aea35]
                        Returned connection 1313532469 to pool.

这条if语句提供了可选的查找文本功能。如果不传入 “title”,那么所有author为“斯蒂芬”的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。

如果希望通过 “title” 和 “type” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。

接口定义方法:

//搜索斯蒂芬作者是否software类型且标题为123的博客
//对应的SQL语句:select * from blogs where author = '斯蒂芬' and type = 'software' and title like '123';
public List<blog> selectBlogif(Map map);//使用map参数方法写的

Mapper的SQL实现:

<select id="selectBlogif" parameterType="map" resultMap="blogResultMap">
    select * from blogs
    where author = '斯蒂芬'
    <if test="type != null">
        and type like #{type}
    </if>
    <if test="title != null">
        and title like #{title}
    </if>
</select>

测试:

@Test
public void selectBlogif(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("type","software");
    map.put("title","123");
    List<blog> blogs = mapper.selectBlogif(map);
    System.out.println(blogs);
    sqlSession.close();
}

结果:

Opening JDBC Connection
    Created connection 849373393.
    ==>  Preparing: select * from blogs where author = '斯蒂芬' and type like ? and title like ? 
        ==> Parameters: software(String), 123(String)
            <==    Columns: blogid, title, author, type, publishDateTime, content, isDisplay, readCount, image
                <==        Row: e9118e01ed3d4fc49aa9cb448185df11, 123, 斯蒂芬, software, 2021-11-09 06:36:09.0, wrewqefasdfsafsadf, 1, 242, images/small-image.jpg
                    <==      Total: 1
                        [blog(id=e9118e01ed3d4fc49aa9cb448185df11, title=123, author=斯蒂芬, type=software, publishDateTime=Tue Nov 09 06:36:09 CST 2021, content=wrewqefasdfsafsadf, isDisplay=1, redCount=0, imge=null)]
                        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@32a068d1]
                        Returned connection 849373393 to pool.

1.3、trim、where、set

这次我们将 “author= ‘斯蒂芬’” 设置成动态条件,看看会发生什么。

<select id="selectBlogif" parameterType="map" resultMap="blogResultMap">
    select * from blogs
    where   <!--在这个地方缺失了一个true,导致无法连接下一句语句-->
    <if test="author != null">
        and author = '斯蒂芬'
    </if>
    <if test="type != null">
        and type like #{type}
    </if>
    <if test="title != null">
        and title like #{title}
    </if>
</select>

结果为:

Opening JDBC Connection
    Created connection 849373393.
    ==>  Preparing: select * from blogs where and type like ? and title like ? 
        ==> Parameters: software(String), 123(String)

可以看到,SQL语句变成了select * from blogs where and type like ? and title like ?

正常的应该是select * from blogs where 1=1 and ... and ...,需要一个恒等式来连接SQL语句,但每次这样写有点麻烦,就用到了where元素

1.3.1、Where

可以将上面的SQL改正:

<select id="selectBlogif" parameterType="map" resultMap="blogResultMap">
    select * from blogs
    <where>
        <if test="author != null">
            and author = '斯蒂芬'
        </if>
        <if test="type != null">
            and type like #{type}
        </if>
        <if test="title != null">
            and title like #{title}
        </if>
    </where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。如果传递的值为空,则where后面的SQL语句会自动删除。

1.3.2、Trim

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="where" prefixOverrides="and | or">
    <if test="author != null">-->
        and author = '斯蒂芬'
    </if>
    <if test="type != null">
        and type like #{type}
    </if>
    <if test="title != null">
        and title like #{title}
    </if>
</trim>

上述例子会移除所有 prefixOverrides 属性中指定的内容(第一个and或者or),并且插入 prefix 属性中指定的内容。

1.3.3、Set

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的),一定要加逗号,不加要出错。

<update id="updateBlog" parameterType="map">
    update blogs
    <set>
        <if test="title != null">title=#{title},</if>
        <if test="author != null">author=#{author}</if>
    </set>
    where blogid=#{id}
</update>

结果:

Opening JDBC Connection
    Created connection 1891546521.
    ==>  Preparing: update blogs SET title=?, author=? where blogid=? 
        ==> Parameters: 已修改这个标题(String), 被修改的作者(String), 0cfc526c437c4055b542b0e3e302d787(String)
            <==    Updates: 1
                Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@70beb599]
                Returned connection 1891546521 to pool.

set 元素等价的自定义 trim 元素

<update id="updateBlog" parameterType="map">
    update blogs
    <trim prefix="set" prefixOverrides=",">
        <if test="title != null">title=#{title},</if>
        <if test="author != null">author=#{author}</if>
    </trim>
    where blogid=#{id}
</update>

1.4、choose、when、otherwise

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

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回类型为 software 的 BLOG。

<select id="selectBlogChoose" resultMap="blogResultMap">
    select * from blogs
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>
            <otherwise>
                and type = #{type}
            </otherwise>
        </choose>
    </where>
</select>
@Test
public void selectBlogChoose(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    map.put("author","斯蒂芬");
    map.put("type","software");
    //map.put("title","123");
    List<blog> blogs = mapper.selectBlogChoose(map);
    sqlSession.close();
}

结果

Opening JDBC Connection
    Created connection 11902257.
    ==>  Preparing: select * from blogs WHERE author = ? 
        ==> Parameters: 斯蒂芬(String)
            <==    Columns: blogid, title, author, type, publishDateTime, content, isDisplay, readCount, image
                <==        Row: e9118e01ed3d4fc49aa9cb448185df11, 123, 斯蒂芬, software, 2021-11-09 06:36:09.0, wrewqefasdfsafsadf, 1, 242, images/small-image.jpg
                    <==      Total: 1
                        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@b59d31]
                        Returned connection 11902257 to pool.

注意看,select * from blogs WHERE author= ? ,跟的是author语句,而不是第一个的title。如果前面两个都没有的话就会调用otherwise里的语句。

1.5、foreach

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

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。

//查询id 1到3的博客信息,记得修改博客id
public List<blog> selectBlogForeach(Map map);
<!--select * from blogs where id in ('1','2','3')-->
<select id="selectBlogForeach" parameterType="map" resultMap="blogResultMap">
    select * from blogs
    <where>
        <foreach collection="ids" item="id" open="blogid in(" close=")" separator=",">
            #{id}
        </foreach>
    </where>
</select>
Opening JDBC Connection
    Created connection 673186785.
    ==>  Preparing: select * from blogs WHERE blogid in( ? , ? , ? ) 
        ==> Parameters: 1(String), 2(String), 3(String)
            <==    Columns: blogid, title, author, type, publishDateTime, content, isDisplay, readCount, image
                <==        Row: 1, 已修改这个标题, 被修改的作者, 交朋友, 2019-06-04 06:44:57.0, Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s., 1, 356, images/small-image.jpg
                    <==        Row: 2, Waiting for the Barbarians, Pearl E. Whites, 计算机, 2018-09-19 21:26:30.0, Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s., 1, 160, images/small-image.jpg
                        <==        Row: 3, The Soldier's Art, Walter Melon, 学习, 2018-02-07 05:17:20.0, Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s., 1, 962, images/small-image.jpg
                            <==      Total: 3
                                Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@282003e1]

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

2、缓存(了解即可)

缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。跟Hibernate 一样,MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口。

什么是缓存(Cache)

  • 存在内存中的历史数据

  • 将用户经常查询的数据放在缓存中(内存),用户去查询数据就不再是从磁盘上查询而是从缓存中查询,提高了查询效率,解决高并发系统的性能问题

优点:减少数据库的交互次数,减少系统开销,提高系统效率

注意:建议经常查询且不常改变的数据可以使用缓存

2.1、Mybatis缓存

  • Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存,缓存可以极大地提升查询效率

  • Mybatis系统中默认定义了两级缓存:一级缓存二级缓存

    • -默认情况下,只有一级缓存开启(SqlSession级别的缓存,也叫本地缓存)

    • 二级缓存需要手动开启和配置,是基于namespace级别的缓存

    • 为提高扩展性,Mybatis定义缓存接口Cache。可以通过实现Cache接口来自定义二级缓存

2.2、一级缓存

一级缓存也叫本地缓存,在sqlSession的创建到close这个间段。

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中

  • 以后要获取相同的数据的话,可以直接在缓存中拿

步骤:

  1. 开启日志(前面讲过)

<!--日志-->
<settings>
    <setting name="logImpl" value="Stdout_Logging"/>
</settings>
  1. 在一个session中查询两次信息

@Test
public void selectBlog(){
    SqlSession sqlSession = MybatisUtils.getsqlSession();
    BlogMapper mapper1 = sqlSession.getMapper(BlogMapper.class);
    blog blog1 = mapper1.selectBlog("123");
​
    BlogMapper mapper2 = sqlSession.getMapper(BlogMapper.class);
    blog blog2 = mapper2.selectBlog("123");
    System.out.println("================");
    System.out.println(blog1 == blog2);
    sqlSession.close();
}
  1. 查看日志

Opening JDBC Connection
    Created connection 297927961.
    ==>  Preparing: select * from blogs where author = '斯蒂芬' and title like ? 
        ==> Parameters: 123(String)
            <==    Columns: blogid, title, author, type, publishDateTime, content, isDisplay, readCount, image
                <==        Row: e9118e01ed3d4fc49aa9cb448185df11, 123, 斯蒂芬, software, 2021-11-09 06:36:09.0, wrewqefasdfsafsadf, 1, 242, images/small-image.jpg
                    <==      Total: 1
                        ================
                        true
                        Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@11c20519]

可以看到值查询了一次,且返回值相等

缓存失效的情况:

  1. 查询不同的东西

  2. 增删改操作,因为会改变原来的数据库数据,所以缓存会刷新

  3. 查询不同的Mapper.xml

  4. 手动清除缓存:sqlSession.clearCache();

2.3、二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低,所以有了二级缓存

  • 基于namepace级别的缓存,一个名称空间,对应一个二级缓存

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中

    • 如果当前会话关闭,这个会话对应的以及缓存就会消失

    • 新的会话查询信息,就可以从二级缓存中回去内容

    • 不同的mapper查出的数据会放在自己对应的缓存(map)中

猜你喜欢

转载自blog.csdn.net/m0_46313726/article/details/121571635