MyBatis(随笔: 动态SQL映射文件)

2024-08-06 13:04:27 浏览数 (2)

动态SQL

我们可以用mybatis执行sql的形式来对数据的表进行增删改查操作,不过遇到比较复杂的业务需要写复杂的sql时(比如说sql的条件不确定,可能有一个条件或者多个), 我们就需要动态sql来提高sql的灵活性。

  • 基于OGNL表达式
  • 完成多条件查询等逻辑实现
  • 用于实现动态SQL的元素主要有 if , trim , where , set , choose(when、otherwise), foreach ;

示例代码 : Demo.

查询用户表, 跟名称 密码 地址做多条件查询

Java接口 UserMapper.java

代码语言:javascript复制
	//动态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 完成多条件查询

代码语言:javascript复制
		<!-- 方式一: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

代码语言:javascript复制
	//2.修改用户表 使用if set frim
	public int upd(User u);

映射文件:UserMapper.XML set 完成修改

代码语言:javascript复制
		<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

代码语言:javascript复制
//	3.in查询用户表, 使用foreach 完成部门查询;	
	public List<User> chain(List<Integer> userRoles);

映射文件:UserMapper.XML

代码语言:javascript复制
<!-- 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

代码语言:javascript复制
 	//5.对于某些查询需求,虽然有多个查询条件,但我们不想应用所有的条件,只选择其中一种查询结果时候可以使用:Choose;
	//用户名模糊查询 | 密码模糊查询 都不符合则查全部;
	public List<User> choose(@Param("name")String name,@Param("pwd")String pwd);

映射文件:UserMapper.XML

代码语言:javascript复制
	<!-- 用户名模糊查询 | 密码模糊查询 都不符合则查全部; -->
		<!-- 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

代码语言:javascript复制
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中配置

代码语言:javascript复制
<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可以选一种...

代码语言:javascript复制
<!-- 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

0 人点赞