我们执行SQL语句,通常还是根据条件来执行的。Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决 拼接SQL语句字符串时的痛点问题。
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行.
需要注意and的位置。
<select id="getEmpByObject" resultType="Employees">
select * from employees where
<if test="employee_id != '' and employee_id != null">
employee_id = #{employee_id}
if>
<if test="email != '' and email != null">
and email = #{email}
if>
select>
where和if一般结合使用:
注意:where标签不能去掉条件最后多余的and
<select id="getEmpByWhere" resultType="employees">
select * from employees
<where>
<if test="employee_id != '' and employee_id != null">
and employee_id = #{employee_id}
if>
<if test="email != '' and email != null">
and email = #{email}
if>
where>
select>
trim用于去掉或添加标签中的内容,属性介绍
<select id="getEmpByTrim" resultType="employees">
select * from employees
<trim prefix="where" suffixOverrides="and">
<if test="employee_id != '' and employee_id != null">
employee_id = #{employee_id} and
if>
<if test="email != '' and email != null">
email = #{email} and
if>
trim>
select>
choose、when、otherwise相当于if…else if…else
<select id="getEmpByChoose" resultType="employees">
select * from employees
<where>
<choose>
<when test="employee_id != '' and employee_id != null">
and employee_id = #{employee_id}
when>
<otherwise>
1 = 1
otherwise>
choose>
where>
select>
foreach控制循环,属性介绍:
<insert id="insertEmp">
insert into employees(employee_id, last_name, email) values
<foreach collection="lists" item="emp" separator=",">
(#{emp.employee_id}, #{emp.last_name}, #{emp.email})
foreach>
insert>
sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入
<sql id="empColumns">
eid,ename,age,sex,did
sql>
select <include refid="empColumns">include> from t_emp
Mybatis的缓存分为两个级别,一级缓存是SqlSession级别,另一个是SqlSessionFactory级别。
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就 会从缓存中直接获取,不会从数据库重新访问
@Test
public void testGetEmpByObject() {
Employees employees = new Employees();
employees.setEmployee_id(300);
employees.setEmail("sakha");
//第一次查询
List<Employees> empByObject1 = mapper.getEmpByObject(employees);
empByObject1.forEach(list -> System.out.println(list));
//第二次查询
List<Employees> empByObject2 = mapper.getEmpByObject(employees);
empByObject2.forEach(list -> System.out.println(list));
}
/**
DEBUG 03-14 13:30:20,141 ==> Preparing: select * from employees where employee_id = ? and email = ? (BaseJdbcLogger.java:137)
DEBUG 03-14 13:30:20,173 ==> Parameters: 300(Integer), sakha(String) (BaseJdbcLogger.java:137)
DEBUG 03-14 13:30:20,201 <== Total: 1 (BaseJdbcLogger.java:137)
Employees{employee_id=300, first_name='ww', last_name='hh', email='sakha'}
Employees{employee_id=300, first_name='ww', last_name='hh', email='sakha'}
*/
从日志输出文件来看,两次查询,只执行了一次sql,这就是SqlSession级别的缓存。
但是一级缓存也会失效,情况如下
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被 缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
开启二级缓存:
在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
在映射文件中设置标签
<mapper namespace="com.shang.mybatis.mappers.DynamicMapper">
<cache >cache>
<select id="getEmpByWhere" resultType="employees">
select * from employees
<where>
<if test="employee_id != '' and employee_id != null">
and employee_id = #{employee_id}
if>
<if test="email != '' and email != null">
and email = #{email}
if>
where>
select>
mapper>
二级缓存必须在SqlSession关闭或提交之后有效
sqlSession1.close(); //关闭此次会话
查询的数据所转换的实体类类型必须实现序列化的接口
public class Employees implements Serializable
@Test
public void testCache() {
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build1 = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession1 = build1.openSession(true);
DynamicMapper mapper = sqlSession1.getMapper(DynamicMapper.class);
Employees employees = new Employees();
employees.setEmployee_id(300);
employees.setEmail("sakha");
List<Employees> empByWhere = mapper.getEmpByWhere(employees);
empByWhere.forEach(list -> System.out.println(list));
sqlSession1.close(); //关闭此次会话
SqlSession sqlSession2 = build1.openSession(true);
DynamicMapper mapper1 = sqlSession2.getMapper(DynamicMapper.class);
Employees employees1 = new Employees();
employees1.setEmployee_id(300);
employees1.setEmail("sakha");
List<Employees> empByWhere1 = mapper1.getEmpByWhere(employees);
empByWhere1.forEach(list -> System.out.println(list));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
DEBUG 03-14 13:46:22,990 Cache Hit Ratio [com.shang.mybatis.mappers.DynamicMapper]: 0.0 (LoggingCache.java:60)
DEBUG 03-14 13:46:23,185 ==> Preparing: select * from employees WHERE employee_id = ? and email = ? (BaseJdbcLogger.java:137)
DEBUG 03-14 13:46:23,207 ==> Parameters: 300(Integer), sakha(String) (BaseJdbcLogger.java:137)
DEBUG 03-14 13:46:23,228 <== Total: 1 (BaseJdbcLogger.java:137)
Employees{employee_id=300, first_name='ww', last_name='hh', email='sakha'}
WARN 03-14 13:46:23,239 As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66 (SerialFilterChecker.java:46)
DEBUG 03-14 13:46:23,252 Cache Hit Ratio [com.shang.mybatis.mappers.DynamicMapper]: 0.5 (LoggingCache.java:60)
Employees{employee_id=300, first_name='ww', last_name='hh', email='sakha'}
*/
从上面可以看到,一次会话关闭,开启第二个会话,不需要执行sql查询语句,直接从二级缓存中可以拿到。
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
在mapper配置文件中添加的cache标签可以设置一些属性:
eviction属性:缓存回收策略 :
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 默认的是 LRU。
flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
size属性:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readOnly属性:只读,true/false true:
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。如果二级缓存没有命中,再查询一级缓存 ,如果一级缓存也没有命中,则查询数据库 。SqlSession关闭之后,一级缓存中的数据会写入二级缓存
<!-- 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包名称 | 作用 |
---|---|
mybatis-ehcache | mybatis-ehcache |
ehcache | EHCache核心包 |
slf4j-api | SLF4J日志门面包 |
logback-classic | 支持SLF4J门面接口的一个具体实现 |
在resource目录下增加EHCache的配置文件ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="D:\ehcache"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
defaultCache>
ehcache>
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。
创建logback的配置文件logback.xml
<configuration debug="true">
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
root>
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
configuration>
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.3.0version>
<dependencies>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.8version>
dependency>
dependencies>
plugin>
plugins>
build>
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
mappers>
configuration>
文件名必须是:generatorConfig.xml
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="123456">
jdbcConnection>
<javaModelGenerator targetPackage="com.atguigu.mybatis.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
javaModelGenerator>
<sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.atguigu.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>
maven工具点击
@Test
public void testMBG() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSession sqlSession = new
SqlSessionFactoryBuilder().build(is).openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
EmpExample empExample = new EmpExample();
//创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系
empExample.createCriteria().andEnameLike("a").andAgeGreaterThan(20).andDidIsNot
Null();
//将之前添加的条件通过or拼接其他条件
empExample.or().andSexEqualTo("男");
List<Emp> list = mapper.selectByExample(empExample);
for (Emp emp : list) {
System.out.println(emp);
}
}
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.2.0version>
dependency>
在MyBatis的核心配置文件中配置插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">plugin>
plugins>
pageNum:当前页的页码 pageSize:每页显示的条数
list:分页之后的数据 navigatePages:导航分页的页码数
PageInfo{
pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8,
list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30,
pages=8, reasonable=false, pageSizeZero=false},
prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true,
hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8,
navigatepageNums=[4, 5, 6, 7, 8]
}
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
@Test
public void test(){
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
System.out.println("\n查询功能前开启分页");
PageHelper.startPage(2, 4);
List<Emp> emps = mapper.selectByExample(null);
emps.forEach(emp -> System.out.println(emp));
System.out.println("\n");
PageInfo<Emp> pages = new PageInfo<>(emps, 5);
System.out.println("PageInfo----->"+pages);
} catch (IOException e) {
e.printStackTrace();
}
}