学习视频:3001 动态SQL中的元素_哔哩哔哩_bilibili
目录
1.1为什么学
1.2动态SQL中的元素
条件查询操作
if 元素
choose、when、otherwise元素
where、trim元素
更新操作
set元素使用场景
复杂查询操作
foreach 元素中的属性
编辑
迭代数组
迭代List
迭代Map
1.3案例:学生信息查询系统
总结:
1.1为什么学
在实际项目的开发中,开发人员在使用JDBC或其他持久层框架进行开发时,经常需要根据不同的条件拼接SQL语句,拼接SQL语句时还要确保不能遗漏必要的空格、标点符号等,这种编程方式给开发人员带来了非常大的不便,而MyBatis提供的SQL语句动态组装功能,恰能很好地解决这一问题。
1.2动态SQL中的元素
使用动态SQL的好处
动态SQL是MyBatis的强大特性之一,MyBatis采用了功能强大的基于OGNL(Object Graph Navigation Language)的表达式来完成动态SQL。在MyBatis的映射文件中,开发人员可通过动态SQL元素灵活组装SQL语句,这在很大程度上避免了单一SQL语句的反复堆砌,提高了SQL语句的复用性。
动态SQL常用元素
条件查询操作
<if>元素
在MyBatis中,<if>元素是最常用的判断元素,它类似于Java中的if语句,主要用于实现某些简单的条件判断。在实际应用中,我们可能会通过某个条件查询某个数据。例如,要查找某个客户的信息,可以通过姓名或者年龄来查找客户,也可以不填写年龄直接通过姓名来查找客户,还可以都不填写而查询出所有客户,此时姓名和年龄就是非必须条件。类似于这种情况,在MyBatis中就可以通过<if>元素来实现。
数据库准备
在名称为mybatis的数据库中,创建一个t_customer表,并插入几条测试数据。
USE mybatis;
CREATE TABLE t_customer (
id int(32) PRIMARY KEY AUTO_INCREMENT,
username varchar(50),
jobs varchar(50),
phone varchar(16));
INSERT INTO t_customer VALUES ('1', 'joy', 'teacher', '13733333333');
INSERT INTO t_customer VALUES ('2', 'jack', 'teacher', '13522222222');
INSERT INTO t_customer VALUES ('3', 'tom', 'worker', '15111111111');
POJO类准备
创建持久化类Customer,在类中声明id、username、jobs和phone属性,及属性对应的getter/setter方法。
public class Customer {
private Integer id; private String username; // 主键ID、客户名称
private String jobs; private String phone; // 职业、电话
// 省略getter/setter方法
@Override
public String toString() {
return "Customer [id=" + id + ", username=" + username + ", jobs=" + jobs + ", phone=" + phone + "]"; }
}
创建映射文件
创建映射文件CustomerMapper.xml,在映射文件中,根据客户姓名和年龄组合条件查询客户信息,使用<if>元素编写该组合条件的动态SQL。
<!– 该xml文件中只列出了if元素的动态SQL-->
<if test="username !=null and username !=‘’“>
and username like concat('%',#{username}, '%')
</if>
<if test="jobs !=null and jobs !=‘’“>
and jobs= #{jobs}
</if>
修改核心配置文件
在配置文件mybatis-config.xml中,引入CustomerMapper.xml映射文件,将CustomerMapper.xml映射文件加载到程序中。
<mapper
resource="com/it/mapper/CustomerMapper.xml">
</mapper>
创建获取SqlSession对象的工具类
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory = null;
// 初始化SqlSessionFactory对象
static {
try {
// 使用MyBatis提供的Resources类加载MyBatis的配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 构建SqlSessionFactory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取SqlSession对象的静态方法
public static SqlSession getSession() {
return sqlSessionFactory.openSession();
}
}
编写测试类
在测试类MyBatisTest中,编写测试方法findCustomerByNameAndJobsTest(),该方法用于根据客户姓名和职业组合条件查询客户信息列表。
public class MyBatisTest {
@Test
public void findCustomerByNameAndJobsTest(){SqlSession session = MyBatisUtils.getSession();Customer customer = new Customer();
customer.setUsername(“jack"); customer.setJobs("teacher");
List<Customer> customers = session.selectList("com.itheima.mapper"
+ ".CustomerMapper.findCustomerByNameAndJobs",customer);
for (Customer customer2 : customers) { System.out.println(customer2); }
session.close();}
}
MyBatisTest.java
package com.it.test;
import com.it.pojo.Customer;
import com.it.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyBatisTest {
@Test
public void findCustomerByUsernameAndJobsTest()
{
SqlSession sqlSession= MyBatisUtils.getSession();
Customer customer=new Customer();
customer.setUsername("jack");
customer.setJobs("teacher");
//执行sql
List<Customer> customers=sqlSession.selectList("com.it.mapper.CustomerMapper.findCustomerByUsernameAndJobs",customer);
for (Customer c:customers)
{
System.out.println(c);
}
//释放资源
sqlSession.close();
}
}
CustomerMapper.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为映射的根节点,用来管理DAO接口
namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
-->
<mapper namespace="com.it.mapper.CustomerMapper">
<!--
id = "接口中的方法名"
parameterType = "接口中传入方法的参数类型"
resultType = "返回实体类对象:包.类名" 处理结果集 自动封装
注意:sql语句后不要出现";"号
查询:select标签
增加:insert标签
修改:update标签
删除:delete标签
-->
<select id="findCustomerByUsernameAndJobs" parameterType="com.it.pojo.Customer" resultType="com.it.pojo.Customer">
SELECT * FROM t_customer where 1=1
<if test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</if>
<if test="jobs!=null and jobs!=''">
and jobs=#{jobs}
</if>
</select>
</mapper>
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" >
<!--配置mybatis环境-->
<configuration>
<!--配置连接使用的相关参数
default为默认使用的环境:development 测试环境
product 生产环境
-->
<properties resource="jdbc.properties"></properties>
<environments default="development">
<!--测试环境-->
<environment id="development">
<!--事务管理类型:指定事务管理的方式 JDBC-->
<transactionManager type="JDBC"/>
<!--数据库连接相关配置,动态获取config.properties文件里的内容-->
<!--数据源类型:POOLED 表示支持JDBC数据源连接池
UNPOOLED 表示不支持数据源连接池
JNDI 表示支持外部数据源连接池
-->
<dataSource type="POOLED">
<!--此处使用的是MySQL数据库,使用Oracle数据库时需要修改,仔细检查各项参数是否正确,里面配置了时区、编码方式、SSL,用以防止中文查询乱码,导致查询结果为null及SSL警告等问题-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url"
value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--下面编写mapper映射文件↓↓↓↓↓ 参考格式:<mapper resource="dao/UserMapper.xml"/> -->
<mapper resource="com/it/mapper/CustomerMapper.xml"></mapper>
</mappers>
</configuration>
<choose>、<when>、<otherwise>元素
<choose><when>otherwise>使用场景
在使用<if>元素时,只要test属性中的表达式为true,就会执行元素中的条件语句,但是在实际应用中,有时只需要从多个选项中选择一个去执行。
例如下面的场景:“当客户名称不为空,则只根据客户名称进行客户筛选;当客户名称为空,而客户职业不为空,则只根据客户职业进行客户筛选。当客户名称和客户职业都为空,则要求查询出所有电话不为空的客户信息。”
针对上面情况,使用<if>元素进行处理是不合适的。MyBatis提供了<choose>、<when>、<otherwise>元素进行处理,这三个元素往往组合在一起使用,作用相当于Java语言中的if…else if…else。
<select id="findCustomerByUsernameOrJobs" parameterType="com.it.pojo.Customer" resultType="com.it.pojo.Customer">
SELECT * FROM t_customer where 1=1
<choose>
<when test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</when>
<when test="jobs!=null and jobs!=''">
and jobs=#{jobs}
</when>
<otherwise>
and phone is not null
</otherwise>
</choose>
</select>
@Test
public void findCustomerByUsernameOrJobsTest()
{
SqlSession sqlSession= MyBatisUtils.getSession();
Customer customer=new Customer();
customer.setUsername("jack");
customer.setJobs("teacher");
//执行sql
List<Customer> customers=sqlSession.selectList("com.it.mapper.CustomerMapper.findCustomerByUsernameOrJobs",customer);
for (Customer c:customers)
{
System.out.println(c);
}
//释放资源
sqlSession.close();
}
<where>、<trim>元素
<where><trim>使用场景
在映射文件中,编写的SQL后面加入了“where 1=1”的条件的话,既保证了where后面的条件成立,又避免了where后面第一个词是and或者or之类的关键字。 例如下面这条Mybatis拼接出的SQL语句是不正确的。
select * from t_customer where and username like
concat('%',?, '%') and jobs = #{jobs}
上述SQL语句中,where后直接跟的是and,这在运行时会报SQL语法错误,针对这种情况,可以使用MyBatis提供的<where>元素和<trim>元素进行处理。
<select id="findCustomerByUsernameAndJobs" parameterType="com.it.pojo.Customer" resultType="com.it.pojo.Customer">
select * from t_customer
<where>
<if test="username !=null and username !=''">
and username like concat('%',#{username}, '%')</if>
<if test="jobs !=null and jobs !=''">
and jobs= #{jobs}</if>
</where>
</select>
如果2个if都不满足,where会被忽视
上述代码配置中,<where>元素会自动判断由组合条件拼装的SQL语句,只有<where>元素内的某一个或多个条件成立时,才会在拼接SQL中加入where关键字,否则将不会添加;即使where之后的内容有多余的“AND”或“OR”,<where>元素也会自动将他们去除。
<trim>元素
<trim>元素用于删除多余的关键字,它可以直接实现<where>元素的功能。<trim>元素包含4个属性。
<select id="findCustomerByUsernameAndJobs" parameterType="com.it.pojo.Customer" resultType="com.it.pojo.Customer">
select * from t_customer
<trim prefix="where" prefixOverrides="and">
<if test="username !=null and username !=''">
and username like concat('%',#{username}, '%')</if>
<if test="jobs !=null and jobs !=''">
and jobs= #{jobs}</if>
</trim>
</select>
更新操作
<set>元素使用场景
在Hibernate框架中,如果想要更新某一个对象,就需要发送所有的字段给持久化对象,然而在实际应用中,大多数情况下都是更新某一个或几个字段。如果更新的每一条数据都要将其所有的属性都更新一遍,那么执行效率是非常差的。为了解决更新数据的效率问题,MyBatis提供了<set>元素。<set>元素主要用于更新操作,它可以在动态SQL语句前输出一个SET关键字,并将SQL语句中最后一个多余的逗号去除。<set>元素与<if>元素结合可以只更新需要更新的字段。
映射文件
<update id="updateCustomerBySet" parameterType="com.it.pojo.Customer">update t_customer
<set>
<if test="username !=null and username !=''">
username=#{username},</if>
<if test="jobs !=null and jobs !=''"> jobs=#{jobs},</if>
<if test="phone !=null and phone !=''">phone=#{phone},</if>
</set> where id=#{id}
</update>
测试
@Test
public void updateCustomerBySetTest()
{
SqlSession session =MyBatisUtils.getSession();
Customer customer=new Customer();
customer.setId(3);
customer.setPhome("12345678912");
int rows= session.update("com.it.mapper.CustomerMapper.updateCustomerBySet",customer);
if(rows>0)
{
System.out.println("修改了"+rows+"条数据");
}
else
{
System.out.println("没有修改数据");
}
session.commit();
session.close();
}
<set>元素字段非空
在映射文件中使用<set>元素和<if>元素组合进行update语句动态SQL组装时,如果<set>元素内包含的内容都为空,则会出现SQL语法错误。因此,在使用<set>元素进行字段信息更新时,要确保传入的更新字段不能都为空。
使用<trim>元素更新
除了使用<set>元素外,还可以通过<trim>元素来实现更新操作。其中, <trim>元素的prefix属性指定要添加的<trim>元素所包含内容的前缀为set,suffixOverrides属性指定去除的<trim>元素所包含内容的后缀为逗号 。
<update id="updateCustomerBySet" parameterType="com.it.pojo.Customer">update t_customer
<trim prefix="set" suffixOverrides=",">
<if test="username !=null and username !=''">
username=#{username},</if>
<if test="jobs !=null and jobs !=''"> jobs=#{jobs},</if>
<if test="phone !=null and phone !=''">phone=#{phone},</if>
</trim> where id=#{id}
</update>
复杂查询操作
<foreach>元素中的属性
<collection>属性的取值
在遍历参数时,<collection>属性的值是必须指定的。不同情况下,该属性的取值也是不一样的,主要有以下三种情况:List类型、数组类型、Map类型。
List->list
数组类型->array
Map->Map
<foreach>元素迭代数组
<foreach>实现入参为数组类型的遍历
例如,要从数据表t_customer中查询出id为1、2、3的客户信息,就可以利用数组作为参数,存储id的属性值1、2、3,并通过<foreach>元素迭代数组完成客户信息的批量查询操作。
<select id="findByArray"
resultType="com.it.pojo.Customer">
select * from t_customer where id in
<foreach item="id" index="index" collection="array"
open="(" separator="," close=")"> #{id}
</foreach>
</select>
@Test
public void SelectByArrayTest()
{
SqlSession session =MyBatisUtils.getSession();
//准备查询的条件
Integer[] ids={2,3};
List<Customer> customers= session.selectList("com.it.mapper.CustomerMapper.findByArray",ids);
for(Customer customer:customers)
{
System.out.println(customer);
}
session.close();
}
<foreach>元素迭代List
<select id="findByList"
resultType="com.it.pojo.Customer">
select * from t_customer where id in
<foreach item="id" index="index" collection="list"
open="(" separator="," close=")"> #{id}
</foreach>
</select>
@Test
public void SelectByListTest()
{
SqlSession session =MyBatisUtils.getSession();
//准备查询的条件
List<Integer> a=new ArrayList<Integer>();
a.add(1);
a.add(2);
List<Customer> customers= session.selectList("com.it.mapper.CustomerMapper.findByList",a);
for(Customer customer:customers)
{
System.out.println(customer);
}
session.close();
}
<foreach>元素迭代Map
<select id="findByMap" parameterType="java.util.Map"
resultType="com.it.pojo.Customer">
select * from t_customer where jobs=#{jobs} and id in
<foreach item="roleMap" index="index" collection="id" open="(" separator="," close=")"> #{roleMap}
</foreach>
</select>
@Test
public void findByMapTest() {
SqlSession session = MyBatisUtils.getSession();
List<Integer> ids=new ArrayList<Integer>();
ids.add(1); ids.add(2); ids.add(3);
Map<String,Object> conditionMap = new HashMap<String, Object>();
conditionMap.put("id",ids); conditionMap.put("jobs","teacher");
List<Customer> customers = session.selectList("com.it.mapper"
+ ".CustomerMapper.findByMap", conditionMap);
for (Customer customer : customers) { System.out.println(customer);}
session.close();
}
1.3案例:学生信息查询系统
多条件查询
当用户输入的学生姓名不为空,则只根据学生姓名进行学生信息的查询;
当用户输入的学生姓名为空,而学生专业不为空,则只根据学生专业进行学生的查询;
<select id="findStudentByNameAndMajor"
resultType="com.it.pojo.Student"
>
select * from dm_student where 1=1
<choose>
<when test="name!=null and name!=''">
and name like concat('%',#{name},'%')
</when>
<when test="major!=null and major!=''">
and major=#{major}
</when>
<otherwise>
and sno is not null
</otherwise>
</choose>
</select>
@Test
public void findStudentByNameAndMajorTest()
{
SqlSession session= MyBatisUtils.getSession();
Student student=new Student();
student.setName("张三");
student.setMajor("英语");
List<Student> students = session.selectList("com.it.mapper.StudentMapper.findStudentByNameAndMajor",student);
for (Student student2 : students)
{
System.out.println(student2);
}
session.close();
}
单条件查询
查询出所有id值小于5的学生的信息;
<select id="findByList" parameterType="java.util.List"
resultType="com.it.pojo.Student">
select * from dm_student where id in
<foreach item="id" index="index" collection="list"
open="(" separator="," close=")">
#{id}
</foreach>
</select>
@Test
public void findByListTest() {
SqlSession session = MyBatisUtils.getSession();
List<Integer> ids=new ArrayList<Integer>();
for(int i =1;i<5;i++){
ids.add(i);}
List<Student> students = session.selectList("com.it.mapper.StudentMapper.findByList", ids);
for (Student student : students) { System.out.println(student);}
session.close();
}
总结:
!!!
创建这个包和配置文件的时候,千万不要在设置的时候写com.it.mapper应该用com/it/mapper,否则等着报错吧!