😤😤😤
动态SQL
为了解决拼接SQL语句字符串时的痛点问题
(🤗让我想起了,寒假的JSTL)
1、if
test属性
: 进行判断
<!--List<Emp> getEmpListByMoreTJ(Emp emp);-->
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from t_emp where 1=1
<if test="ename != '' and ename != null">
and ename = #{ename}
</if>
<if test="age != '' and age != null">
and age = #{age}
</if>
<if test="sex != '' and sex != null">
and sex = #{sex}
</if>
</select>
2、where
where标签能自动添加where关键字,并将条件最前方多余的and
去掉
<select id="getEmpListByMoreTJ2" resultType="Emp">
select * from t_emp
<where>
<if test="ename != '' and ename != null">
ename = #{ename}
</if>
<if test="age != '' and age != null">
and age = #{age}
</if>
<if test="sex != '' and sex != null">
and sex = #{sex}
</if>
</where>
</select>
3、trim
用于添加或去掉标签中的内容
常用属性
prefix
: 再trim标签中的内容的前面添加某些内容
prefixOverrides
: 在trim标签中的内容的前面去掉某些内容
suffix
: 在trim标签中的内容的后面添加某些内容
suffixOverrides
: 在trim标签中的内容的后面去掉某些内容
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and">
<if test="ename != '' and ename != null">
ename = #{ename} and
</if>
<if test="age != '' and age != null">
age = #{age} and
</if>
<if test="sex != '' and sex != null">
sex = #{sex}
</if>
</trim>
</select>
4、choose、when、otherwise
choose、when、otherwise相当于if…else if…else
<!--List<Emp> getEmpListByChoose(Emp emp);-->
<select id="getEmpListByChoose" resultType="Emp">
select <include refid="empColumns"></include> from t_emp
<where>
<choose>
<when test="ename != '' and ename != null">
ename = #{ename}
</when>
<when test="age != '' and age != null">
age = #{age}
</when>
<when test="sex != '' and sex != null">
sex = #{sex}
</when>
<when test="email != '' and email != null">
email = #{email}
</when>
</choose>
</where>
</select>
5、foreach
属性 :
collection
: 设置要循环的数组或集合
item
: 表示集合或数组中的每一个数据
separator
: 设置循环体之间的分隔符
open
: 设置foreach标签的内容的开始符
close
: 设置foreach标签中的内容的结束符
<!--int insertMoreEmp(List<Emp> emps);-->
<insert id="insertMoreEmp">
insert into t_emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.ename},#{emp.age},#{emp.sex},#{emp.email},null)
</foreach>
</insert>
<!--int deleteMoreByArray(int[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where
<foreach collection="eids" item="eid" separator="or">
eid = #{eid}
</foreach>
</delete>
<!--int deleteMoreByArray(int[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>
6、SQL片段
可以记录一段公共sql片段,在使用的地方通过include
标签引入
refid属性
: 通过id引入sql片段
<sql id="empColumns">
eid,ename,age,sex,did
</sql>
select <include refid="empColumns"></include> from t_emp
MyBatis的缓存
1、一级缓存
SqlSession
级别:
通过同一个SqlSession查询 的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
@Test
public void getDeptById(){
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
MyBatis3Mapper mapper = sqlSession.getMapper(MyBatis3Mapper.class);
Dept dept1 = mapper.getDeptById(1);
System.out.println(dept1);
Dept dept2 = mapper.getDeptById(1);
System.out.println(dept2);
}
只执行了一条sql
DEBUG 04-05 08:34:20,941 ==> Preparing: select * from dept where did = ? (BaseJdbcLogger.java:137)
DEBUG 04-05 08:34:20,962 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 04-05 08:34:20,974 <== Total: 1 (BaseJdbcLogger.java:137)
Dept{did=1, deptName='部门1', emps=null}
Dept{did=1, deptName='部门1', emps=null}
使一级缓存失效的四种情况
(1)、不同的SqlSession对应不同的一级缓存
(2)、同一个SqlSession但是查询条件不同
(3)、 同一个SqlSession两次查询期间执行了任何一次增删改操作
(4)、同一个SqlSession的两次查询期间手动清空了缓存
sqlSession.clearCache()
2、二级缓存
SqlSessionFactory
级,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被 缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
@Test
public void getDeptById(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config." + "xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
MyBatis3Mapper mapper1 = sqlSession1.getMapper(MyBatis3Mapper.class);
System.out.println(mapper1.getDeptById(1));
sqlSession1.commit(); // 注意这里提交
MyBatis3Mapper mapper2 = sqlSession2.getMapper(MyBatis3Mapper.class);
System.out.println(mapper2.getDeptById(1));
} catch (IOException e) {
e.printStackTrace();
}
}
只执行了一条sql
DEBUG 04-05 09:06:44,039 Cache Hit Ratio [com.flzj.mapper.MyBatis3Mapper]: 0.0 (LoggingCache.java:60)
DEBUG 04-05 09:06:44,218 ==> Preparing: select * from dept where did = ? (BaseJdbcLogger.java:137)
DEBUG 04-05 09:06:44,241 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 04-05 09:06:44,257 <== Total: 1 (BaseJdbcLogger.java:137)
Dept{did=1, deptName='部门1', emps=null}
DEBUG 04-05 09:06:44,267 Cache Hit Ratio [com.flzj.mapper.MyBatis3Mapper]: 0.5 (LoggingCache.java:60)
Dept{did=1, deptName='部门1', emps=null}
开启条件
1、在核心配置文件中,设置全局配置属性cacheEnabled=true
(默认为true)
2、在映射文件中设置标签<cache>
3、二级缓存必须在SqlSession关闭或提交之后有效
sqlSession.close();
sqlSession.commit();
4、查询的数据转换的实体类型必须实现序列化
的接口
相关的配置
在mapper配置文件中添加cache
标签可以设置一些属性
属性 | 值 | 作用 |
---|---|---|
eviction | LRU(Least Recently Used) FIFO(First in First Out) SOFT(软引用) WEAK(弱引用)(默认是LRU) | 缓存回收策略 |
flushInterval | 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新 | 刷新间隔,单位毫秒 |
size | 引用数目,正整数 | 代表缓存最多可以存储多少个对象,太大容易导致内存溢出 |
readOnly | true: 只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了 很重要的性能优势。 false : 读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。 | 只读 |
使二级缓存失效的情况
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
3、MyBatis缓存查询的顺序
1、先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
2、 如果二级缓存没有命中,再查询一级缓存
3、如果一级缓存也没有命中,则查询数据库
4、SqlSession关闭之后,一级缓存中的数据会写入二级缓存
4、整合第三方缓存EHCache
代替我们的二级缓存
4.1、依赖
<!-- 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>
各jar包功能
jar包名 | 作用 |
---|---|
mybatis-ehcache | Mybatis和EHCache的整合包 |
ehcache | EHCache核心包 |
slf4j-api | SLF4J日志门面包 |
logback-classic | 支持SLF4J门面接口的一个具体实现 |
4.2、创建EHCache的配置文件
ehcache.xml
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\flzj\ehcache"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
下图为缓存生成的文件
4.3、设置二级缓存的类型
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
4.4、加入logback日志
存在SLF4J
时,作为简易日志的log4j
将失效,此时我们需要借助SLF4J
的具体实现logback
来打印日志
创建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.flzj.crowd.mapper" level="DEBUG"/>
</configuration>
MyBatis的逆向工程
先创建数据库表
,由框架负责根据数据库表,反向生成Java实体类
、Mapper接口
、 Mapper映射文件
1、创建逆向工程
依赖
<!-- 依赖MyBatis核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</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>
<!-- 数据库连接池 -->
<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>
创建逆向工程的配置文件
文件名必须是 : 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.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/db03"
userId="root"
password="root">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.flzj.mybatis.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.flzj.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.flzj.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="emp" domainObjectName="Emp"/>
<table tableName="dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
点击创建
2、QBC查询
根据条件来查询 DeptExample类
@Test
public void test1(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
DeptExample deptExample = new DeptExample();
deptExample.createCriteria().andDidBetween(1,2);
deptExample.or().andDeptNameLike("部门");
List<Dept> depts = mapper.selectByExample(deptExample);
System.out.println(depts);
} catch (IOException e) {
e.printStackTrace();
}
}
查询语句发生改变
DEBUG 04-05 10:55:43,913 ==> Preparing: select did, dept_name from dept WHERE ( did between ? and ? ) or( dept_name like ? ) (BaseJdbcLogger.java:137)
DEBUG 04-05 10:55:43,936 ==> Parameters: 1(Integer), 2(Integer), 部门(String) (BaseJdbcLogger.java:137)
DEBUG 04-05 10:55:43,948 <== Total: 2 (BaseJdbcLogger.java:137)
[Dept{did=1, deptName='部门1'}, Dept{did=2, deptName='部门2'}]
分页插件
1、添加依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<!-- 分页插件 -->
<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>
2、分页插件的使用
开启分页功能
在查询功能之前使用
PageHelper.startPage(int pageNum, int pageSize)
pageNum
: 当前页的页码
pageSize
: 每页显示的条数
...
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
PageHelper.startPage(2,4);
List<Dept> depts = mapper.selectByExample(null);
depts.forEach(emp -> System.out.println(emp));
...
Dept{did=5, deptName='部门5'}
Dept{did=6, deptName='部门6'}
Dept{did=7, deptName='部门7'}
Dept{did=8, deptName='部门8'}
获取分页相关数据
在查询获取list集合之后
PageInfo pageInfo = new PageInfo<>(List list, int navigatePages)
list
: 分页之后的数据
navigatePages
: 导航分页的页码数
...
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
PageHelper.startPage(2,4);
List<Dept> list = mapper.selectByExample(null);
PageInfo<Dept> pageInfo = new PageInfo<>(list, 5);
System.out.println(pageInfo);
...
PageInfo{pageNum=2, pageSize=4, size=4, startRow=5, endRow=8, total=14, pages=4, list=Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=14, pages=4, reasonable=false, pageSizeZero=false}[Dept{did=5, deptName='部门5'}, Dept{did=6, deptName='部门6'}, Dept{did=7, deptName='部门7'}, Dept{did=8, deptName='部门8'}], prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=4, navigatepageNums=[1, 2, 3, 4]}
分页相关数据
常用数据 | 解释 |
---|---|
pageNum | 当前页的页码 |
pageSize | 每页显示的条数 |
size | 当前页显示的真实数据 |
total | 总记录数 |
pages | 总页数 |
prePage | 上一页页码 |
nextPage | 下一页页码 |
isFirstPage/isLastPage | 是否为第一页/最后一页 |
hasPreviousPage/hasNextPage | 是否存在上一页/下一页 |
navigatePages | 导航分页的页码数 |
navigatepageNums | 导航分页的页码 [1,2,3,4,5] |
不想说骚话了
马上开始新阶段嗯冲,(ง •_•)ง