MyBatis —— 动态SQL和缓存

前言

在上一篇文章中荔枝梳理了一些特殊的SQL查询和一对多、多对一的映射关系,而在这篇文章中荔枝将会梳理有关MyBatis动态SQL和MyBatis缓存的相关知识,同时也稍微了解了有关MyBatis中借助MAVEN中的插件管理来实现逆向工程。希望对需要的小伙伴有帮助哈哈哈~~~


文章目录

目录

一、动态SQL

[1.1 if标签](#1.1 if标签)

[1.2 where标签](#1.2 where标签)

[1.3 trim标签](#1.3 trim标签)

[1.4 choose、when、otherwise标签](#1.4 choose、when、otherwise标签)

[1.5 foreach标签](#1.5 foreach标签)

[1.5 sql标签](#1.5 sql标签)

二、MyBatis的缓存

[2.1 一级缓存](#2.1 一级缓存)

[2.2 二级缓存](#2.2 二级缓存)

[2.3 MyBatis缓存查询顺序](#2.3 MyBatis缓存查询顺序)

三、MyBatis的逆向工程(MBG)

总结


一、动态SQL

Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的问题。在执行SQL查询时,只有属性值存在我们才能做相应的SQL语句的拼接,因此我们需要动态来拼装SQL语句。在JDBC中我们根据不同的条件拼接SQL语句时往往需要注意一些预留处理,而MyBatis的动态SQL特性就极大地简化了这一处理过程。

1.1 if标签

根据if标签中的test属性所对应的表达式决定标签中的内容是否需要拼接到SQL中。

XML 复制代码
<?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.crj.mybatis.mapper.DynamicSQLMapper">

<!--    List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp where 1=1
        <if test="empName != null and empName != ''">
            and emp_name = #{empName}
        </if>
        <if test="age != null and age != ''">
            and age = #{age}
        </if>
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
    </select>
</mapper>

这段实例demo中,我们需要在select语句中添加一个恒成立的条件1=1,这是为了防止SQL语句在某些属性不存在时出现的拼接错误。动态SQL的特性其实类似一种按需加载的过程,弹性拼接起SQL语句。if标签中的test属性其实就是if标签中的SQL语句段拼接的条件,满足这个条件才能拼接在原有的查询SQL语句中。

1.2 where标签

where标签可以辅助我们动态生成SQL语句中的where关键字,当where后面有内容时,where标签会帮助我们自动生成where关键字,同时会去掉内容前多余的and和or关键字,此时就无需我们手动添加where关键字和恒成立的条件了;当where后面没有内容时,此时不会生成where关键字。

XML 复制代码
<!--    List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <where>
            <if test="empName != null and empName != ''">
                and emp_name = #{empName}
            </if>
            <if test="age != null and age != ''">
                and age = #{age}
            </if>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
        </where>
    </select>

1.3 trim标签

若标签中有内容时,属性功能正常;若标签没有内容时, trim不会有任何效果。

几个属性:

prefix: 将trim标签中内容前面添加指定内容

suffix: 将trim标签中内容后面添加指定内容

prefixOverrides: 将trim标签中内容前面去掉指定内容

suffixOverrides: 将trim标签中内容后面去掉指定内容

XML 复制代码
<!--    List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <trim prefix="where" suffixOverrides="and|or">
            <if test="empName != null and empName != ''">
                 emp_name = #{empName} and
            </if>
            <if test="age != null and age != ''">
                age = #{age} and
            </if>
            <if test="sex != null and sex != ''">
               sex = #{sex}
            </if>
        </trim>
    </select>

1.4 choose、when、otherwise标签

choose和when标签组合起来相当于if...else if...else的结构,如果所有的when条件都不满足,则会执行otherwise中的条件。

XML 复制代码
<!--    List<Emp> getEmpByChoose(Emp emp);-->
    <select id="getEmpByChoose" resultType="Emp">
        select * from t_emp
        <where>
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name = #{empName}
                </when>
                <when test="age != null and age != ''">
                    age = #{age}
                </when>
                <when test="sex != null and sex != ''">
                    sex = #{sex}
                </when>
                <otherwise>
                    did = 1
                </otherwise>
            </choose>
        </where>
    </select>

1.5 foreach标签

foreach标签简单来说就是一个循环,可以被用来执行批量操作,foreach标签有五种属性:

  • collection:设置需要循环的数组或集合
  • item:循环出来的单个对象
  • sqparator:循环体之间的分隔符
  • open:循环的所有内容开头的内容
  • close:循环的所有内容结束的内容
XML 复制代码
<!--    int deleteMoreByArray(Integer[] eids);-->
    <delete id="deleteMoreByArray">
        delete * from t_emp where eid in
        <foreach collection="eids" item="eid" separator="," open="(" close=")">
            #{eid}
        </foreach>
    </delete>

1.5 sql标签

sql标签又成为SQL片段,它可以用来记录常用的sql执行语句。通过include标签来引用SQL片段。

XML 复制代码
    <sql id="emColumns">eid,emp_name,age,sex,email</sql>
    <select id="getEmpByChoose" resultType="Emp">
        select <include refid="emColumns"></include> from t_emp      
    </select>

二、MyBatis的缓存

2.1 一级缓存

MyBatis的一级缓存是SqlSession级别 的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据就会从缓存中直接获取而不会从数据库中重新访问。一级缓存是默认开启的

一级缓存失效的四种情况:

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSessioni两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

手动清空缓存:

java 复制代码
sqlSession.clearCache();

2.2 二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSessioni查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。

二级缓存开启的条件:

  1. 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
  2. 在映射文件中设置标签<cache />
  3. 二级缓存必须在SqlSession关闭或提交之后有效sqlSession.close() | sqlSession.commit()
  4. 查询的数据所转换的实体类类型必须实现序列化的接口

实体类实现序列化接口

java 复制代码
public class Emp implements Serializable 

二级缓存失效的情况:

两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效。

二级缓存的相关配置

在二级缓存的开启条件中,我们发现<cache />标签是可以配置一些属性的,具体如下

eviction属性:缓存回收策略

  • LRU(Least Recently Used)-最近最少使用的:移除最长时间不被使用的对象。
  • FIFO(First in First out)-先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT-软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK-弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

默认的是LRU。

flushlnterval属性:刷新间隔,单位毫秒

默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用增删改语句时刷新

size属性:引用数目,正整数

代表缓存最多可以存储多少个对象,太大容易导致内存溢出

readOnly属性:只读,true/false

true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。

false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是flse。

2.3 MyBatis缓存查询顺序

首先先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。如果二级缓存没有命中,再查询一级缓存;如果一级缓存也没有命中,则查询数据库。注意在sqlSession关闭之后,一级缓存中的数据会写入二级缓存


三、MyBatis的逆向工程(MBG)

正向工程 :先创建ava实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:

  • Java实体类
  • Mapper接口
  • MapperB映射文件

在使用逆向工程之前首先应该配置好项目依赖

pom.xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.crj</groupId>
    <artifactId>MyBatis_MBG</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

    <!--依赖MyBatis核心包-->
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.3</version>
        </dependency>

        <!--log4j日志-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

    <!--控制Maven在构建过程中相关配置-->
    <build>
        <!--构建过程用到的插件-->
        <plugins>
            <!--具体插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>
                <!--插件的依赖-->
                <dependencies>
                    <!--逆向工程的核心依赖-->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>

                    <!--数据库连接池c3p0-->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>
                    <!--mysql驱动-->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.8</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

逆向工程配置文件:

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
        targetRuntime:执行生成的逆向工程的版本
            MyBatis3Simple:生成基本的CRUD
            MyBatis:生成带条件的CRUD
    -->

    <!-- 是否去除自动生成的注释 true:是 : false:否 -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!-- 数据库连接 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <!--
            javaBean的生成策略
            生成model模型,对应的包路径,以及文件存放路径(targetProject),targetProject可以指定具体的路径
            也可以使用"MAVEN"来自动生成,这样生成的代码会在target/generatord-source目录下
        -->
        <javaModelGenerator targetPackage="com.crj.mybatis.pojo" targetProject=".\src\main\java">
            <!--是否能够使用子包-->
            <property name="enableSubPackages" value="true" />
            <!-- 从数据库返回的值被清理前后的空格  -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <!--
            SQL映射文件的生成策略
            对应的mapper.xml文件
        -->
        <sqlMapGenerator targetPackage="com.crj.mybatis.mapper" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!-- 对应的Mapper接口类文件的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.crj.mybatis.mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!-- 逆向分析的表 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>

    </context>
</generatorConfiguration>

测试类中我们这样来进行条件查询:

java 复制代码
EmpExample example = new EmpExample();
//这里可以借助example.createCriteria().and开头的方法来设置查询条件
example.createCriteria().andEmpNameEqualTo("荔枝");
mapper.selectByExample(example);

总结

嘿哈!MyBatis的相关知识学习也就大致到这里啦,荔枝接下来会继续学习mall项目,希望九月份能遇见不一样的自己吧哈哈哈哈。最近看丙哥的文章嘿嘿,分享给大家一句话:习惯自律,享受孤独~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

相关推荐
陈平安Java and C2 小时前
MyBatisPlus
java
秋野酱3 小时前
如何在 Spring Boot 中实现自定义属性
java·数据库·spring boot
Bunny02123 小时前
SpringMVC笔记
java·redis·笔记
feng_blog66884 小时前
【docker-1】快速入门docker
java·docker·eureka
枫叶落雨2225 小时前
04JavaWeb——Maven-SpringBootWeb入门
java·maven
m0_748232395 小时前
SpringMVC新版本踩坑[已解决]
java
码农小灰5 小时前
Spring MVC中HandlerInterceptor和Filter的区别
java·spring·mvc
乔木剑衣6 小时前
Java集合学习:HashMap的原理
java·学习·哈希算法·集合
专职7 小时前
spring boot中实现手动分页
java·spring boot·后端
神探阿航7 小时前
第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
java·算法·蓝桥杯