MyBatis-动态SQL

一、简介&环境搭建
1.1简介

MyBatis最强大的特性之一就是它的动态SQL功能。 如果您有使用JDBC或任何类似框架的经验,您就会理解有条件地将SQL字符串连接在一起是多么痛苦,请确保不要忘记空格或忽略列列表末尾的逗号。处理动态SQL可能非常痛苦。虽然使用动态SQL永远不会成为问题,但是MyBatis使用了一种强大的动态SQL语言,可以在任何映射的SQL语句中使用。
在MyBatis的早期版本中,有很多元素需要了解和理解。MyBatis 3大大改进了这一点,现在可以使用的元素还不到一半。MyBatis使用强大的基于OGNL的表达式来消除大部分其他元素:
• if
• choose (when, otherwise)
• trim (where, set)
• foreach

2.环境搭建

编写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="com.atorg.mybatis.dao.EmployeeMapperDynamicSQl">
	<!--namespace的值为接口的全类名-->
	<!-- •	if
         •	choose (when, otherwise):分支选择;带了break的switch-case
         		如果带了id就用id查,带了lastname就用lastName查
         •	trim (where(封装查询条件), set(封装修改条件))
         •	foreach
	 -->
  </mapper>
二、if判断

条件:查询员工,携带了哪个字段查询条件就带上这个字段的值
接口编写

package com.atorg.mybatis.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.atorg.mybatis.bean.Employee;

public interface EmployeeMapperDynamicSQl {
	//携带哪个字段查询条件就带上这个字段
	public List<Employee> getEmpsTestInnerParameter(Employee employee);
	}

接口实现编写

<?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="com.atorg.mybatis.dao.EmployeeMapperDynamicSQl">
	  <!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
	  <select id="getEmpsByConditionIf" resultType="emp">
	  	select * from tab1_employee
	  	where
	  	<!--test:判断表达式-->
	  	<!--test:判断表达式(OGNL)
		  	从参数中取值进行判断
		  	遇见特殊符号应该去写准转义字符
		  	  -->
		  	<if test="id!=null"><!--判断id不为空是拼接到sql语句中-->
		  		id=#{id}
		  	</if>
		  	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;"><!--如上和id判断一致-->
		  		and last_name like #{lastName}
		  	</if>
		  	<if test="email!= null and email.trim()!=&quot;&quot;">
		  		and email =#{email}
		  	</if>
		  	<!-- OGNL会进行字符串与数字的转换判断 “0”==0-->
		  	<if test="gender==0 or gender==1">
		  		and gender=#{gender]
		  	</if>
	  </select>

提示:遇见特殊符号应该去写转义字符
测试程序

package com.atorg.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
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;
import org.junit.Test;
import com.atorg.mybatis.bean.Department;
import com.atorg.mybatis.bean.Employee;
import com.atorg.mybatis.dao.DepartmentMapper;
import com.atorg.mybatis.dao.EmployeeMapper;
import com.atorg.mybatis.dao.EmployeeMapperAnnotation;
import com.atorg.mybatis.dao.EmployeeMapperDynamicSQl;
import com.atorg.mybatis.dao.EmployeeMapperPlus;

public class MyBatisTest {
	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void testDynamicSQl() throws IOException {
		// 获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 获取sqlSession对象不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		// 获取接口的实现类对象
		try {
			EmployeeMapperDynamicSQl mapper = openSession.getMapper(EmployeeMapperDynamicSQl.class);
			// 测试if/where
			Employee employee = new Employee(1,"Admin",null,null);
		    List<Employee> employees=mapper.getEmpsByConditionIf(employee);
			for (Employee emp : employees) {
			System.out.println(emp);
			}
		} finally {
			openSession.close();
		}
	}

测试结果
测试类中给出的参数为 idname
执行结果
结论: 与测试程序给出的参数一致
讨论
当实现接口的程序动态sql中的第一个参数不合法时,sql语句为
select * from tab1_employee where and last_name=#{LastName}
这时就出现了问题,where条件后直接出现一个and,此时执行出现语法错误
出现语法错误
解决办法
查询的时候如果某些条件没带可能sql拼装会有问题
1.给where后面加上1=1,以后的条件都and xxx

<?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="com.atorg.mybatis.dao.EmployeeMapperDynamicSQl">
	  <!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
	  <select id="getEmpsByConditionIf" resultType="emp">
	  	select * from tab1_employee
	  	where 1=1
	  	<!--test:判断表达式-->
	  	<!--test:判断表达式(OGNL)
		  	从参数中取值进行判断
		  	遇见特殊符号应该去写准转义字符
		  	  -->
		  	<if test="id!=null"><!--判断id不为空是拼接到sql语句中-->
		  		and id=#{id}
		  	</if>
		  	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;"><!--如上和id判断一致-->
		  		and last_name like #{lastName}
		  	</if>
		  	<if test="email!= null and email.trim()!=&quot;&quot;">
		  		and email =#{email}
		  	</if>
		  	<!-- OGNL会进行字符串与数字的转换判断 “0”==0-->
		  	<if test="gender==0 or gender==1">
		  		and gender=#{gender]
		  	</if>
	  </select>

此时执行
执行成功
2.mybatis推荐使用where标签来将所有的查询条件包括在内,mybatis就会where标签中拼装sql,多出来的and或者or去掉

<?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="com.atorg.mybatis.dao.EmployeeMapperDynamicSQl">
	  <!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
	  <select id="getEmpsByConditionIf" resultType="emp">
	  	select * from tab1_employee
	  	<where>
	  	<!--test:判断表达式-->
	  	<!--test:判断表达式(OGNL)
		  	从参数中取值进行判断
		  	遇见特殊符号应该去写准转义字符
		  	  -->
		  	<if test="id!=null"><!--判断id不为空是拼接到sql语句中-->
		  		id=#{id}
		  	</if>
		  	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;"><!--如上和id判断一致-->
		  		and last_name like #{lastName}
		  	</if>
		  	<if test="email!= null and email.trim()!=&quot;&quot;">
		  		and email =#{email}
		  	</if>
		  	<!-- OGNL会进行字符串与数字的转换判断 “0”==0-->
		  	<if test="gender==0 or gender==1">
		  		and gender=#{gender]
		  	</if>
		  	</where>
	  </select>

此时执行
执行成功
讨论
当我们的sql语句中的and出现在条件后边的时候,当倒数第一个参数为null时select * from tab1_employee WHERE last_name like ? and 此时最后多出一个and,出现语法错误
测试
sql语句出现语法错误
结论:where标签只会去掉第一个多出的and或者or 此时就引出了另一个标签trim

三、trim标签

编写接口

package com.atorg.mybatis.dao;

import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.atorg.mybatis.bean.Employee;

public interface EmployeeMapperDynamicSQl {
	public List<Employee> getEmpsByConditionTrim(Employee employee);
	}

编写接口实现

<?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="com.atorg.mybatis.dao.EmployeeMapperDynamicSQl">
	  <!-- public List<Employee> getEmpsByConditionTrim(Employee employee); -->
	  <select id="getEmpsByConditionTrim" resultType="emp">
	  	select * from tab1_employee
	  	<!-- 后面多出的and或者or  where标签不能解决
	  	 prefix="":前缀,trim标签体是整个字符串拼串后的结果
	  	 			prefix给拼串后的整个字符串加一个前缀
	  	 prefixOverrides="" :前缀覆盖,去掉整个字符串前面多余的字符
	  	 suffix="" 后缀
	  	 			suffix给拼串后的整个字符串加一个后缀
	  	 suffixOverrides=""
	  	 			后缀覆盖:去掉整个字符串后面多余的字符-->
	  	 <!-- trim自定义字符串截取规则:只要满足条件都拼串 -->
	  	<trim prefix="where" suffixOverrides="and">  
	  		<if test="id!=null">
		  		id=#{id} and
		  	</if>
		  	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
		  		last_name like #{lastName} and
		  	</if>
		  	<if test="email!= null and email.trim()!=&quot;&quot;">
		  		email =#{email} and
		  	</if>
		  	<!-- OGNL会进行字符串与数字的转换判断 “0”==0-->
		  	<if test="gender==0 or gender==1">
		  		gender=#{gender]
		  	</if>
	  	</trim>
	  </select>
</mapper>

测试程序

package com.atorg.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
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;
import org.junit.Test;
import com.atorg.mybatis.bean.Department;
import com.atorg.mybatis.bean.Employee;
import com.atorg.mybatis.dao.DepartmentMapper;
import com.atorg.mybatis.dao.EmployeeMapper;
import com.atorg.mybatis.dao.EmployeeMapperAnnotation;
import com.atorg.mybatis.dao.EmployeeMapperDynamicSQl;
import com.atorg.mybatis.dao.EmployeeMapperPlus;

public class MyBatisTest {
	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void testDynamicSQl() throws IOException {
		// 获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 获取sqlSession对象不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		// 获取接口的实现类对象
		try {
			EmployeeMapperDynamicSQl mapper = openSession.getMapper(EmployeeMapperDynamicSQl.class);
			// 测试trim
			 Employee employee = new Employee(5,"%e%",null,null);
			 List<Employee> employees=mapper.getEmpsByConditionTrim(employee);
			 for (Employee employee2 : employees) {
			 System.out.println(employee2); }
		} finally {
			openSession.close();
		}
	}

测试结果
原先where标签如果最后一个参数为空时,会多出and或者or出现sql语句语法错误,此时使用trim标签,会将多余的and删除,以下测试结果证实了这一说法
测试成功
prefix="":前缀,trim标签体是整个字符串拼串后的结果
prefix给拼串后的整个字符串加一个前缀
prefixOverrides="" :前缀覆盖,去掉整个字符串前面多余的字符
suffix="" 后缀   suffix给拼串后的整个字符串加一个后缀
suffixOverrides=""    后缀覆盖:去掉整个字符串后面多余的字符

四、choose标签(只选择满足条件的其中一个)

编写接口

package com.atorg.mybatis.dao;

import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.atorg.mybatis.bean.Employee;
public interface EmployeeMapperDynamicSQl {
	public List<Employee> getEmpByConditionChoose(Employee employee);
	}

编写接口实现

<?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="com.atorg.mybatis.dao.EmployeeMapperDynamicSQl">
	  <!--choose:只选择满足条件的一个  -->
	  <!-- public List<Employee> getEmpByConditionChoose(Employee employee); -->
	  <select id="getEmpByConditionChoose" resultType="emp">
	  	select * from tab1_employee
	  	<where>
	  		<choose>
	  			<when test="id!=null">
	  				id=#{id}
	  			</when>
	  			<when test="lastName!=null">
	  				last_name like #{lastName}
	  			</when>
	  			<when test="email!=null">
	  				email=#{email}
	  			</when>
	  			<otherwise><!--其他条件-->
	  				gender=0
	  			</otherwise>
	  		</choose>
	  	</where>
	  </select>
</mapper>

测试程序

package com.atorg.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
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;
import org.junit.Test;
import com.atorg.mybatis.bean.Department;
import com.atorg.mybatis.bean.Employee;
import com.atorg.mybatis.dao.DepartmentMapper;
import com.atorg.mybatis.dao.EmployeeMapper;
import com.atorg.mybatis.dao.EmployeeMapperAnnotation;
import com.atorg.mybatis.dao.EmployeeMapperDynamicSQl;
import com.atorg.mybatis.dao.EmployeeMapperPlus;
public class MyBatisTest {

	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}
	@Test
	public void testDynamicSQl() throws IOException {
		// 获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 获取sqlSession对象不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		// 获取接口的实现类对象
		try {
			EmployeeMapperDynamicSQl mapper = openSession.getMapper(EmployeeMapperDynamicSQl.class);
			Employee employee = new Employee(null,"%e%",null,null);
			// 测试choose
			  List<Employee> emps= mapper.getEmpByConditionChoose(employee);
			  for (Employee employee2 : emps) { System.out.println(employee2);
			  }
		} finally {
			openSession.close();
		}
	}

测试结果
此时只有name属性不为空,所以此时choose会选择姓名作为查询参数,以下测试结果证实这一说法 如果一次带了多个参数(多个参数都不为null)则默认会选择第一个作为查询参数,如果所有参数都为null则默认执行其他条件即otherwise标签中的条件作为查询条件测试成功

五、set标签和if标签结合做更新操作(封装修改条件)

接口编写

package com.atorg.mybatis.dao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.atorg.mybatis.bean.Employee;
public interface EmployeeMapperDynamicSQl {
	public void updateEmp(Employee employee);
	}

接口实现编写

<?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="com.atorg.mybatis.dao.EmployeeMapperDynamicSQl">
	  <update id="updateEmp">
	  	update tab1_employee 
	  	<!-- set标签去除,号 -->
		<set>
			<if test="lastName!=null">
				last_name=#{lastName},
			</if>
			<if test="email!=null">
				email=#{email},
			</if>
			<if test="gender!=null">
				gender=#{gender}
			</if>
		</set>
		<!--trim做更新操作
		 <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>

测试程序

package com.atorg.mybatis.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
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;
import org.junit.Test;
import com.atorg.mybatis.bean.Department;
import com.atorg.mybatis.bean.Employee;
import com.atorg.mybatis.dao.DepartmentMapper;
import com.atorg.mybatis.dao.EmployeeMapper;
import com.atorg.mybatis.dao.EmployeeMapperAnnotation;
import com.atorg.mybatis.dao.EmployeeMapperDynamicSQl;
import com.atorg.mybatis.dao.EmployeeMapperPlus;
public class MyBatisTest {
	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}
@Test
	public void testDynamicSQl() throws IOException {
		// 获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 获取sqlSession对象不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		// 获取接口的实现类对象
		try {
			EmployeeMapperDynamicSQl mapper = openSession.getMapper(EmployeeMapperDynamicSQl.class);
			 Employee employee = new Employee(1,"Admin",null,null);
			// 测试更新
			/*
			 * mapper.updateEmp(employee); openSession.commit();
			 */
			mapper.updateEmp(employee);
			openSession.commit();
		} finally {
			openSession.close();
		}
	}
}

测试
测试成功

Mybatis-plus是在Mybatis基础上进行封装的一个框架,它简化了平时开发过程中对常用接口的调用,可以省去一些繁琐的操作。然而,对于一些更为复杂的查询,Mybatis-plus可能无法满足需求,此时就需要我们自定义SQL语句来实现。通过在入口类的MybatisSqlSessionFactoryBuilder#build方法中注入mybatis-plus自定义的动态配置xml文件,可以实现自定义SQL语句和动态SQL的功能。具体的实现步骤如下: 1. 在应用启动时,在入口类的MybatisSqlSessionFactoryBuilder#build方法中将mybatis-plus的自定义动态配置xml文件注入到Mybatis中。 2. 在自定义的动态配置xml文件中,可以使用各种Mybatis-plus提供的方法来实现动态SQL的功能,比如IF标签、CHOOSE标签、FOREACH标签等。 3. 在自定义SQL语句中,可以结合Mybatis-plus的Wrapper类来实现条件查询,例如使用LambdaQueryWrapper来构建查询条件。 总结起来,Mybatis-plus提供了简化开发的接口,但对于一些更为复杂的查询,仍然需要我们自定义SQL语句和动态SQL来实现。通过注入自定义的动态配置xml文件,并结合Mybatis-plus提供的方法和Wrapper类,可以实现更加灵活和高效的数据查询。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [mybatis-plus/mybatis 自定义 sql 语句、动态 sql](https://blog.csdn.net/CREATE_17/article/details/109117091)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Mybatis Plus实现动态SQL语句的原理,你知道吗?](https://blog.csdn.net/weixin_38405253/article/details/119880820)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值