一、项目目标
本项目实现对MyBatis的动态SQL的操作,包括使用动态SQL进行条件查询、更新以及复杂查询操作。本项目要求利用MyBatis的动态SQL的知识完成一个学生信息查询系统。
该系统要求实现2个以下功能:
(1)多条件查询
1、当用户输入的学生姓名不为空,则只根据学生姓名进行学生信息的查询;
2、 当用户输入的学生姓名为空,而学生专业不为空,则只根据学生专业进行学生的查询;
3、 当用户输入的学生姓名和专业都为空,则要求查询出所有学号不为空的学生信息。
(2)单条件查询查询出所有id值小于5的学生的信息
二、实现步骤
功能1、多条件查询
1.项目搭建
创建一个名称为mybatis-demo02的Maven项目
2.数据准备
在名称为mydb的数据库中,创建一个dm_student表,并插入几条测试数据,具体代码如下所示。
# 使用mydb数据库
USE mybatis;
# 创建一个名称为dm_student的表
CREATE TABLE dm_student(
id int(32) PRIMARY KEY AUTO_INCREMENT,
name varchar(50),
major varchar(50),
sno varchar(16)
);
# 插入7条数据
INSERT INTO dm_student VALUES ('1', '张三', '数学', '10001');
INSERT INTO dm_student VALUES ('2', '李四', '英语', '10002');
INSERT INTO dm_student VALUES ('3', '王五', '计算机', '10003');
INSERT INTO dm_student VALUES ('4', '王刚', '化学', '10004');
INSERT INTO dm_student VALUES ('5', '李华', '物理', '10005');
INSERT INTO dm_student VALUES ('6', '李雷', '中文', '10006');
INSERT INTO dm_student VALUES ('7', '张飞', '英语', '10007');
实现效果
3.POJO类准备
在项目src/main/java目录下创建org.example包,在org.example包下创建持久化类Student,在类中声明id、name、major和sno属性,以及属性对应的getter/setter方法。Student类具体代码如下所示
package org.example;
/**
* 学生信息查询系统学生持久化类POJO类
*/
public class Student {
private Integer id; //主键id
private String name; // 姓名
private String major; // 专业
private String sno; // 学号
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", major='" + major + '\'' +
", sno='" + sno + '\'' +
'}';
}
}
4.创建映射文件
在项目src/main/resources目录下创建org.example包,在org.example包下创建映射文件TestStudent.xml,在映射文件中,编写根据学生姓名和专业组合成的条件查询学生信息的动态SQL。TestStudent.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="org.example.TestStudent">
<select id="findStudentByNameAndMajor"
parameterType="student"
resultType="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>
</mapper>
在上述代码中,第9~19行代码使用<choose>元素进行SQL拼接,当第一个<when>元素中的条件为真时,只动态组装第一个<when>元素内的SQL片段并执行,否则就继续向下判断第二个<when>元素中的条件是否为真,以此类推,直到某一个<when>元素中的条件为真。当前面所有when元素中的条件都不为真时,则动态组装<otherwise>元素内的SQL片段并执行。
Tip:<choose>(<when>、<otherwise>)相当于Java中的switch...case...default语句,用于多条件判断。
5.修改mybatis-config.xml核心配置文件
在mybatis-config.xml映射文件的<mappers>元素下添加TestStudent.xml映射文件路径的配置,用于将TestStudent.xml映射文件加载到程序中。具体配置代码如下。
<mappers>
<mapper resource="org.example/TestStudent.xml"/>
<!-- 此处mapper配置的目的是将mapper.xml映射交给mybatis-->
</mappers>
6.编写MyBatisUtils工具类
在项目src/main/java目录下创建Utils包,在utils包下创建MyBatisUtils工具类,该类用于封装读取配置文件信息的代码
package Utils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.Reader;
import static org.apache.ibatis.io.Resources.getResourceAsReader;
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory = null;
static { try {
// 使用MyBatis提供的Resources类加载MyBatis的配置文件
Reader reader = getResourceAsReader("mybatis-config.xml");
// 构建SqlSessionFactory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) { e.printStackTrace();}}
public static SqlSession getSession() {//获取SqlSession对象的静态方法
return sqlSessionFactory.openSession();}
}
//创建MyBatisUtils工具类,该类用于封装读取配置文件信息的代码。
//第16~17行代码读取mybatis-config.xml文件内容到reader对象中;
//第19~20行代码创建SqlSessionFactory类的实例;
//第26~28行代码是定义了一个getSession()静态方法,
//并在静态方法中通过SqlSessionFactory类的实例创建SqlSession实例。
7.编写测试方法
(1)编写测试接口
在org.example包下创建在org.example包下创建持久化类Student的接口文件用于测试
package org.example;
import java.util.List;
/**
* 接口式开发
* 根据学生姓名或职业查询学生信息列表
* 创建接口
* 1、方法名称保证和映射文件的sql语句的statmentId一致
* ctrl+shift+t,鼠标悬停接口名,创建接口测试类
* alt+enter,快速导入类
*/
public interface TestStudent {
List<Student> findStudentByNameAndMajor(Student student);
}
(2)编写测试方法
package org.example;
import Utils.MyBatisUtils;
import junit.framework.TestCase;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import java.util.List;
public class TestStudentTest extends TestCase {
SqlSession sqlSession=null;
TestStudent testStudent=null;
// 获取接口动态代理对象
@Before
public void setUp() throws Exception {
sqlSession = MyBatisUtils.getSession();
testStudent = sqlSession.getMapper(TestStudent.class);
}
public void testFindStudentByNameOrMajorTest() {
// 通过工具类生成SqlSession对象
SqlSession session = MyBatisUtils.getSession();
// Student,封装需要组合查询的条件
Student student = new Student();
student.setName("张三");
student.setMajor("英语");
List<Student> students = testStudent.findStudentByNameAndMajor(student);
// 输出查询结果信息
for (Student student2 : students) {
// 打印输出结果
System.out.println(student2);
}
// 关闭SqlSession
session.close();
}
}
上述代码中,第7行代码通过MyBatisUtils工具类获取SqlSession对象;第9~11行代码创建Student对象,封装需要组合查询的条件;第13~14行代码执行SqlSession的查询方法,返回结果集;第16~19行代码使用foreach循环打印查询结果信息;第21行代码关闭SqlSession,释放资源。
功能2、单条件查询
1.修改映射文件
在映射文件TestStudent.xml中的<mapper>元素下,编写查询所有id值小于5的学生信息的动态SQL。具体代码如下所示。
<!--<foreach>遍历List -->
<select id="findByList" parameterType="java.util.List"
resultType="student">
select * from dm_student where id in
<foreach item="id" index="index" collection="list"
open="(" separator="," close=")">
#{id}
</foreach>
</select>
上述配置代码中,使用<foreach>元素迭代List集合,实现学生信息的批量查询操作。其中,<foreach>元素的collection属性用于设置传入的参数为List类型,<foreach>元素将学生id信息存储在List集合中,并对List集合进行遍历,遍历出的值用于构建SQL语句中的in条件语句。
2.编写测试方法
为了验证上述配置,可以在测试类MyBatisTest中,编写测试方法findByListTest(),findByListTest()具体代码如下所示。
(1)编写测试接口
package org.example;
import java.util.List;
/**
* 接口式开发
* 根据学生姓名或职业查询学生信息列表
* 创建接口
* 1、方法名称保证和映射文件的sql语句的statementId一致
* ctrl+shift+t,鼠标悬停接口名,创建接口测试类
* alt+enter,快速导入类
*/
public interface TestStudent {
// 多条件查询
// List<Student> findStudentByNameAndMajor(Student student);
// 单条件查询
List<Student> findByListTest(Student student);
}
(2)测试方法(关键部分)
public void testTestFindByListTest() {
// 获取 SqlSession
SqlSession session = MyBatisUtils.getSession();
// 创建 List 集合,封装查询 id
List<Integer> ids = new ArrayList<>();
// 将小于5的 id 值放入 list 中
for (int i = 1; i < 5; i++) {
ids.add(i);
}
// 执行查询方法
List<Student> students = testStudent.findByListTest(ids);
// 输出查询结果信息
for (Student student3 : students) {
System.out.println(student3);
}
// 关闭 SqlSession
session.close();
}
}
三、项目效果
功能1、多条件查询
(1)只根据学生姓名进行学生信息的查询
执行测试类MyBatisTest的findStudentByNameOrMajorTest()方法,控制台的输出结果如图3-1所示。
图3-1.findStudentByNameOrMajorTest()方法执行结果(1)
由图3-1中的输出结果分析可知,在查询学生信息时,虽然同时传入了姓名和专业两个查询条件,但MyBatis所生成的SQL只是动态组装了学生姓名条件进行查询。
(2)当用户输入的学生姓名为空,而学生专业不为空,则只根据学生专业进行学生的查询
如果将上述代码中的第10行代码“student.setName("张三");”删除或者注释掉,使SQL只按专业进行查询。再次执行findStudentByNameOrMajorTest ()方法,控制台的输出结果
图3-2.findStudentByNameOrMajorTest()方法执行结果(2)
由图3-2中的输出结果分析可知,MyBatis生成的SQL组装了学生职业进行条件查询,同样查询出了学生信息。
(3)当用户输入的学生姓名和专业都为空,则要求查询出所有学号不为空的学生信息
如果将上述代码中的第10~11行代码都删除或者注释掉(即学生姓名和专业都为空),那么程序的执行结果如图3-3所示。
图3-3.findStudentByNameOrMajorTest()方法执行结果(3)
由图3-3中的输出结果分析可知,当姓名和专业参数都为空时,MyBatis的SQL组装了<otherwise>元素中的SQL片段进行条件查询。
功能2、单条件查询
执行MyBatisTest测试类的findByListTest()方法
四、总结
1.MyBatis动态SQL中的常用元素及其作用。
<if>:判断语句,用于单条件判断。
<choose>(<when>、<otherwise>):相当于Java中的switch...case...default语句,用于多条件判断。
<where>:简化SQL语句中where的条件判断。
<trim>:可以灵活地去除多余的关键字。
<set>:用于SQL语句的动态更新。
<foreach>:循环语句,常用于in语句等列举条件中。
2.在使用<foreach>时,collection属性需要注意的几点:
(1)如果传入的是单参数且参数类型是一个数组或者List的时候,collection属性值分别为array和list(或collection);
(2)如果传入的参数是多个的时候,就需要把它们封装成一个Map了,当然单参数也可以封装成Map集合,这时候collection属性值就为Map的键。
(3)如果传入的参数是POJO包装类的时候,collection属性值就为该包装类中需要进行遍历的数组或集合的属性名。