MyBatis框架:DynamicSQL

动态SQL

简介

动态 SQL是MyBatis强大特性之一。极大的简化我们拼装 SQL的操作。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。
MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作。
if
choose (when, otherwise)
trim (where, set)
foreach

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判断

	<!-- 
		现在查询员工,要求是:如果查询给了哪一个属性的值,
		查询的时候就加上这个属性的值作为查询条件
	 -->
	 <select id="getEmpByIf" resultType="bean.Emp">
	 	
	 	<!--if
	 		test:就是判断表达式(OGNL)
	 		是从参数里面取值进行判断的
	 		如果遇到特殊符号应该去写转义字符
	 		比如下面的ename的判断
	 			单引号可以写成:&quot;
	 			and本来可以写成&&,但是$是特殊符号,可以写成&amp;
	 			结果就是:<if test="ename!=null && ename!="" "></if>
	 			
	 			下面的gender==&quot;&quot;不能写成gender=='男'
	 			这样不能通过,'男'会将男当成数字进行转换,但是''可以代表空串
	 	 -->
	 	
	 	select * from emp where
	 	<if test="eid!=null">
	 		eid=#{eid}
	 	</if>
	 	<if test="ename!=null and ename!='' ">
	 		and ename like #{ename}
	 	</if>
	 	<if test="email!=null and email.trim()!=''">
	 		and email=#{email}
	 	</if>
	 	<if test="gender=="男" or gender=="女"">
	 		and gender=#{gender}
	 	</if>
	 </select>

public List<Emp> getEmpByIf(Emp emp);
			EmpMapperDynamicSQL empMapperDynamicSQL = openSession.getMapper(EmpMapperDynamicSQL.class);
			Emp emp = new Emp(7, "jane", null, "男");
			List<Emp> empByIf = empMapperDynamicSQL.getEmpByIf(emp);
			for (Emp emp2 : empByIf)
			{
				System.out.println(emp2);
			}
结果:(发的查询条件没有email)
DEBUG 05-17 22:51:18,439 ==>  Preparing: select * from emp where eid=? and ename like ? and gender=?   (BaseJdbcLogger.java:145) 
DEBUG 05-17 22:51:18,466 ==> Parameters: 7(Integer), jane(String),(String)  (BaseJdbcLogger.java:145) 
DEBUG 05-17 22:51:18,484 <==      Total: 1  (BaseJdbcLogger.java:145) 
Emp [eid=7, ename=jane, email=jane.com, gender=, dept=null]

where

上面的if查询你可以看出来,如果你不传入eid的话,就会多出一个and,那么就会报sql的错,因为sql变成这样的了

select * from emp where and ename like ?

解决的方法有

  1. 在where后面加上1=1,后面的条件都andxxxx
  2. mybatis使用where标签来将所有的查询条件包括在内,mybatis就会将where标签中拼接的sql多出来的and或者or去掉.
    但是注意的是,where标签只会去掉每个标签第一个位置多出来的and和or
    如果是将and写在每一个if的后面,最后一个if不写and,比如这样写
	<select id="getEmpByIf" resultType="bean.Emp">
	 	select * from emp 
	 	<where>
		 	<if test="eid!=null">
		 		eid=#{eid} and
		 	</if>
		 	<if test="ename!=null and ename!='' ">
		 		 ename like #{ename} and
		 	</if>
		 	<if test="email!=null and email.trim()!=''">
		 		 email=#{email} and
		 	</if>
		 	<if test="gender=="男" or gender=="女"">
		 		 gender=#{gender}
		 	</if>
	 	</where>
	 </select>

			EmpMapperDynamicSQL empMapperDynamicSQL = openSession.getMapper(EmpMapperDynamicSQL.class);
			Emp emp = new Emp(7, null, null, null);
			List<Emp> empByIf = empMapperDynamicSQL.getEmpByIf(emp);

结果的SQL:select * from emp WHERE eid=? and

正确的写法:

	 <select id="getEmpByIf" resultType="bean.Emp">
	 	select * from emp 
	 	<where>
		 	<if test="eid!=null">
		 		eid=#{eid}
		 	</if>
		 	<if test="ename!=null and ename!='' ">
		 		and ename like #{ename}
		 	</if>
		 	<if test="email!=null and email.trim()!=''">
		 		and email=#{email}
		 	</if>
		 	<if test="gender=="男" or gender=="女"">
		 		and gender=#{gender}
		 	</if>
	 	</where>
	 </select>

			EmpMapperDynamicSQL empMapperDynamicSQL = openSession.getMapper(EmpMapperDynamicSQL.class);
			Emp emp = new Emp(null, "jane", null, null);
			List<Emp> empByIf = empMapperDynamicSQL.getEmpByIf(emp);
			for (Emp emp2 : empByIf)
			{
				System.out.println(emp2);
			}
结果SQL:
select * from emp WHERE ename like ? 

Trim

	<select id="getEmpByIfTrim" resultType="bean.Emp">
	 	select * from emp 
	 	<!-- 
	 		现在是后面多出的and或者or,where标签不能解决
	 		trim:自定义字符串截取规则
	 		trim标签体中是一个整体的字符串拼接后的结果
	 		
	 		prefix:前缀,给拼串后的整个字符串增加一个前缀
	 		prefixOverrides:前缀覆盖,去掉字符串(每次拼接的字符串)前面多余的字符
	 		
	 		suffix:后缀,给拼串后的整个字符串增加一个后缀
	 		suffixOverrides:后缀覆盖,去掉字符串(每次拼接的字符串)后面多余的字符
	 	 -->
	 	<trim prefix="where"  suffixOverrides="and">
		 	<if test="eid!=null">
		 		eid=#{eid} and
		 	</if>
		 	<if test="ename!=null and ename!='' ">
		 		ename like #{ename} and
		 	</if>
		 	<if test="email!=null and email.trim()!=''">
		 		email=#{email} and
		 	</if>
		 	<if test="gender=="男" or gender=="女"">
		 		gender=#{gender}
		 	</if>
	 	</trim>
	 </select>

			EmpMapperDynamicSQL empMapperDynamicSQL = openSession.getMapper(EmpMapperDynamicSQL.class);
			Emp emp = new Emp(null, "jane", null, null);
			List<Emp> empByIf = empMapperDynamicSQL.getEmpByIfTrim(emp);
			for (Emp emp2 : empByIf)
			{
				System.out.println(emp2);
			}
结果SQL:select * from emp where ename like ? 

choose

	<!-- 
		choose(when,otherwise):分支选择,相当于带了break的swtich-case语句
		现在需求比如是:如果带了id就只是用id来查,
		如果是带了ename就只是使用ename来查,只会进入其中的一个
	 -->
	<select id="getEmpByIfChose" resultType="bean.Emp">
	 	select * from emp 
	 	<where>
	 		<choose>
	 			<when test="eid!=null">eid=#{eid}</when>
	 			<when test="ename!=null and ename!='' ">ename like #{ename}</when>
	 			<when test="email!=null and email.trim()!=''">email=#{email}</when>
	 			<otherwise>gender="女"</otherwise>
		 	</choose>
	 	</where>
	 </select>

			EmpMapperDynamicSQL empMapperDynamicSQL = openSession.getMapper(EmpMapperDynamicSQL.class);
			Emp emp = new Emp(null, null, null, null);
			List<Emp> empByIf = empMapperDynamicSQL.getEmpByIfChose(emp);
			for (Emp emp2 : empByIf)
			{
				System.out.println(emp2);
			}
结果:
select * from emp WHERE gender="女" 

set

	<!-- public void updateEmp(Emp emp);
		现在需求是更新员工的信息,
		之前是写死的更新员工的全部信息,现在是哪一个
		属性有值就更新哪一列,
		如果只有if标签,在每一个if后面就会多出一个,(逗号)
		所以使用<set>标签,作用有两:有一个set,每一个标签后面的逗号去掉
		这个set标签的作用也可以使用<Trim>标签来代替
	 -->
	<update id="updateEmp" >
		update emp 
		<set>
		<if test="ename!=null">ename=#{ename},</if>
		<if test="email!=null">email=#{email},</if>
		<if test="gender!=null">gender=#{gender}</if>
		where eid=#{eid}
		</set>
	</update>

			EmpMapperDynamicSQL empMapperDynamicSQL = openSession.getMapper(EmpMapperDynamicSQL.class);
			Emp emp = new Emp(3, "凤", null, "女");
			empMapperDynamicSQL.updateEmp(emp);
			openSession.commit();

foreach遍历集合

	<!-- 现在需求是传入多个eid,查询多名员工
	public List<Emp> getEmpByForeach(List<Integer> eids);
	
		collection:指定要便利的集合,就写集合的名字就行
			item:将当前遍历的元素赋值给指定的变量
			separator:设置每个元素之间的分隔符
			open:遍历出所有的结果后,在拼接的总字符串前面加一个开始的东西
			close:遍历出总的结果后,在总的字符串后面增加一个结束的字符串
			index:索引.遍历list的时候index是索引,item就是值
						遍历map的时候index就是map的key,item就是map的值
						
			#{变量名}:这样就能取出变量的值,也就是当前遍历出的元素
	 -->
	<select id="getEmpByForeach" resultType="bean.Emp">
		select * from emp
		<foreach collection="eids" item="theid" 
		separator="," open="where eid in(" close=")">
			#{theid}
		</foreach>
	</select>

public List<Emp> getEmpByForeach(@Param("eids")List<Integer> eids);
			List<Emp> empByForeach = empMapperDynamicSQL.getEmpByForeach(Arrays.asList(3,5,7));
			for (Emp emp2 : empByForeach)
			{
				System.out.println(emp2);
			}
结果SQL:
 Preparing: select * from emp where eid in( ? , ? , ? )   

foreach批量操作

	<!-- public void addEmps(@Param("emps")List<Emp> emps);
		现在需要批量插入多个员工,传入员工的list
		有两种写法:
	 -->
	 
	 <!-- 第二种方法:这种方式需要数据库设置数据库连接属性allowMultiQueries=true,
	 这个设置是允许有多条语句,语句之间使用;来连接 -->
	 <insert id="addEmps">
	 	<foreach collection="emps" item="emp" separator=";">
		 	insert into emp (ename,email,gender,did) values
		 	(#{emp.ename},#{emp.email},#{emp.gender},#{emp.dept.did})
	 	</foreach>
	 </insert>
	 
	 <!-- 写法一 -->
	<!-- <insert id="addEmps">
		insert into emp (ename,email,gender,did) values 
		<foreach collection="emps" item="emp" separator=",">
			(#{emp.ename},#{emp.email},#{emp.gender},#{emp.dept.did})
		</foreach>
	</insert> -->
	<!-- 
		上面的批量保存在MySQL是支持的,
		但是在Oracle是不支持values(),(),()这种方式
		Oracle支持的批量方式有:
		1.多个insert放在begin - end里面
			begin
				insert into emp(eid,ename,email)
				values(emp_seq.nextval,'jane01','jane1.com');
				insert into emp(eid,ename,ename)
				values(emp_seq.nextval,'jane02','jane2.com');
			end;
		2.利用中间表,union是联合,后面有多少个数据写多少个
			insert into emp(eid,ename,email)
				select emp_seq.nextval,ename,email from
				(
					select 'jane01' ename,'jane1.com' email from dual
					union
					select 'jane02' ename,'jane2.com' email from dual
					union
					select 'jane03' ename,'jane3.com' email from dual
				)
	 -->
	 <insert id="addEmps" databaseId="oracle">
	 		<!-- 第一种方式 -->
	 		<!-- 
	 		<foreach collection="emps" item="emp" open="begin" close="end;">
				insert into emp(eid,ename,email)
				values(emp_seq.nextval,#{emp.ename},#{emp.email});
	 		</foreach>
	 		 -->
	 		 
	 		 <!-- 第二种方式 -->
	 		 
	 		 <foreach collection="emps" item="emp" 
	 		 	open="insert into emp(eid,ename,email)
				select emp_seq.nextval,ename,email from
				("
				close=")"
				separator="union"
				>
					select #{emp.ename} ename ,#{emp.email} email from dual
	 		 </foreach>
	 		 
	 </insert>

_parameter和databaseId

	<!-- 
		两个内置参数:
		现在不只是方法传递过来的参数可以用来判断,取值等操作
		mybatis还有两个内置的参数:_parameter和_databaseId
		_parameter:代表整个参数,就是方法传进来的所有参数
			传入单个参数:_parameter就是这个参数
			传入多个参数:参数会被封装为一个map,这个_parameter就是代表这个map
			
		_databaseId:如果设置了databaseIdProvider标签给不同数据库厂商起别名
				_databaseId就是代表当前数据库厂商的别名
				
		利用这两参数,我们可以在一个select里面写不同数据库的CRUD
		例如下面的,我们根据不同的环境切换不同的SQL语句
		在mysql环境下执行mysql的语句,oracle执行oracle的语句
		只不过我这里两个数据库的表都是一样的
		还有根据_parameter可以动态设置查询条件
			如果传入的参数不是空的就按照名字查询
	 -->
	<!-- public List<Emp> getEmpInnerParameter(Emp emp); -->
	<select id="getEmpInnerParameter" resultType="bean.Emp">
		<if test="_databaseId=='mysql'">
			select * from emp
			<if test="_parameter!=null">
				<!-- #{_parameter.ename}和#{ename} 都行 -->
				where ename=#{_parameter.ename}
			</if>
		</if>
		<if test="_databaseId=='oracle'">
			select * from emp
			<if test="_parameter!=null">
				where ename=#{ename}
			</if>
		</if>
		
	</select>

bind

	<!-- bind标签:可以将OGNL表达式的值绑定到一个变量中,
	方便后来引用这个变量
	比如下面的bind标签,先是传入的eid前面增加18154的数字,命名为MyEid
	然后在查询的时候引用这个变量
	结果:
	DEBUG 05-19 06:07:58,349 ==>  Preparing: select * from emp where eid=?   (BaseJdbcLogger.java:145) 
	DEBUG 05-19 06:07:58,372 ==> Parameters: 181541(String)  (BaseJdbcLogger.java:145) 
	 -->
	
	<select id="getEmpByEid" resultType="bean.Emp">
		<bind name="MyEid" value=" '18154'+eid"/>
		select * from emp where eid=#{MyEid}
	</select>

sql抽取重用片段

	<!-- 
		抽取可重用的sql片段,方便后面引用
		1.sql抽取就是抽取经常要查询的列名,或者插入用的列名抽取出来方便引用
		2.而抽取出来的sql想引用使用include来引用
		3.include还可以自定义一些property属性,sql标签内能使用自定义的属性
			include和property之间的取值使用${变量名}来取值
			而${变量名}不能使用这种方法来取值
		举个例子使用:
		比如之前的在不同的数据库使用不同的查询语句,因为不同数据库里面的字段可能不一样
		使用这里就可以将这字段都列出来,而且不同数据库自动选择不同语句
		但是我这里数据库字段都一样,这里只是演示
	 -->
	<sql id="insertColumn">
		<if test="_databaseId=='mysql' ">
			eid,ename,email,#{testcolumn}
		</if>
		<if test="_databaseId=='oracle'">
			eid,ename,email,#{testcolumn}
		</if>
	</sql>
	<!-- 下面的oracle插入就是使用重用sql片段,
		其中<property>还设置了一列的列名是jane
	 -->
	<!-- 
	<insert id="addEmps" databaseId="oracle">
		insert into emp
		(
		<include refid="insertColumn">
			<property name="testcolumn" value="jane"/>
		</include>
		)
			select emp_seq.nextval,ename,email from
			(
 		 <foreach collection="emps" item="emp" close=")" separator="union" >
				select #{emp.ename} ename ,#{emp.email} email from dual
 		 </foreach>
	 </insert>
	 -->

你可能感兴趣的:(MyBatis)