动态SQL
MyBatis框架的动态SQL技术时一种根据特定条件动态拼接SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
if标签
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;否则标签的内容不会执行
DynamicSQLMapper.xml
<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="emp">
select * from t_emp where
<if test="empName !=null and empName!=''">
emp_name=#{empName}
</if>
<if test="age!=null and age!=''">
and age=#{age}
</if>
<if test="sex!=null and sex!=''">
and sex=#{sex}
</if>
<if test="email!=null and email!=''">
and email=#{email}
</if>
</select>
测试
但是如果都不成立,此时就多了一个where
可以在SQL语句加一个1=1
但是查属性值都为空的Emp对象却查到了所有的Emp对象(有问题)
使用where标签
where标签
当where标签中有内容时会自动生成where关键字,并将内容前多余的and或or去掉
内容后的不会
当where标签中没有内容时,此时where标签没有任何效果
trim标签
prefix|suffix:将trim标签中内容前面或后面添加指定内容
prefixOverrides|suffixOverrides:将trim标签中内容前面或后面去掉指定内容
若trim标签中没有内容,没有任何效果
choose,when,otherwise标签
相当于if...else if...else
当有when标签中有条件满足时就将标签中内容拼接到sql语句中,后面标签不再判断
传入的emp对象
new Emp(null, "GYQ", 15, "", "")
都不满足,将otherwise标签中内容拼接
传入的emp对象
new Emp(null, "", null, "", "")
foreach标签
批量删除
方式一:
方式二:
批量添加
collection:设置需要循环的数组或集合 item:表示数组或集合中的每一个数据
separator:循环体之间的分隔符 open:foreach标签所循环的所有内容的开始符
close:foreach标签所循环的所有内容的结束符
sql标签
设置SQL片段,引用SQL片段
MyBatis的缓存
一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。默认开启
连续两次执行相同的查询
使一级缓存失效的四种情况:
不同的SqlSession对应不同的一级缓存(获取另一个SqlSession,两者执行相同查询语句)
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存
二级缓存
二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会冲缓存中获取
二级缓存的开启条件
在核心配置文件中,设置全局配置属性cacheEnabled=”true“,默认为true,不需要设置
在映射文件中设置标签<cache/>
二级缓存必须在SqlSession关闭或提交之后有效
查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的清况
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
相关配置
在mapper配置文件中添加的catche标签可以设置一些属性:
MyBaits缓存查询的顺序
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,再查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
整合第三方缓存EHCache
使用第三方技术代替MyBatis的二级缓存
添加依赖
<!--MyBatis EHCache整合包-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!--slf4j日志门面的一个具体实现-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
slf4j:Simple Logging Facade for Java 简单日志门面(不是具体的日志解决方案,它只服务于各种各样的日志系统)
创建EHCache的配置文件ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="D:\ideaworkspace\MyBatis\ehcache"/>
<!--
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
设置二级缓存的类型
加入logback日志
存在
SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现bogback来实现打印日志。
创建logback的配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger]
[%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>
测试:
MyBatis的逆向工程
正向工程:
先创建Java实体类,由框架负责根据实体类生成数据库表,Hiberbate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类,Mapper接口,Mapper映射文件
创建逆向工程的步骤
添加依赖和插件
<dependencies>
<!--MyBatis核心jar包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--Mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--Log4j日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</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>
<!-- 数据库连接池 -->
<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>8.0.27</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
创建jdbc.properties和log4j.xml
创建MyBatis的核心配置文件
<?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="jdbc.properties"></properties>
<!--设置MyBatis的全局配置-->
<settings>
<!--将下划线自动映射为驼峰,emp_name:empName-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<typeAliases>
<package name="com.gyq.mybatis.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.gyq.mybatis.mapper"/>
</mappers>
</configuration>
创建逆向工程的配置文件
文件名为generatorConfig.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(清新简洁版 增删改)
MyBatis3: 生成带条件的CRUD(奢华尊享版 )
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="root">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.gyq.mybatis.pojo" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/><!--去掉空格-->
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.gyq.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.gyq.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
双击
自动生成的代码
👍
奢华版
将上面的配置文件更改 targetRuntime="MyBatis3",将刚才生成的删除后再次生成
在Emp类中生成无参,带参构造器和toString方法,用于测试
测试查询
测试修改
MyBatis的分页插件
使用步骤
添加依赖
<!--分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
配置分页插件
在MyBatis的核心配置文件中配置插件
<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
分页插件的使用
其中pageNum:3表示当前要查的是第3页的数据,pageSize表示每一页的数据条数
Page{count=true, pageNum=3, pageSize=4, startRow=8, endRow=12, total=21, pages=6, reasonable=false, pageSizeZero=false}[Emp{eid=11, empName='a', age=null, sex='null', email='null', did=null}, Emp{eid=12, empName='a', age=null, sex='null', email='null', did=null}, Emp{eid=13, empName='a', age=null, sex='null', email='null', did=null}, Emp{eid=14, empName='a', age=null, sex='null', email='null', did=null}]
navigatePages:3表示当前导航分页的数量,显示第三页时的相关信息
PageInfo{pageNum=3, pageSize=4, size=4, startRow=9, endRow=12, total=21, pages=6, list=Page{count=true, pageNum=3, pageSize=4, startRow=8, endRow=12, total=21, pages=6, reasonable=false, pageSizeZero=false}[Emp{eid=11, empName='a', age=null, sex='null', email='null', did=null}, Emp{eid=12, empName='a', age=null, sex='null', email='null', did=null}, Emp{eid=13, empName='a', age=null, sex='null', email='null', did=null}, Emp{eid=14, empName='a', age=null, sex='null', email='null', did=null}], prePage=2, nextPage=4, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=3, navigateFirstPage=2, navigateLastPage=4, navigatepageNums=[2, 3, 4]}