在上一章中我们学习了《MyBatis学习总结(二)——MyBatis核心配置文件与输入输出映射》,这一章主要是介绍一对一关联查询、一对多关联查询与动态SQL等内容。
一、多表关联查询
表与表之间有三种常见的关联关系,分别是一对一,一对多与多对多关系,MyBatis直接提供一对一与一对多的关联关系,可以通过间接的方式实现多对多关联。
1.1、一对一关系
1.1.1、执行环境
假定一个员工(emp)拥有一个登录用户(user),员工与用户表之间是一对一关系:
用户表:
员工表:
SQL:
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号', `username` varchar(64) NOT NULL COMMENT '用户名', `password` varchar(64) NOT NULL COMMENT '密码', PRIMARY KEY (`id`), UNIQUE KEY `users_username_uindex` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='用户表'; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', 'tom', '123456'); INSERT INTO `user` VALUES ('2', 'rose', '888888'); INSERT INTO `user` VALUES ('3', 'mark', 'qwerty'); INSERT INTO `user` VALUES ('4', 'jack', 'qaz123'); INSERT INTO `user` VALUES ('5', 'mali', 'uio890'); SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `emp` -- ---------------------------- DROP TABLE IF EXISTS `emp`; CREATE TABLE `emp` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号', `user_id` int(11) DEFAULT NULL COMMENT '用户编号', `realname` varchar(32) NOT NULL COMMENT '姓名', `email` varchar(64) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (`id`), KEY `emp_user_id` (`user_id`), CONSTRAINT `emp_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='员工表'; -- ---------------------------- -- Records of emp -- ---------------------------- INSERT INTO `emp` VALUES ('1', '1', '汤姆', 'tom@gmail.com'); INSERT INTO `emp` VALUES ('2', '2', '梅贵', 'rose@163.com'); INSERT INTO `emp` VALUES ('3', '3', '马克', 'mark@sina.com'); INSERT INTO `emp` VALUES ('4', '4', '岳翰', 'jack@gmail.com'); INSERT INTO `emp` VALUES ('5', '5', '马丽', 'mali@sina.com');
关系:
1.1.2、关联查询(1次查询)
实体:
用户:
package com.zhangguo.mybatis03.entities; /**用户POJO*/ public class User { private int id; private String username; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
员工:
package com.zhangguo.mybatis03.entities; /**员工POJO*/ public class Emp { private int id; /**用户编号*/ private int user_id; private String realname; private String email; /**用户对象*/ private User user; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUser_id() { return user_id; } public void setUser_id(int user_id) { this.user_id = user_id; } public String getRealname() { return realname; } public void setRealname(String realname) { this.realname = realname; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public User getUser() { return user; } public Emp setUser(User user) { this.user = user; return this; } }
接口:
package com.zhangguo.mybatis03.dao; import com.zhangguo.mybatis03.entities.Emp; /**员工数据访口*/ public interface EmpMapper { /**获得员工通过员工编号*/ Emp getEmpById_1(int id); }
映射:
<?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.zhangguo.mybatis03.dao.EmpMapper"> <!--一对一查询,方法1,通过内联接--> <select id="getEmpById_1" resultMap="empMap_1" parameterType="int"> SELECT emp.id, emp.user_id, emp.realname, emp.email, `user`.username, `user`.`password` FROM emp INNER JOIN `user` ON emp.user_id = `user`.id where emp.id=#{id} </select> <!--员工关联查询结果映射--> <resultMap id="empMap_1" type="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> <!--映射关系,指定属性与属性的类型--> <association property="user" javaType="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> </association> </resultMap> </mapper>
测试:
package com.zhangguo.mybatis03.dao; import com.zhangguo.mybatis03.entities.Emp; import org.junit.Assert; import org.junit.Test; import org.junit.Before; import org.junit.After; /** * EmpDao Tester. * * @author <Authors name> * @version 1.0 * @since <pre>09/30/2018</pre> */ public class EmpDaoTest { EmpMapper empDao; @Before public void before() throws Exception { empDao=new EmpDao(); } @After public void after() throws Exception { } /** * Method: getEmpById_1(int id) * 获得员工通过员工编号 */ @Test public void testGetEmpById_1() throws Exception { Emp entity=empDao.getEmpById_1(1); System.out.println(entity); Assert.assertNotNull(entity); } }
结果:
1.1.3、嵌套查询(2次查询)
实体:同上
接口:
/**获得员工通过员工编号,多次查询*/ Emp getEmpById_2(int id);
映射:
<!--一对一查询,方法2,通过多次查询(嵌套查询)--> <select id="getEmpById_2" resultMap="empMap_2"> SELECT emp.id, emp.user_id, emp.realname, emp.email FROM emp where id=#{id} </select> <!--员工多次查询结果映射--> <resultMap id="empMap_2" type="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> <!--通过外键user_id再次发起查询,调用selectUserById获得User对象--> <association property="user" column="user_id" select="selectUserById"></association> </resultMap> <!--根据用户编号获得用户对象--> <select id="selectUserById" resultType="User"> SELECT `user`.id, `user`.username, `user`.`password` FROM `user` where id=#{id} </select>
测试:
/** * Method: getEmpById_2(int id) * 获得员工通过员工编号,一对一方法二 */ @Test public void testGetEmpById_2() throws Exception { Emp entity=empDao.getEmpById_2(2); System.out.println(entity); Assert.assertNotNull(entity); }
结果:
MyBatis中使用association标签来解决一对一的关联查询,association标签可用的属性如下:
- property:对象属性的名称
- javaType:对象属性的类型
- column:所对应的外键字段名称
- select:使用另一个查询封装的结果
1.2、一对多关系
1.2.1、执行环境
一个用户帐号可以被多个员工使用,形成一个一对多的关系,表中的数据如下:
员工表emp:
用户表user:
1.2.2、关联查询(1次查询)
实体:
员工:
package com.zhangguo.mybatis03.entities; /**员工POJO*/ public class Emp { private int id; /**用户编号*/ private int user_id; private String realname; private String email; /**用户对象*/ private User user; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUser_id() { return user_id; } public void setUser_id(int user_id) { this.user_id = user_id; } public String getRealname() { return realname; } public void setRealname(String realname) { this.realname = realname; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public User getUser() { return user; } public Emp setUser(User user) { this.user = user; return this; } @Override public String toString() { return "Emp{" + "id=" + id + ", user_id=" + user_id + ", realname='" + realname + '\'' + ", email='" + email + '\'' + ", user=" + user + '}'; } }
用户:
package com.zhangguo.mybatis03.entities; import java.util.List; /**用户POJO*/ public class User { private int id; private String username; private String password; /**员工集合,一个用户对象对应多个员工对象*/ private List<Emp> emps; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<Emp> getEmps() { return emps; } public User setEmps(List<Emp> emps) { this.emps = emps; return this; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", emps=" + emps + '}'; } }
接口:
/**获得用户通过用户编号,1对多级联查询*/ User getUserById_1(int id);
映射:
<!--一对多查询,方法1,通过内联接--> <select id="getUserById_1" resultMap="userMap_1" parameterType="int"> SELECT emp.id, emp.user_id, emp.realname, emp.email, `user`.username, `user`.`password` FROM emp INNER JOIN `user` ON emp.user_id = `user`.id where `user`.id=#{id} </select> <resultMap id="userMap_1" type="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> <!--将emps对象映射成一个集合,emps是user类型中的属性,ofType用于指定集合中存放的对象类型--> <collection property="emps" ofType="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> </collection> </resultMap>
测试:
/** * Method: getUserById_1(int id) * 获得用户过用户编号,级联查询 */ @Test public void testGetUserById_1() throws Exception { User entity=empDao.getUserById_1(2); System.out.println(entity); Assert.assertNotNull(entity); }
结果:
上面的示例中会发现User对象中包含多个Emp对象,此时的Emp对象中又引用了User对象,但值是空的,如果想设置值可以继续用1对1的办法赋值:
映射:
<resultMap id="userMap_1" type="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> <!--将emps对象映射成一个集合,emps是user类型中的属性,ofType用于指定集合中存放的对象类型--> <collection property="emps" ofType="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> <!--映射关系,指定属性与属性的类型--> <association property="user" javaType="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> </association> </collection> </resultMap>
结果:
1.1.3、嵌套查询(多次查询)
实体:同上
接口:
/**获得用户通过用户编号,1对多嵌套查询*/ User getUserById_2(int id);
映射:
<!--一对多查询,方法2,通过嵌套查询多次--> <select id="getUserById_2" resultMap="userMap_2" parameterType="int"> SELECT `user`.id, `user`.username, `user`.`password` FROM `user` where id=#{id} </select> <resultMap id="userMap_2" type="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> <!--将emps对象映射成一个集合,emps是user类型中的属性,ofType用于指定集合中存放的对象类型--> <!--select用于指定再次查询的SQL编号,column用于指定参数列--> <collection property="emps" ofType="Emp" column="id" select="selectEmpById"></collection> </resultMap> <!--根据员工编号获得员工对象--> <select id="selectEmpById" resultType="Emp"> SELECT emp.id, emp.user_id, emp.realname, emp.email FROM emp where user_id=#{id} </select>
测试:
/** * Method: getUserById_2(int id) * 获得用户过用户编号,嵌套查询 */ @Test public void testGetUserById_2() throws Exception { User entity=empDao.getUserById_2(5); System.out.println(entity); Assert.assertNotNull(entity); }
结果:
MyBatis中使用collection标签来解决一对多的关联查询,ofType属性指定集合中元素的对象类型。
二、动态SQL
2.0、MySQL环境与前置要求
数据与SQL环境如下:
前置要求:
2.1、什么是动态SQL
MyBatis的动态SQL是基于OGNL的表达式的。它对SQL语句进行灵活的操作,通过表达式判断来实现对SQL的灵活拼接、组装。
mybatis核心对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
主要通过以下标签:if,where,choose(when,otherwise),trim,set,foreach。
2.2、if条件判断
根据 name和 sex 来查询数据。如果name为空,那么将只根据sex来查询;反之只根据name来查询
首先不使用 动态SQL 来书写
接口:
/** * 根据学生姓名和性别获得学生集合 */ List<Student> selectStudentsByNameAndSex(@Param("name") String name,@Param("sex") String sex);
映射:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student where name=#{name} and sex=#{sex}; </select>
测试:
/** * Method: selectStudentsByNameAndSex */ @Test public void testSelectStudentsByNameAndSex() throws Exception { List<Student> students=dao.selectStudentsByNameAndSex("rose",null); System.out.println(students); Assert.assertNotNull(students); }
结果:
上面的查询语句,我们发现如果 #{sex} 为空,那么查询结果也是空,如何解决这个问题呢?使用 if 来判断
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student where 1=1 <!--如果test为真会输出中间的内容--> <if test="name!=null and name!=''"> and name=#{name} </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </select>
结果:
参考:
<!-- 2 if(判断参数) - 将实体类不为空的属性作为where条件 --> <select id="getStudentList_if" resultMap="resultMap_studentEntity" parameterType="liming.student.manager.data.model.StudentEntity"> SELECT ST.STUDENT_ID, ST.STUDENT_NAME, ST.STUDENT_SEX, ST.STUDENT_BIRTHDAY, ST.STUDENT_PHOTO, ST.CLASS_ID, ST.PLACE_ID FROM STUDENT_TBL ST WHERE <if test="studentName !=null "> ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName, jdbcType=VARCHAR}),'%') </if> <if test="studentSex != null and studentSex != '' "> AND ST.STUDENT_SEX = #{studentSex, jdbcType=INTEGER} </if> <if test="studentBirthday != null "> AND ST.STUDENT_BIRTHDAY = #{studentBirthday, jdbcType=DATE} </if> <if test="classId != null and classId!= '' "> AND ST.CLASS_ID = #{classId, jdbcType=VARCHAR} </if> <if test="classEntity != null and classEntity.classId !=null and classEntity.classId !=' ' "> AND ST.CLASS_ID = #{classEntity.classId, jdbcType=VARCHAR} </if> <if test="placeId != null and placeId != '' "> AND ST.PLACE_ID = #{placeId, jdbcType=VARCHAR} </if> <if test="placeEntity != null and placeEntity.placeId != null and placeEntity.placeId != '' "> AND ST.PLACE_ID = #{placeEntity.placeId, jdbcType=VARCHAR} </if> <if test="studentId != null and studentId != '' "> AND ST.STUDENT_ID = #{studentId, jdbcType=VARCHAR} </if> </select>
虽然1=1这种方法结合if可以解决我们的需求,但是1=1明显是冗余的,通过where可以解决。
2.3、where条件
where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。
修改后的映射:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--1、如果两个if只要有一个有输出就会在sql中添加 where--> <where> <if test="name!=null and name!=''"> <!--2、如果where后以and或or开始则会删除and或or--> and name like concat(concat('%',#{name}),'%'); </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </where> </select>
测试:
/** * Method: selectStudentsByNameAndSex */ @Test public void testSelectStudentsByNameAndSex() throws Exception { List<Student> students=dao.selectStudentsByNameAndSex("a",null); System.out.println(students); Assert.assertNotNull(students); }
结果:
这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
where标记的作用类似于动态sql中的set标记,他的作用主要是用来简化sql语句中where条件判断的书写的,如下所示:
<select id="selectByParams" parameterType="map" resultType="user"> select * from user <where> <if test="id != null ">id=#{id}</if> <if test="name != null and name.length()>0" >and name=#{name}</if> <if test="gender != null and gender.length()>0">and gender = #{gender}</if> </where> </select>
在上述SQL中加入ID的值为null的话,那么打印出来的SQL为:select * from user where name="xx" and gender="xx"
where 标记会自动将其后第一个条件的and或者是or给忽略掉
2.4、if+set设置值
当update语句中没有使用if标签时,如果有一个参数为null,都会导致错误。
当在update语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置SET 关键字,和剔除追加到条件末尾的任何不相关的逗号。如果set包含的内容为空的话则会出错。
使用if+set标签修改后,如果某项为null则不进行更新,而是保持数据库原值。
如果通过if判断表面可以解决问题,如下所示:
<update id="updateStudent" parameterType="student"> update student set <if test="name!=null and name.lenght()>0"> name=#{name} , </if> <if test="sex!=null and sex.lenght()>0"> sex=#{sex} </if> where id=#{id} </update>
这样做也会有问题,就是当sex为空时的sql就变成了 update student set name=#{name} , where id=#{id},这明显是错误的。
同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?
接口:
/** * 更新学生 */ int updateStudent(Student entity);
映射:
<update id="updateStudent" parameterType="student"> update student <!--自动添加set--> <set> <!--智能处理逗号问题--> <if test="name!=null and name.length()>0"> name=#{name} </if> <if test="sex!=null and sex.length()>0"> sex=#{sex} </if> </set> where id=#{id} </update>
注意:某些情况下逗号必须添加,如下所示:
<update id="updateStudent" parameterType="student"> update student <!--自动添加set--> <set> <!--智能处理逗号问题--> <if test="name!=null and name.length()>0"> name=#{name} , </if> <if test="sex!=null and sex.length()>0"> sex=#{sex} , </if> </set> where id=#{id} </update>View Code
结尾的逗号会被自动删除。
测试:
/** * Method: updateStudent */ @Test public void testUpdateStudent() throws Exception { //会将实体中的每一个字段都更新,不好 // Student entity=dao.selectStudentById(11); // //entity.setName("张丽美"); // entity.setSex("girl"); // // Assert.assertEquals(1,dao.updateStudent(entity)); //不需要先执行查询 Student student=new Student(); student.setId(9); //只更新了name与sex没有关系 student.setName("malili"); Assert.assertEquals(1,dao.updateStudent(student)); }
结果:
这样写,如果第一个条件 name 为空,那么 sql 语句为:update student set sex=? where id=?
如果第一个条件不为空,那么 sql 语句为:update student u set name= ? , sex = ? where id=?
set主要解决了自动添加标签与处理逗号的问题,另外这种更新方法比较以前的全部更新方式在开发中性能更高。
2.5、choose(when,otherwise) 开关
如果不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句。
假定这里需要优先根据编号搜索,没有时选择name,最后考虑sex:
接口:
/** * 根据学生编号、姓名和性别获得学生集合 */ List<Student> selectStudentsByNameAndSex(@Param("id") int id, @Param("name") String name,@Param("sex") String sex);
映射:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <where> <choose> <when test="id>0"> id=#{id} </when> <when test="name!=null and name!=''"> name=#{name} </when> <otherwise> sex=#{sex} </otherwise> </choose> </where> </select>
测试:
/** * Method: selectStudentsByNameAndSex */ @Test public void testSelectStudentsByNameAndSex() throws Exception { List<Student> students=dao.selectStudentsByNameAndSex(1,"rose","girl"); System.out.println(students); Assert.assertNotNull(students); }
结果:
也就是说,这里我们有三个条件,id,name,sex,只能选择一个作为查询条件
如果 id 不为空,那么查询语句为:select * from student where id=?
如果 id 为空,那么看name是否为空,如果不为空,那么语句为 select * from student where name=?;
如果name为空,那么查询语句为 select * from student where sex=?
2.6、trim裁剪
trim标记是一个格式化的标记,可以完成set或者是where标记的功能
①、用 trim 改写上面第二点的 if+where 语句
if+where的办法:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--1、如果两个if只要有一个有输出就会在sql中添加 where--> <where> <if test="name!=null and name!=''"> <!--2、如果where后以and或or开始则会删除and或or--> and name like concat(concat('%',#{name}),'%'); </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </where> </select>
trim的办法:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--1、prefix表示将前置where,prefixOverrides将删除打头内容--> <trim prefix="where" prefixOverrides="and | or"> <if test="name!=null and name!=''"> and name like concat(concat('%',#{name}),'%') </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </trim> </select>
测试结果:
prefix:将加上前缀
prefixoverride:去掉第一个and或者是or
②、用 trim 改写上面第三点的 if+set 语句
if+set的方法:
<update id="updateStudent" parameterType="student"> update student <!--自动添加set--> <set> <!--智能处理逗号问题--> <if test="name!=null and name.length()>0"> name=#{name} </if> <if test="sex!=null and sex.length()>0"> sex=#{sex} </if> </set> where id=#{id} </update>
trim的方法:
<update id="updateStudent" parameterType="student"> update student <trim prefix="set" suffixOverrides=","> <if test="name!=null and name.length()>0"> name=#{name}, </if> <if test="sex!=null and sex.length()>0"> sex=#{sex}, </if> </trim> where id=#{id} </update>
结果:
suffix:后缀
suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)
可以自定义添加前后缀,与之对应的属性是prefix和suffix。同时通过prefixOverrides和suffixOverrides分别来覆盖首尾部的内容,即忽略不必要的前后缀。就是说它可以充当where标签,也可以充当set标签啦~
充当where标签:
<trim prefix = "where" prefixOverrides="and|or" > ... </trim>
充当set标签:
<trim prefix="set" suffixOverrides=","> ... </trim>
例子:动态添加用户属性
<insert id="find" resultType="Admin"> insert into admin <trim prefix="(" suffix=")" suffixOverrides=","> <if test = "aname != null and aname !='' "> aname, </if> <if test = "city != null and city !='' "> city, </if> <if test = "age != null and age !='' "> age, </if> </trim> <trim prefix="values(" suffix=")" suffixOverrides=","> <if test = "aname != null and aname !='' "> #{aname}, </if> <if test = "city != null and city !='' "> #{city}, </if> <if test = "age != null and age !='' "> #{age}, </if> </trim> </insert>
上面相应的语句为:insert into admin (…) values(…);。通过trim标签用()包裹,以及自动忽略尾部的逗号。
2.7、SQL 片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
比如:下面的映射文件中对于id,name,sex出现多次:
<select id="selectStudentsByNameOrSex" resultType="student"> SELECT id,name,sex from student where name like '%${realname}%' or sex=#{sex}; </select> <select id="selectStudentsByIdOrSex" resultType="student"> SELECT id,name,sex from student where id=#{no} or sex=#{sex}; </select> <select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--1、prefix表示将前置where,prefixOverrides将删除打头内容--> <trim prefix="where" prefixOverrides="and | or"> <if test="name!=null and