动态sql就是根据不同的条件生成不同的sql语句(在sql中添加了逻辑运算),运用动态sql可以避免拼接字符串的痛苦
1、if 标签
where后的条件都不为空:
public interface StudentMapper { List<Student> selectStudentBySexAndSname(Student student); }
配置文件:
<select id="selectStudentBySexAndSname" parameterType="Student" resultType="pers.zhb.pojo.Student"> select * from student where <if test="sex!=null and sex !=''"> sex=#{sex} </if> <if test="sname!=null and sname !=''"> and sname=#{sname} </if> </select>
test里面是判断表达式(条件), 也就是说只有条件满足的时候才会拼接上后面的语句
测试:
public void selectStudentBySexAndSname() throws Exception { String resource = "sqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student student=new Student(); student.setSex("男"); student.setSname("zhai"); List<Student> students = studentMapper.selectStudentBySexAndSname(student); for(Student stu:students){ System.out.println(stu); } }
DEBUG [main] - Find JAR URL: file:/D:/IdeaProjects/MybatisDemo/web/WEB-INF/classes/pers/zhb/mapper/StudentMapper.xml DEBUG [main] - Not a JAR: file:/D:/IdeaProjects/MybatisDemo/web/WEB-INF/classes/pers/zhb/mapper/StudentMapper.xml DEBUG [main] - Reader entry: <?xml version="1.0" encoding="UTF-8" ?> DEBUG [main] - Checking to see if class pers.zhb.mapper.StudentMapper matches criteria [is assignable to Object] DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 916419490. DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@369f73a2] DEBUG [main] - ==> Preparing: select * from student where sex=? and sname=? DEBUG [main] - ==> Parameters: 男(String), zhai(String) DEBUG [main] - <== Total: 1 Student [snumber=201811, sname=zhai, sex=男, birthday=1998-11-11, classno=tx171, point=890, phone=1234567890, email=null]
因为两个条件都不为空,因此,都要作为条件拼接sql语句,执行的时候是执行的拼接后的sql语句。
SELECT * FROM student WHERE sex='男' AND sname='zhai'
当只有一个条件不为空:分为两种情况:
当第一个条件不为空:
@Test public void test(){ SqlSession sqlSession= MybatisUtils.getSqlSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student student=new Student(); student.setSname("zhai"); List<Student> students = studentMapper.selectStudentBySexAndSname(student); for(Student stu:students){ System.out.println(stu); } }
测试的时候只给sname赋值,也就是说sex为空,sex后的语句不会拼接,相当于执行下面的语句,这是不符合sql的语法规则的,测试的时候会报错:
SELECT *
FROM student
WHERE
AND sname='zhai'
解决方案:在where后main添加语句:1=1,相当于执行下面的sql语句:
SELECT * FROM student WHERE 1=1 AND sname='zhai'
配置文件:
<select id="selectStudentBySexAndSname" parameterType="pers.zhb.pojo.Student" resultType="pers.zhb.pojo.Student"> select * from student where 1=1 <if test="sex!=null and sex !=''"> sex=#{sex} </if> <if test="sname!=null and sname !=''"> and sname=#{sname} </if> </select>
但是,当第二个条件为空的时候字符串拼接后依旧符合sql的语法规则,即使不添加1=1的条件依旧可以正常执行sql语句:
@Test public void test(){ SqlSession sqlSession= MybatisUtils.getSqlSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student student=new Student(); student.setSex("男"); List<Student> students = studentMapper.selectStudentBySexAndSname(student); for(Student stu:students){ System.out.println(stu); } }
此时,执行的sql语句为:
SELECT *
FROM student
WHERE sex='男'
2、where 标签
(1)分别注释掉setsex和setsname:
配置文件:
<select id="selectStudentBySexAndSname" parameterType="Student" resultType="pers.zhb.pojo.Student"> select * from student where <if test="sex!=null and sex !=''"> sex=#{sex} </if> <if test="sname!=null and sname !=''"> and sname=#{sname} </if> </select>
测试:
public void selectStudentBySexAndSname() throws Exception { String resource = "sqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student student=new Student(); student.setSex("男"); student.setSname("zhai"); List<Student> students = studentMapper.selectStudentBySexAndSname(student); for(Student stu:students){ System.out.println(stu); } }
当注释掉 student.setSex("男");的时候报错,而注释掉student.setSname("zhai");的时候不会报错,这是因为注释掉后sql语句的字符串拼接出现错误。
注释掉student.setSex("男");后相当于:
<select id="selectStudentBySexAndSname" parameterType="Student" resultType="pers.zhb.pojo.Student"> select * from student where <if test="sname!=null and sname !=''"> and sname=#{sname} </if> </select>
即:where和and直接相连,这是不符合sql语句规范的,因此,需要添加1=1,来拼接sql语句使其能够表达完整含义:
<select id="selectStudentBySexAndSname" parameterType="Student" resultType="pers.zhb.pojo.Student"> select * from student where 1=1 <if test="sex!=null and sex !=''"> sex=#{sex} </if> <if test="sname!=null and sname !=''"> and sname=#{sname} </if> </select>
(2)为了解决上述问题,可以添加where标签,添加之后不用再写1=1了。
<select id="selectStudentBySexAndSname" parameterType="Student" resultType="pers.zhb.pojo.Student"> select * from student <where> <if test="sex!=null and sex !=''"> sex=#{sex} </if> <if test="sname!=null and sname !=''"> and sname=#{sname} </if> </where> </select>
(3)当什么都不传的时候(条件都为空):
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 282828951. DEBUG [main] - ==> Preparing: select * from student DEBUG [main] - ==> Parameters: DEBUG [main] - <== Total: 11
从日志文件可以看出,是查询所有的学生信息
3、sql片段:提取sql语句的公共部分
<sql id="selector"> <include refid="selector"></include> </sql> <select id="selectStudentBySexAndSname" parameterType="Student" resultType="pers.zhb.pojo.Student"> select * from student <where> <if test="sex!=null and sex !=''"> sex=#{sex} </if> <if test="sname!=null and sname !=''"> and sname=#{sname} </if> </where> </select>
4、foreach
(1)创建接口:
public interface StudentMapper {
Student findStudentById(Integer studentno);
List<Student> findStudentByQueryVo(QueryVo vo);
Integer countStudent();
List<Student> selectAllStudent();
List<Student> selectStudentBySexAndSname(Student student);
List<Student> selectStudentByIds(Integer ids[]);
List<Student> selectStudentByIds(List<Integer> ids);
List<Student> selectStudentByIds(QueryVo vo);
}
(2)vo实现:
创建vo:
public class QueryVo implements Serializable { private static final long serialVersionUID = 1L; private Student student; List <Integer> idsList; Integer[] ids; public List<Integer> getIdsList() { return idsList; } public void setIdsList(List<Integer> idsList) { this.idsList = idsList; } public Integer[] getIds() { return ids; } public void setIds(Integer[] ids) { this.ids = ids; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } }
配置文件:
<select id="selectStudentByIds" parameterType="QueryVo" resultType="pers.zhb.pojo.Student"> select * from student <where> <foreach collection="idsList" item="studentno" separator="," open="studentno in(" close=")"> #{studentno} </foreach> </where> </select>
测试:
public void selectStudentByIds() throws Exception { String resource = "sqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); List<Integer>ids=new ArrayList<>(); ids.add(201811); ids.add(201812); ids.add(201813); QueryVo vo=new QueryVo(); vo.setIdsList(ids); List<Student> students=studentMapper.selectStudentByIds(vo); for(Student student:students){ System.out.println(student); } }
(3)数组:
配置文件:
<select id="selectStudentByIds" parameterType="QueryVo" resultType="pers.zhb.pojo.Student"> select * from student <where> <foreach collection="array" item="studentno" separator="," open="studentno in(" close=")"> #{studentno} </foreach> </where> </select>
item是遍历的每一项,分隔符为逗号,array为要遍历的集合,open是以什么开始,close是以什么结束
测试:
public void selectStudentByIds() throws Exception { String resource = "sqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Integer ids[]=new Integer[3]; ids[0]=201811; ids[1]=201812; ids[2]=201813; List<Student> students=studentMapper.selectStudentByIds(ids); for(Student student:students){ System.out.println(student); } }
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 633075331. DEBUG [main] - ==> Preparing: select * from student WHERE studentno in( ? , ? , ? ) DEBUG [main] - ==> Parameters: 201811(Integer), 201812(Integer), 201813(Integer) DEBUG [main] - <== Total: 3 Student(studentno=201811, sname=zhai, sex=男, birthday=1998-11-11, classno=80501, point=890, phone=1234567890, email=null) Student(studentno=201812, sname=zhai2, sex=男, birthday=1998-11-11, classno=80601, point=893, phone=19837372533, email=null) Student(studentno=201813, sname=zhai3, sex=男, birthday=1998-11-11, classno=80501, point=892, phone=19837372534, email=null)
(4)集合:
配置文件:
<select id="selectStudentByIds" parameterType="QueryVo" resultType="pers.zhb.pojo.Student"> select * from student <where> <foreach collection="list" item="studentno" separator="," open="studentno in(" close=")"> #{studentno} </foreach> </where> </select>
测试:
public void selectStudentByIds() throws Exception { String resource = "sqlMapConfig.xml"; InputStream in = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in); SqlSession sqlSession = sqlSessionFactory.openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); List<Integer> ids=new ArrayList<>(); ids.add(201811); ids.add(201812); ids.add(201813); List<Student> students=studentMapper.selectStudentByIds(ids); for(Student student:students){ System.out.println(student); } }
5、choose
有的时候我们不想应用到所有的条件语句,而且想从中选择一项,nyatis的choose元素有点像java中的switch语句
(1)配置:
<select id="selectStudentBySexAndSname" parameterType="pers.zhb.pojo.Student" resultType="pers.zhb.pojo.Student"> select * from student <where> <choose> <when test="sname!=null"> sname=#{sname} </when> <when test="sex!=null"> and sex=#{sex} </when> <otherwise> and classno=#{classno} </otherwise> </choose> </where> </select>
(2)测试:
所有条件都为空:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 282828951. DEBUG [main] - ==> Preparing: select * from student WHERE classno=? DEBUG [main] - ==> Parameters: null DEBUG [main] - <== Total: 0
最后一个条件不为空(otherwise不为空):
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 282828951. DEBUG [main] - ==> Preparing: select * from student WHERE classno=? DEBUG [main] - ==> Parameters: 80501(String) DEBUG [main] - <== Total: 5 Student(studentno=201811, sname=zhai, sex=男, birthday=1998-11-11, classno=80501, point=890, phone=1234567890, email=null) Student(studentno=201813, sname=zhai3, sex=男, birthday=1998-11-11, classno=80501, point=892, phone=19837372534, email=null) Student(studentno=201814, sname=zhai3, sex=男, birthday=1998-11-11, classno=80501, point=892, phone=19837372534, email=null) Student(studentno=201815, sname=qwerr, sex=男, birthday=1998-11-11, classno=80501, point=892, phone=19837372534, email=null) Student(studentno=201816, sname=jiayou, sex=男, birthday=1998-11-11, classno=80501, point=892, phone=19837372534, email=null)
最后一个条件不为空,前面的条件有一个不为空:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 22069592. DEBUG [main] - ==> Preparing: select * from student WHERE sname=? DEBUG [main] - ==> Parameters: zhai(String) DEBUG [main] - <== Total: 1 Student(studentno=201811, sname=zhai, sex=男, birthday=1998-11-11, classno=80501, point=890, phone=1234567890, email=null)
可以见到只有在所有的条件都为空的情况下,才会执行otherwise里面的条件,前面的只要有一个不为空就会执行此条件而舍弃otherwise里面的条件。
都不为空:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 22069592. DEBUG [main] - ==> Preparing: select * from student WHERE sname=? DEBUG [main] - ==> Parameters: zhai(String) DEBUG [main] - <== Total: 1 Student(studentno=201811, sname=zhai, sex=男, birthday=1998-11-11, classno=80501, point=890, phone=1234567890, email=null)
优先执行第一个条件
6、模块的提取
使用标签将sql片段提取出来(公共部分),然后在另一段配置文件中引入即可,可以实现代码的复用
<sql id="iftest"> <choose> <when test="sname!=null"> sname=#{sname} </when> <when test="sex!=null"> and sex=#{sex} </when> <otherwise> and classno=#{classno} </otherwise> </choose> </sql> <select id="selectStudentBySexAndSname" parameterType="pers.zhb.pojo.Student" resultType="pers.zhb.pojo.Student"> select * from student <where> <include refid="iftest"></include> </where> </select>
测试:
DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - PooledDataSource forcefully closed/removed all connections. DEBUG [main] - Opening JDBC Connection DEBUG [main] - Created connection 22069592. DEBUG [main] - ==> Preparing: select * from student WHERE sname=? DEBUG [main] - ==> Parameters: zhai(String) DEBUG [main] - <== Total: 1 Student(studentno=201811, sname=zhai, sex=男, birthday=1998-11-11, classno=80501, point=890, phone=1234567890, email=null) Process finished with exit code 0