动态SQL
我们可以用mybatis执行sql的形式来对数据的表进行增删改查操作,不过遇到比较复杂的业务需要写复杂的sql时(比如说sql的条件不确定,可能有一个条件或者多个), 我们就需要动态sql来提高sql的灵活性。
- 基于OGNL表达式
- 完成多条件查询等逻辑实现
- 用于实现动态SQL的元素主要有 if , trim , where , set , choose(when、otherwise), foreach ;
示例代码 : Demo.
查询用户表, 跟名称 密码 地址做多条件查询
Java接口 UserMapper.java
//动态Sql
// 1. if where trim
// 查询用户表, 跟名称 密码 地址做多条件查询
//如果参数过多可以使用 Map;
public List<User> cha(@Param("name")String name,@Param("pwd")String pwd,@Param("address")String address);
映射文件:UserMapper.XML
if 完成多条件查询
<!-- 方式一:if -->
<select id="cha" resultType="User" >
SELECT * FROM `smbms_user` WHERE 1=1
<!-- 为了保证拼接 where 1=1 如果if成立 and拼接,不成立也不会因为 where后面啥也没有而报错! -->
<if test="name!=null and name!='' " >
AND userName LIKE CONCAT('%',#{name},'%')
</if>
<if test="pwd!=null and pwd!='' " >
AND userPassword LIKE CONCAT('%',#{pwd},'%')
</if>
<if test="address!=null and address!='' " >
AND address LIKE CONCAT('%',#{address},'%')
</if>
</select>
<!--
使用 if 进行动态sql处理:
test: 使用OGNL表达式,进行验证参数而拼接对应的 sql 语句;
对于参数,不建议使用 parameterType="" 传单个参数,可以是 Map|复杂数据类型,而单个参数因为没有参数名所以 OGNL表达式无法表示进行判断...可以使用注解..
-->
where 完成多条件查询
代码语言:javascript复制 <!-- 方式二: where -->
<select id="cha" resultType="User" >
SELECT * FROM `smbms_user`
<where>
<if test="name!=null and name!='' " >
AND userName LIKE CONCAT('%',#{name},'%')
</if>
<if test="pwd!=null and pwd!='' " >
AND userPassword LIKE CONCAT('%',#{pwd},'%')
</if>
<if test="address!=null and address!='' " >
AND address LIKE CONCAT('%',#{address},'%')
</if>
</where>
</select>
<!--
使用 where if 进行动态sql处理: if就不介绍了
where: 这里sql语句上面就不需要加 where 1=1,防止拼接错误了.
因为: where 会自动在第一个 if成立的sql前面加 where 并移除它的and,而之后的不会有影响;
-->
trim 完成多条件查询
代码语言:javascript复制 <!-- 方式三: trim -->
<select id="cha" resultType="User" >
SELECT * FROM `smbms_user`
<trim prefix="where" prefixOverrides="and|or" > <!-- 第一个成立if 新增where 剪切 and 或 or -->
<if test="name!=null and name!='' " >
AND userName LIKE CONCAT('%',#{name},'%')
</if>
<if test="pwd!=null and pwd!='' " >
AND userPassword LIKE CONCAT('%',#{pwd},'%')
</if>
<if test="address!=null and address!='' " >
AND address LIKE CONCAT('%',#{address},'%')
</if>
</trim>
</select>
<!--
使用 trim if 进行动态sql处理: if就不介绍了
trim : 类似于一个可以自定义的标签,这里就相当于将 trim 设置为一个类似 where标签; 更灵活地去除多余关键字,替代where和set
prefix 代表的是语句的前缀,会在第一个成立if sql前面加 prefix的值;
suffix 代表的是语句的后缀,会在最后一个成立if sql后面加 suffix的值;
prefixOverrides 代表的是需要去掉的字符串,会在第一个成立if 剪切sql中 prefixOverrides的值;
suffixOverrides 代表的是语句的后缀,会在最后一个成立if 剪切sql中 suffixOverrides的值;
-->
修改用户表 使用if set trim
Java接口 UserMapper.java
//2.修改用户表 使用if set frim
public int upd(User u);
映射文件:UserMapper.XML
set 完成修改
<update id="upd" parameterType="User" >
UPDATE `smbms_user`
<set>
<if test="userName!=null and userName!='' " > `userName` =#{userName}, </if>
<if test="userPassword!=null and userPassword!='' " > `userPassword`=#{userPassword}, </if>
</set>
where id= #{id}
</update> -->
<!--
set: 会默认在第一个成立if 前面加SET,并去掉最后一个列的 , '逗号'
注意要确保至少有一个set 成立哦~不然 :UPDATE 表 WHERE ID=? 并不符合Sql语句!,没有一个if成立导致没有 SET要修改的列;
-->
trim 完成修改
代码语言:javascript复制<update id="upd" parameterType="User">
UPDATE `smbms_user`
<trim prefix="set" suffix="where id = #{id}" suffixOverrides=",">
<if test="userName!=null and userName!='' " > `userName` =#{userName}, </if>
<if test="userPassword!=null and userPassword!='' " > `userPassword`=#{userPassword}, </if>
</trim>
</update>
in查询用户表, 使用foreach 完成部门查询;
Java接口 UserMapper.java
// 3.in查询用户表, 使用foreach 完成部门查询;
public List<User> chain(List<Integer> userRoles);
映射文件:UserMapper.XML
<!-- 3.in查询用户表, 使用fearche 完成部门查询; -->
<select id="chain" parameterType="java.util.List" resultType="User" >
SELECT * FROM `smbms_user` WHERE `userRole` IN
<foreach collection="list" item="W" open="(" close=")" separator="," >
#{W}
</foreach>
</select>
<!--
foreach: 迭代一个集合,通常用于in条件
item 表示集合中每一个元素进行迭代时候的别名;
index 指定一个名称,用于表示在迭代过程中,每次迭代到的位置。
collection:必须指定:
list集合: 当传入参数是单参数且是一个list时候, collection属性值应该为 list;
array数组: 当传入参数是单参数且是一个数组时候,collection属性值应该为array
map-key集合: 当传入参数为多参数,就需要包它们封装为一个Map进行处理;
open 表示该语句开始时候的符合,既然是in 那一定是 open="("
separator 表示在每次进行迭代之间什么符号,作为分隔符 既然是in 那一定是 separator=","
close 表示该语句结束时候的符合,既然是in 那一定是 close=")"
-->
对于某些查询需求,虽然有多个查询条件,但我们不想应用所有的条件,只选择其中一种查询结果时候可以使用:Choose;
Java接口 UserMapper.java
//5.对于某些查询需求,虽然有多个查询条件,但我们不想应用所有的条件,只选择其中一种查询结果时候可以使用:Choose;
//用户名模糊查询 | 密码模糊查询 都不符合则查全部;
public List<User> choose(@Param("name")String name,@Param("pwd")String pwd);
映射文件:UserMapper.XML
<!-- 用户名模糊查询 | 密码模糊查询 都不符合则查全部; -->
<!-- Choose -->
<select id="choose" resultType="User" >
SELECT * FROM `smbms_user`
<where>
<choose>
<when test="name!=null and name!='' ">
AND userName LIKE CONCAT('%',#{name},'%')
</when>
<when test="pwd!=null and pwd!='' ">
AND userPassword LIKE CONCAT('%',#{pwd},'%')
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>
<!--
choose(when、otherwise): 相当于Java中switch语句,当when有条件满足的时候,就跳出choose,如果没有一个成立则执行: otherwise;
<choose>
<when test ="条件1"> …</when>
<when test ="条件2"> …</when>
.....
<otherwise>…</otherwise>
</choose>
-->
运行类 DTSqlRun .Java
package com.wsm.run;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import org.apache.ibatis.session.SqlSession;
import com.wsm.mapper.UserMapper;
import com.wsm.pojo.User;
import com.wsm.utis.MyBatisUtil;
public class DTSqlRun {
static Scanner input = new Scanner(System.in);
public static void main(String[] args) {
DTSqlRun r = new DTSqlRun();
//r.cha();
//r.upd();
//r.chain();
r.choose();
}
public void cha(){
SqlSession session = MyBatisUtil.create();
UserMapper um = session.getMapper(UserMapper.class);
List<User> users = um.cha("w",null,"");
if(users.size()==0){
System.out.println("没有数据!");
}
for (User user : users) {
System.out.println(user.getId() "t" user.getUserCode() "t" user.getUserName() "t" user.getPhone()
"t" user.getAddress());
}
MyBatisUtil.close(session);
}
public void upd(){
User u = new User();
//u.setUserName("asdsasad");
u.setUserPassword("xxasssssda");
u.setId(1);
SqlSession session = MyBatisUtil.create();
UserMapper um = session.getMapper(UserMapper.class);
if(um.upd(u)==1){
session.commit(); //手动提交;
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
MyBatisUtil.close(session);
}
public void chain(){
List<Integer> in = new ArrayList<Integer>();
in.add(1);
in.add(2);
//in.add(3);
SqlSession session = MyBatisUtil.create();
UserMapper um = session.getMapper(UserMapper.class);
List<User> users = um.chain(in);
if(users.size()==0){
System.out.println("没有数据!");
}
for (User user : users) {
System.out.println(user.getId() "t" user.getUserCode() "t" user.getUserName() "t" user.getPhone()
"t" user.getAddress() "t" user.getUserRole());
}
MyBatisUtil.close(session);
}
public void choose(){
SqlSession session = MyBatisUtil.create();
UserMapper um = session.getMapper(UserMapper.class);
List<User> users = um.choose("s","ss"); //两个参数都传了,但pwd的属性无效,因为name 成立了 <choose> 就结束了;
if(users.size()==0){
System.out.println("没有数据!");
}
for (User user : users) {
System.out.println(user.getId() "t" user.getUserCode() "t" user.getUserName() "t" user.getPhone()
"t" user.getAddress());
}
MyBatisUtil.close(session);
}
}
MyBatis缓存
正如大多数框架一样,MyBatis提供了一级缓存
和 二级缓存
一级缓存
一级缓存是基于,PerpetyalCache(MyBatis 自带的), HashMap本地缓存,作用范围在 SqlSession域内. 当SqlSession关闭时,则其所有的缓存就会被清空…
二级缓存
二级缓存就是 global caching 它超出 sqlsession 范围之外, 结果可以被所有的sqlSession 共享;
开启它只需要在MyBatis 的核心配置文件( MyBatis-config.xml ) setting 中设置…
二级缓存的配置:
1.需要在MyBatis-config.xml中配置
<settings>
<!--配置mybatis的log实现为LOG4J -->
<setting name="logImpl" value="LOG4J" />
<!--设置resultMap的自动映射级别,NONE(禁止自动匹配),PARTIAL(默认)自动匹配所有属性,有内部嵌套(association、collection)的除外 且 数据库列与实体类名相同,FULL(自动匹配所有属性)比PARTIAL更强! -->
<setting name="autoMappingBehavior" value="FULL" />
<!-- 设置二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
2.在对应的Mapper.xml 文件中设置缓存,默认是没有开启缓存的; 需要注意的是:global caching 作用域针对的是 mapper 的 namespace 而言的,即只有在此 namepace 内的查询才可以共享这个 cache…
代码语言:javascript复制<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!-- 二级缓存 -->
<!--
eviction :文件的保存形式;
flushInterval :缓存对象的毫秒数;
size :缓存对象的个数; 先进先出原则,如果已经满了,则最先进的移除,添加新的
readOnly : 只读
-->
3.在 mapper 文件配置支持 cache 后, 如果需要对个别查询进行调整,可以单独设置 cache 代码
这样就不需要在配置 Cache了… 方法2 和 方法3可以选一种...
<!-- userCache: true开启 false关闭; -->
<select id="cha" resultMap="RoleMap" useCache="true" >
...
</select>
示例demo
代码语言:javascript复制//根据编号查部门及 部门员工;
public void cha(){
SqlSession session = MyBatisUtil.create();
RoleMapper rm = session.getMapper(RoleMapper.class);
//一级缓存:缓存数据存储在 sqlSession中;
Role r = rm.cha(2);
System.out.println(r.getRoleName() "t" r.getId());
List<User> u = r.getUsers();
for (User user : u) {
System.out.println(user.getUserName());
}
//因此不关闭 sqlSession 在此查相同的数据不会在需要sql语句; 访问数据库;
System.out.println("------------------------------------------");
Role r1 = rm.cha(2);
System.out.println(r1.getRoleName() "t" r1.getId());
//而查询没有查过的数据还是需要sql 访问数据库
System.out.println("------------------------------------------");
Role r2 = rm.cha(3);
System.out.println(r2.getRoleName() "t" r2.getId());
MyBatisUtil.close(session); //关闭SqlSession
System.out.println("二级缓存");
//二级缓存: 数据存储在 sqlSessionFactory工厂中;
//需要在配置信息中设置
//MyBatis: <setting name="cacheEnabled" value="true"/>
//xxxMapper.xml中: <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
//而查询时: <select id="cha" resultMap="RoleMap" useCache="true" > 即可;!! useCache="true/false" 开启/关闭;
//即使是SqlSession 关闭对于查过的数据也会保存起来
SqlSession sessionr = MyBatisUtil.create();
RoleMapper rmr = sessionr.getMapper(RoleMapper.class);
Role rr = rmr.cha(1);
System.out.println(rr.getRoleName() "t" rr.getId());
MyBatisUtil.close(sessionr); //关闭SqlSession
//上面关闭了,才用二级缓存;所以不在需要Sql 数据库操作;
SqlSession sessionr1 = MyBatisUtil.create();
RoleMapper rmr1 = sessionr1.getMapper(RoleMapper.class);
Role rr1 = rmr1.cha(1);
System.out.println(rr1.getRoleName() "t" rr1.getId());
MyBatisUtil.close(sessionr1); //关闭SqlSession
}
Run