动态sql
环境搭建
基本环境搭建以后(详细搭建环境建前篇)
建立相应的接口bao.EmployeeMapperDynamicSQL和映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
OGNL
OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的
表达式语言,通过它可以非常方便的来操作对象属性。 类似于我们的EL,SpEL等
访问对象属性: person.name
调用方法: person.getName()
调用静态属性/方法: @java.lang.Math@PI
@java.util.UUID@randomUUID()
调用构造方法: new com.atguigu.bean.Person(‘admin’).name
运算符: +,-*,/,%
逻辑运算符: in,not in,>,>=,<,<=,==,!=
注意:xml中特殊符号如”,>,<等这些都需要使用转义字符
访问集合伪属性:
if
在test中可用属性方法
例子:
//全局配置文件/mybaits/wenjian/mybatis-config.xml
<?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>
<settings>
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
<setting name="jdbcTypeForNull" value="NULL"/>
<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<typeAlias type="bao.User" alias="emp"/>
</typeAliases>
<!--若是再别的包下,/ 分割 -->
<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>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapperDynamicSQL.xml" />
<mapper resource="EmployeeMapper.xml" />
<mapper resource="DepartmentMapper.xml" />
</mappers>
</configuration>
///////////////////////////////////////////////////////////////////////////
//bean User.java
package bao;
import org.apache.ibatis.type.Alias;
public class User {
private int username;
private String password;
private Department dpartment;
public Department getDpartment() {
return dpartment;
}
public void setDpartment(Department dpartment) {
this.dpartment = dpartment;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getUsername() {
return username;
}
public void setUsername(int username) {
this.username = username;
}
@Override
public String toString() {
return "User [dpartment=" + dpartment + ", password=" + password
+ ", username=" + username + "]";
}
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(int username, String password, Department dpartment) {
super();
this.username = username;
this.password = password;
this.dpartment = dpartment;
}
}
///////////////////////////////////////////////////////////////////////
//接口/mybaits/src/bao/EmployeeMapperDynamicSQL.java
package bao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface EmployeeMapperDynamicSQL {
public List<User> getEmpsByConditionIf(User e);
}
////////////////////////////////////////////////////////////
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.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="bao.EmployeeMapperDynamicSQL">
<select id="getEmpsByConditionIf" resultType="bao.User">
select * from USER1
where
<if test="username!=null">
username=#{username}</if>
<if test="password!=null">
and password=#{password}</if>
</select>
</mapper>
///////////////////////////////////////////////
//测试
package bao;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisTest {
public static void main(String []args) throws IOException {
SqlSession openSession =getSqlSession();
try {
// 3、获取接口的实现类对象
EmployeeMapperDynamicSQL mapper = openSession.getMapper( EmployeeMapperDynamicSQL.class);
System.out.println(mapper.getEmpsByConditionIf(new User(1,"123",null)));
openSession.commit();
} finally {
openSession.close();
}
}
public static SqlSession getSqlSession() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 1、获取sqlSessionFactory对象
// 2、获取sqlSession对象
SqlSessionFactory a=new SqlSessionFactoryBuilder().build(inputStream);
return a.openSession();
}
}
_MyBatis_动态sql_where_查询条件.
查询的时候如果某些条件没带可能sql拼装会有问题
解决方案:
1、给where后面加上1=1,以后的条件都and xxx.
例子(在前面例子基础上)
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml改为
<select id="getEmpsByConditionIf" resultType="bao.User">
select * from USER1
where 1=1
<if test="username!=null">
and username=#{username}</if>
<if test="password!=null">
and password=#{password}</if>
</select>
2、mybatis使用where标签来将所有的查询条件包括在内。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉
where只会去掉前面username=#{username} 多出来的and或者or。
例子(在前面例子基础上)
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml改为
<select id="getEmpsByConditionIf" resultType="bao.User">
select * from USER1
<where>
<if test="username!=100">
username=#{username}</if>
<if test="password!=null">
and password=#{password}</if>
</where>
</select>
_MyBatis_动态sql_trim_自定义字符串截取
例子(在前面例子基础上)
//接口bao.EmployeeMapperDynamicSQL.java
public List<User> getEmpsByConditionTrim(User e);
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<select id="getEmpsByConditionTrim" resultType="bao.User">
select * from USER1
<!-- 后面多出的and或者or where标签不能解决
prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。
prefix给拼串后的整个字符串加一个前缀
prefixOverrides="":
前缀覆盖: 去掉整个字符串前面多余的字符
suffix="":后缀
suffix给拼串后的整个字符串加一个后缀
suffixOverrides=""
后缀覆盖:去掉整个字符串后面多余的字符
-->
<!-- 自定义字符串的截取规则 -->
<trim prefix="where" suffixOverrides="and" prefixOverrides="and">
<if test="username!=100">
and username=#{username} and
</if>
<if test="password!=null">
and password=#{password} and
</if>
</trim>
</select>
//测试
System.out.println(mapper.getEmpsByConditionTrim(new User(100,"123",null)));
_MyBatis_动态sql_choose_分支选择
例子(在前面例子基础上)
//接口bao.EmployeeMapperDynamicSQL.java
public List<User> getEmpsByConditionChoose(User e);
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<select id="getEmpsByConditionChoose" resultType="bao.User">
select * from USER1
<where>
<!-- 选取首个满足的 -->
<choose>
<when test="username!=100">
username=#{username}
</when>
<when test="password!=null">
password=#{password}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
//测试
System.out.println(mapper.getEmpsByConditionChoose(new User(100,"123",null)));
_MyBatis_动态sql_set_与if结合的动态更新
例子(在前面例子基础上)
//接口bao.EmployeeMapperDynamicSQL.java
public void updateEmp(User e);
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<update id="updateEmp">
<!-- Set标签的使用 -->
update USER1
<set>
<if test="password!=null">
password=#{password},
</if>
<if test="dpartment!=null">
dpartment=#{dpartment},
</if>
</set>
where username=#{username}
<!--
Trim:更新拼串
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id} -->
</update>
//测试
mapper.updateEmp(new User(3,"i love you",null));
_MyBatis_动态sql_foreach_遍历集合
例子(在前面例子基础上)
//接口bao.EmployeeMapperDynamicSQL.java
public List<User> getEmpsByConditionForeach(@Param("ids")List<Integer> ids);
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<select id="getEmpsByConditionForeach" resultType="bao.User">
select * from USER1
<!--
collection:指定要遍历的集合:
list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}就能取出变量的值也就是当前遍历出的元素
-->
<foreach collection="ids" item="item_id" separator=","
open="where username in(" close=")">
#{item_id}
</foreach>
</select>
//测试
System.out.println(mapper.getEmpsByConditionForeach(Arrays.asList(1,2,3,4)));
_MyBatis_动态sql_foreach_mysql下foreach批量插入的两种方式
第一种:
例子在之前的基础上
//接口bao.EmployeeMapperDynamicSQL.java
public void addEmps(@Param("emps")List<User> emps);
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<!-- 批量保存 -->
<!--public void addEmps(@Param("emps")List<Employee> emps); -->
<!--MySQL下批量保存:可以foreach遍历 mysql支持values(),(),()语法-->
<insert id="addEmps">
insert into USER1(PASSWORD,DID)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.password},#{emp.dpartment.id})
</foreach>
</insert>
//测试
ArrayList<User> a= new ArrayList<User>();
a.add(new User(100,"dhdt",new Department(1,"设计部")));
mapper.addEmps(a);
第二种
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<!-- 这种方式需要数据库连接属性allowMultiQueries=true;
这种分号分隔多个sql可以用于其他的批量操作(删除,修改) -->
<insert id="addEmps">
<foreach collection="emps" item="emp" separator=";">
insert into tbl_employee(last_name,email,gender,d_id)
values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
_MyBatis_动态sql_内置参数_parameter&_databaseId
例子在之前的基础上
//接口bao.EmployeeMapperDynamicSQL.java
public List<User> getEmpsTestInnerParameter(User e);
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<!-- 两个内置参数:
不只是方法传递过来的参数可以被用来判断,取值。。。
mybatis默认还有两个内置参数:
_parameter:代表整个参数
单个参数:_parameter就是这个参数
多个参数:参数会被封装为一个map;_parameter就是代表这个map
_databaseId:如果配置了databaseIdProvider标签。//全局配置文件中/mybaits/wenjian/mybatis-config.xml
_databaseId就是代表当前数据库的别名oracle
-->
<!--public List<User> getEmpsTestInnerParameter(User e); -->
<select id="getEmpsTestInnerParameter" resultType="bao.User">
<if test="_databaseId=='mysql'">
select * from USER1
<if test="_parameter!=null">
where last_name like #{username}
</if>
</if>
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter!=null">
where last_name like #{_parameter.lastName}
</if>
</if>
</select>
//测试
System.out.println(mapper.getEmpsTestInnerParameter(new User(1,null,null)));
_MyBatis_动态sql_bind_绑定
例子在之前的基础上
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
<!--public List<Employee> getEmpsTestInnerParameter(Employee employee); -->
<select id="getEmpsTestInnerParameter" resultType="bao.User" >
<!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
<bind name="password" value="'%'+password+'%'"/>
select * from USER1
<if test="_parameter!=null">
where password like #{password}
</if>
</select>
_MyBatis_动态sql_sql_抽取可重用的sql片段
例子在之前的基础上
//映射文件/mybaits/wenjian/EmployeeMapperDynamicSQL.xml
insert into employees(
<!-- 引用外部定义的sql -->
<include refid="insertColumn">
<property name="testColomn" value="abc"/>
</include>
)
<foreach collection="emps" item="emp" separator="union"
open="select employees_seq.nextval,lastName,email from("
close=")">
select #{emp.lastName} lastName,#{emp.email} email from dual
</foreach>
</insert>
<!--
抽取可重用的sql片段。方便后面引用
1、sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
2、include来引用已经抽取的sql:
3、include还可以自定义一些property,sql标签内部就能使用自定义的属性
include-property:取值的正确方式${prop},
#{不能使用这种方式}
-->
<sql id="insertColumn">
<if test="_databaseId=='oracle'">
employee_id,last_name,email,${prop}
</if>
<if test="_databaseId=='mysql'">
last_name,email,gender,d_id
</if>
</sql>
MyBatis-缓存机制
• MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
• MyBatis系统中默认定义了两级缓存。
• 一级缓存和二级缓存。
– 1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
– 2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
– 3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
_MyBatis_缓存_一级缓存失效的四种情况
-
一级缓存:(本地缓存):sqlSession级别的缓存。一级缓存是一直开启的;SqlSession级别的一个Map与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询):
-
1、sqlSession不同。
-
2、sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
-
3、sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
-
4、sqlSession相同,手动清除了一级缓存(缓存清空)openSession.clearCache();
-
_MyBatis_缓存_二级缓存介绍
二级缓存:(全局缓存):基于namespace级别的缓存:一个namespace对应一个二级缓存:
工作机制:
1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
3、sqlSession=EmployeeMapper>Employee
DepartmentMapper===>Department
不同namespace查出的数据会放在自己对应的缓存中(map)
效果:数据会从二级缓存中获取查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
使用:
1)、开启全局二级缓存配置:
在之前的例子基础上配置文件/mybaits/wenjian/mybatis-config.xml
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
2)、去mapper.xml中配置使用二级缓存:
映射文件/mybaits/wenjian/DepartmentMapper.xml
<!--
eviction:缓存的回收策略:
• LRU – 最近最少使用的:移除最长时间不被使用的对象。
• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
• 默认的是 LRU。
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
false:非只读:mybatis觉得获取的数据可能会被修改。
mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
size:缓存存放多少元素;
type="":指定自定义缓存的全类名;
实现Cache接口即可;
-->
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="256"></cache>
3)、我们的POJO需要实现序列化接口
package bao;
import java.io.Serializable;
import org.apache.ibatis.type.Alias;
public class User implements Serializable{
private int username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getUsername() {
return username;
}
public void setUsername(int username) {
this.username = username;
}
@Override
public String toString() {
return "User [ password=" + password
+ ", username=" + username + "]";
}
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(int username, String password) {
super();
this.username = username;
this.password = password;
}
}
和缓存有关的设置/属性:
1)、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)
2)、每个select标签都有useCache=“true”:false:不使用缓存(一级缓存依然使用,二级缓存不使用)
3)、【每个增删改标签的:flushCache=“true”:(一级二级都会清除)】
增删改执行完成后就会清楚缓存;
测试:flushCache=“true”:一级缓存就清空了;二级也会被清除;
查询标签:flushCache=“false”:
如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
4)、sqlSession.clearCache();只是清楚当前session的一级缓存;
5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存;
整合第三方缓存
架包
详细教程
第三方缓存整合:
1)、导入第三方缓存包即可;
2)、导入与第三方缓存整合的适配包;官方有;
3)、mapper.xml中使用自定义缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
在之前的例子中:
映射文件/mybaits/wenjian/EmployeeMapper.xml
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
引用缓存:namespace:指定和哪个名称空间下的缓存一样
在映射文件中
<cache-ref namespace="dao.EmployeeMapper"/>