MyBatis深入
typora-copy-images-to: mybatis
typora-root-url: mybatis
MyBatis深入
一 Mybatis主键映射
1.1主键映射作用
当数据插入操作不关心插入后数据的主键(唯一标识),那么建议使用 不返回自增主键值 的方式来配置插入语 句,这样可以避免额外的SQL开销.
当执行插入操作后需要立即获取插入的**自增主键值**,比如一次操作中保存一对多这种关系的数据,那么就要使用插入后获取自增主键值的方式配置.
mybatis进行插入操作时,如果表的主键是自增的,针对不同的数据库相应的操作也不同。基本上经常会遇到的就是**Oracle Sequece** 和 **Mysql** **自增主键**,解释如下。
1.2自动递增返回主键
一对多的那种表结构,在插入多端数据时,需要获取刚刚保存了的一端的主键。那么这个时候,上述的配置就无法满足需要了。为此我们需要使用mybatis提供的<selectKey />来单独配置针对自增逐渐的处理。
- Oracle Sequence配置
<sql id='TABLE_NAME'>TEST_USER</sql>
<sql id='TABLE_SEQUENCE'>SEQ_TEST_USER_ID.nextval</sql>
<!-- 注意这里需要先查询自增主键值 -->
<insert id="insertOrcle" parameterType="User">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select
<include refid="TABLE_SEQUENCE" />
from dual
</selectKey>
insert into
<include refid="TABLE_NAME" />
(ID,NAME,AGE) values ( #{id}, #{name}, #{age} )
</insert>
当使用了后,在实际的插入操作时,mybatis会执行以下两句SQL:
在执行插入 语句2 之前,会先执行
语句1 以获取当前的ID值,然后mybatis使用反射调用User对象的setId方法,将 语句1 查询出的值保存在User对象中,然后才执行
语句2 这样就保证了执行完插入后
@Test
public void testInsertOracle(){
User u=new User("小毛", 18);
int count = mapper.insertOrcle(u);
System.out.println(count>0?"新增成功":"新增失败");
System.out.println("新增主键是:"+u.getId());
}
user.id 是有值的
- Mysql自增主键配置
针对于Mysql这种自己维护主键的数据库,可以直接使用以下配置在插入后获取插入主键
<sql id='TABLE_NAME'>TEST_USER</sql>
<insert id="insertMySql" useGeneratedKeys="true" keyProperty="id" parameterType="User">
insert into
<include refid="TABLE_NAME" />
( NAME, AGE ) values ( #{name}, #{age} )
</insert>
当然,由于Mysql的自增主键可以通过SQL语句
select LAST_INSERT_ID();
来获取的。因此针对Mysql,Mybatis也可配置如下:
<sql id='TABLE_NAME'>TEST_USER</sql>
<!-- 注意这里需要先查询自增主键值 -->
<insert id="insertMySql" parameterType="User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into
<include refid="TABLE_NAME" />
(ID,NAME,AGE) values ( #{id}, #{name}, #{age} )
</insert>
只不过该中配置需要额外的一条查询SQL!
二 关联映射
2.1关联映射作用
在现实的项目中进行数据库建模时,我们要遵循数据库设计范式的要求,会对现实中的业务模型进行拆分,封装在不同的数据表中,表与表之间存在着一对多或是多对多的对应关系。进而,我们对数据库的增删改查操作的主体,也就从单表变成了多表。那么Mybatis中是如何实现这种多表关系的映射呢?
-
查询结果集ResultMap
resultMap元素是 MyBatis中最重要最强大的元素。它就是让你远离90%的需要从结果 集中取出数据的JDBC代码的那个东西,而且在一些情形下允许你做一些 JDBC 不支持的事 情。
有朋友会问,之前的示例中我们没有用到结果集,不是也可以正确地将数据表中的数据映射到Java对象的属性中吗?是的。这正是resultMap元素设计的初衷,就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们的关系。
- resultMap元素中,允许有以下直接子元素:
- constructor - 类在实例化时,用来注入结果到构造方法中(本文中暂不讲解)
- id - 作用与result相同,同时可以标识出用这个字段值可以区分其他对象实例。可以理解为数据表中的主键,可以定位数据表中唯一一笔记录
- result - 将数据表中的字段注入到Java对象属性中
- association - 关联,简单的讲,就是“有一个”关系,如“用户”有一个“帐号”
- collection - 集合,顾名思议,就是“有很多”关系,如“客户”有很多“订单”
- discriminator(鉴别器) - 使用结果集决定使用哪个结果映射(暂不涉及)
每个元素的用法及属性我会在下面结合使用进行讲解。
数据库sql语句如下
/*
Navicat MySQL Data Transfer
Source Server : mysql
Source Server Version : 50624
Source Host : localhost:3306
Source Database : mybatis1105
Target Server Type : MYSQL
Target Server Version : 50624
File Encoding : 65001
Date: 2018-11-05 14:39:43
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `class`
-- ----------------------------
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
`c_id` int(11) NOT NULL AUTO_INCREMENT,
`c_name` varchar(20) DEFAULT NULL,
`teacher_id` int(11) DEFAULT NULL,
PRIMARY KEY (`c_id`),
UNIQUE KEY `uq_teacher_Id` (`teacher_id`),
CONSTRAINT `fk_teacher_id` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`t_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of class
-- ----------------------------
INSERT INTO `class` VALUES ('1', 'Java班', '1');
INSERT INTO `class` VALUES ('2', 'H5班', '2');
-- ----------------------------
-- Table structure for `course`
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`course_id` int(11) NOT NULL AUTO_INCREMENT,
`course_name` varchar(20) DEFAULT NULL,
`course_time` int(11) DEFAULT NULL,
PRIMARY KEY (`course_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('1', 'Java', '12');
INSERT INTO `course` VALUES ('2', 'SQL', '18');
INSERT INTO `course` VALUES ('3', 'HTML', '14');
-- ----------------------------
-- Table structure for `course_student`
-- ----------------------------
DROP TABLE IF EXISTS `course_student`;
CREATE TABLE `course_student` (
`cs_id` int(11) NOT NULL AUTO_INCREMENT,
`c_id` int(11) DEFAULT NULL,
`s_id` int(11) DEFAULT NULL,
PRIMARY KEY (`cs_id`),
KEY `FK_C_ID` (`c_id`),
KEY `FK_S_ID` (`s_id`),
CONSTRAINT `FK_C_ID` FOREIGN KEY (`c_id`) REFERENCES `course` (`course_id`),
CONSTRAINT `FK_S_ID` FOREIGN KEY (`s_id`) REFERENCES `student` (`s_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of course_student
-- ----------------------------
INSERT INTO `course_student` VALUES ('1', '1', '1');
INSERT INTO `course_student` VALUES ('2', '1', '2');
INSERT INTO `course_student` VALUES ('3', '2', '1');
INSERT INTO `course_student` VALUES ('5', '2', '3');
INSERT INTO `course_student` VALUES ('6', '1', '4');
-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`s_id` int(11) NOT NULL AUTO_INCREMENT,
`s_name` varchar(20) DEFAULT NULL,
`class_id` int(11) DEFAULT NULL,
PRIMARY KEY (`s_id`),
KEY `FK_class_id` (`class_id`),
CONSTRAINT `FK_class_id` FOREIGN KEY (`class_id`) REFERENCES `class` (`c_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', 'student_A', '1');
INSERT INTO `student` VALUES ('2', 'student_B', '1');
INSERT INTO `student` VALUES ('3', 'student_C', '1');
INSERT INTO `student` VALUES ('4', 'student_D', '2');
INSERT INTO `student` VALUES ('5', 'student_E', '2');
INSERT INTO `student` VALUES ('6', 'student_F', '2');
-- ----------------------------
-- Table structure for `teacher`
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`t_id` int(11) NOT NULL AUTO_INCREMENT,
`t_name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`t_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', 'teacher1');
INSERT INTO `teacher` VALUES ('2', 'teacher2');
2.2 一对一关联
一个班级有一个班主任,一个班主任对应一个班级。现在有如下数据库联系,一对一关联其实和一对都很像,只是将外键设一个唯一约束
2.2.1 配置bean
public class Classess {
private Integer cid;
private String cname;
private Teacher teacher;
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Teacher {
private Integer tid;
private String tname;
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
@Override
public String toString() {
return "Teacher [tid=" + tid + ", tname=" + tname + "]";
}
}
2.2.2 配置核心配置文件 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">
<configuration>
<!-- 1.配置dataSource配置文件 -->
<properties resource="jdbc.properties">
</properties>
<!-- 设置别名 -->
<typeAliases>
<package name="com.dream.bean"/>
</typeAliases>
<!-- 2.配置连接环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
<property name="driver.encoding" value="${encoding}"/>
</dataSource>
</environment>
</environments>
<!-- 3.配置mapper -->
<mappers>
<package name="com.dream.mapper"/>
</mappers>
</configuration>
jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis1105?useUnicode=true&characterEncoding=utf-8
username=root
password=as501226107A.
encoding=UTF8
2.2.3配置mapper
classmapper.java
public interface ClassesMapper {
//1.通过id获得班级,并获取教该班机的教师
public Classess getClassesById(Integer id);
//1.通过id获得班级,并获取教该班机的教师(嵌套查询)
public Classess getClassesById1(Integer id);
}
classesMapper.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.dream.mapper.ClassesMapper">
<!--
根据班级id查询班级信息(带老师的信息)
##1. 联表查询
SELECT * FROM class c,teacher t WHERE c.teacher_id=t.t_id AND c.c_id=1;
##2. 执行两次查询
SELECT * FROM class WHERE c_id=1; //teacher_id=1
SELECT * FROM teacher WHERE t_id=1;//使用上面得到的teacher_id
-->
<!-- 方法1 -->
<!-- 设置返回Map -->
<resultMap type="Classess" id="classesMap1">
<id column="c_id" property="cid"/>
<result column="c_name" property="cname"/>
<association property="teacher" javaType="Teacher">
<id column="t_id" property="tid"/>
<result column="t_name" property="tname"/>
</association>
</resultMap>
<select id="getClassesById" resultMap="classesMap1">
select * from teacher t,class c where c.teacher_id =t.t_id
and c.c_id=#{id}
</select>
<!-- ======================================================== -->
<!-- 方法2 -->
<resultMap type="Classess" id="classesMap2">
<id column="c_id" property="cid"/>
<result column="c_name" property="cname"/>
<association property="teacher" column="teacher_id" select="getTeacherById"/>
</resultMap>
<select id="getClassesById1" resultMap="classesMap2">
select * from class c where c.c_id=#{id}
</select>
<select id="getTeacherById" resultType="Teacher">
<!-- 注意设置别名让查出的结果列明与bean属性对应 -->
select t_id tid,t_name tname from teacher where t_id=#{id}
</select>
</mapper>
方法一:使用一个sql语句完成查询,我们需要给select设置resultMap
type:为返回的类型
: 表示”一个“,property表示Teacher对应的属性名;javaType表示属性的类型
在association中将bean的属性和查询出来的结果集的列进行映射
方法二:首先根据id查出对应的班级,在根据班级中的teacher_id去查询对应的teacher
**这里我们需要注意的是,<association>标签不需要设置类型,我们需要加入column,然后需要一个新的select语句,这个select语句需要teacherid ,该参数就在第一个查询结果集中,我们需要找到对应的列(即class表中的外键),所以我们设置一个column,传递到**
该位置的teacher_id
并且,我们需要给查询的列设置别名和我们的bean属性对应
2.2.4测试结果
方法一:
@Test
public void test1() {
//测试一个语句返回结果
Classess classesById = c.getClassesById2(1);
System.out.println(classesById.getStudents());
}
方法二:
@Test
public void test2() {
//测试两条语句返回结果(嵌套查询)
Classess classesById = c.getClassesById3(1);
System.out.println(classesById.getStudents());
}
2.3 一对多关联
2.3.1需求
一个学生对应一个班级,一个班级有多个学生,这里我们重点配置1的一方,因为它存在集合
2.3.2 配置bean
public class Student {
private Integer sid;
private String sname;
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + "]";
}
}
public class Classess {
private Integer cid;
private String cname;
private Teacher teacher;
private List<Student> students;
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
}
2.3.3 配置mapper
<!-- 根据classId查询对应的班级信息,包括学生,老师 -->
<!-- 方式一: 嵌套结果: 使用嵌套结果映射来处理重复的联合结果的子集 SELECT * FROM class c, teacher t,student
s WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id AND c.c_id=1 -->
<select id="getClass3" parameterType="int" resultMap="ClassResultMap3">
select *
from class c, teacher t,student s where c.teacher_id=t.t_id and
c.C_id=s.class_id and c.c_id=#{id}
</select>
<resultMap type="Classes" id="ClassResultMap3">
<id property="id" column="c_id" />
<result property="name" column="c_name" />
<association property="teacher" column="teacher_id"
javaType="Teacher">
<id property="id" column="t_id" />
<result property="name" column="t_name" />
</association>
<!-- ofType指定students集合中的对象类型 -->
<collection property="students" ofType="Student">
<id property="id" column="s_id" />
<result property="name" column="s_name" />
</collection>
</resultMap>
<!-- 方式二:嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型 SELECT * FROM class WHERE c_id=1;
SELECT * FROM teacher WHERE t_id=1 //1 是上一个查询得到的teacher_id的值 SELECT * FROM
student WHERE class_id=1 //1是第一个查询得到的c_id字段的值 -->
<select id="getClass4" parameterType="int" resultMap="ClassResultMap4">
select * from class where c_id=#{id}
</select>
<resultMap type="Classes" id="ClassResultMap4">
<id property="id" column="c_id" />
<result property="name" column="c_name" />
<association property="teacher" column="teacher_id" javaType="Teacher" select="getTeacher2"></association>
<collection property="students" ofType="Student" column="c_id" select="getStudent"></collection>
</resultMap>
<select id="getTeacher2" parameterType="int" resultType="Teacher">
SELECT t_id id, t_name name FROM teacher WHERE t_id=#{id}
</select>
<select id="getStudent" parameterType="int" resultType="Student">
SELECT s_id id, s_name name FROM student WHERE class_id=#{id}
</select>
2.3.4编写单元测试类
@Test
public void test3(){
Classes c1 = mapper.getClass3(1);
System.out.println(c1);
}
@Test
public void test4(){
Classes c1 = mapper.getClass4(1);
System.out.println(c1);
}
2.3.5 MyBatis一对多关联查询总结
MyBatis中使用collection标签来解决一对多的关联查询,**ofType****属性指定集合中元素的对象类型**。
2.4
2.4 多对多关联
2.4.1 需求
一个课程可以被多个学生选择,一个学生可以选择多门课程,我们想查看该学生的所有选择课程
2.4.2 创建bean
public class Student {
private Integer sid;
private String sname;
private List<Course> courses;
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + "]";
}
public List<Course> getCourses() {
return courses;
}
public void setCourses(List<Course> courses) {
this.courses = courses;
}
}
2.4.3 编写sql映射文件CourseMapper.xml
添加如下的SQL映射信息
<?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.dream.mapper.StudentMapper">
<select id="getStudentById" parameterType="int" resultMap="getStudentMap1">
select * from student s,
course c,
course_student cs
where s.s_id=cs.s_id
and c.course_id=cs.c_id
and s.s_id=#{id}
</select>
<resultMap type="Student" id="getStudentMap1">
<id property="sid" column="s_id"/>
<result property="sname" column="s_name"/>
<collection property="courses" ofType="Course">
<id property="cid" column="course_id"/>
<result property="cname" column="course_name"/>
<result property="ctime" column="course_time"/>
</collection>
</resultMap>
</mapper>
加入学生id为1,该sql的结果集如下:
我们根据sql的结果集来配resultMap
三 动态sql
3.1 动态SQL简介
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,**你就能体会到根据不同条件拼接** **SQL** **语句有多么痛苦**。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 **OGNL**(Struts2语法) 的表达式来消除其他元素。
mybatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:
- if 语句 (简单的条件判断)
- choose (when,otherwise) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似.
- trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
- where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
- set (主要用于更新时)
- foreach (在实现 mybatis in 语句查询时特别有用)
3.2 分支判断
3.2.1 if元素
根据 username 和 sex 来查询数据。如果username为空,那么将只根据sex来查询;反之只根据username来查询
首先不使用 动态SQL 来书写
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="User">
select * from user where username=#{username} and sex=#{sex}
</select>
上面的查询语句,我们可以发现,如果 #{username} 为空,那么查询结果也是空,如何解决这个问题呢?使用 if 来判断
<select id="selectUserByUsernameAndSex" resultType="user"
parameterType="User">
select * from user where
<if test="username != null">
username=#{username}
</if>
<if test="sex!= null">
and sex=#{sex}
</if>
</select>
这样写我们可以看到,如果 sex 等于 null,那么查询语句为 select * from user where username=#{username},但是如果usename 为空呢?那么查询语句为 select * from user where and sex=#{sex},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句
3.2.2 动态SQL:if+where 语句
<select id="selectUserByUsernameAndSex" resultType="User"
parameterType="User">
select * from user
<where>
<if test="username != null">
username=#{username}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</where>
</select>
**这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。**
3.2.3 动态SQL:if+set 语句
同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?
<!-- 根据 id 更新 user 表的数据 -->
<update id="updateUserById" parameterType="User">
update user u
<set>
<if test="username != null and username != ''">
u.username = #{username},
</if>
<if test="sex != null and sex != ''">
u.sex = #{sex},
</if>
</set>
where id=#{id}
</update>
这样写,如果第一个条件 username 为空,那么 sql 语句为:update user u set u.sex=? where id=?
如果第一个条件不为空,那么 sql 语句为:update user u set u.username = ? ,u.sex = ? where id=?
如果出现多余逗号 会自动去掉!!
3.2.4 动态SQL:choose(when,otherwise) 语句
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
<select id="selectUserByChoose" resultType="User" parameterType="User">
select * from user
<where>
<choose>
<when test="id !='' and id != null">
id=#{id}
</when>
<when test="username !='' and username != null">
and username=#{username}
</when>
<otherwise>
and sex=#{sex}
</otherwise>
</choose>
</where>
</select>
也就是说,这里我们有三个条件,id,username,sex,只能选择一个作为查询条件
如果 id 不为空,那么查询语句为:select * from user where id=?
如果 id 为空,那么看username 是否为空,如果不为空,那么语句为 select * from user where username=?;
如果 username 为空,那么查询语句为 select * from user where sex=?
3.2.5 动态SQL:trim 语句 了解
trim标记是一个格式化的标记,可以完成set或者是where标记的功能
①、用 trim 改写上面第二点的 if+where 语句
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="User">
select * from user
<trim prefix="where" prefixOverrides="and | or">
<if test="username != null">
and username=#{username}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</trim>
</select>
prefix:前缀
prefixoverride:去掉第一个and或者是or
②、用 trim 改写上面第三点的 if+set 语句
<!-- 根据 id 更新 user 表的数据 -->
<update id="updateUserById" parameterType="User">
update user u
<trim prefix="set" suffixOverrides=",">
<if test="username != null and username != ''">
u.username = #{username},
</if>
<if test="sex != null and sex != ''">
u.sex = #{sex},
</if>
</trim>
where id=#{id}
</update>
3.2.6动态SQL: SQL 片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的**重用性**,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:
<!-- 定义 sql 片段 -->
<sql id="selectUserByUserNameAndSexSQL">
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="sex != null and sex != ''">
AND sex = #{sex}
</if>
</sql>
引用 sql 片段
<select id="selectUserByUsernameAndSex" resultType="user"
parameterType="User">
select * from user
<trim prefix="where" prefixOverrides="and | or">
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="selectUserByUserNameAndSexSQL"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</trim>
</select>
注意:①、最好基于 单表来定义 sql 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where
5.2.7动态SQL: foreach 语句
需求:我们需要查询 user 表中 id 分别为1,2,3的用户
sql语句:select * from user where id=1 or id=2 or id=3
select * from user where id in (1,2,3)
①、建立一个 UserVo 类,里面封装一个 List ids 的属性
public class UserVo {
//封装多个用户的id
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
②、我们用 foreach 来改写 select * from user where id=1 or id=2 or id=3
<select id="selectUserByListId" parameterType="UserVo"
resultType="User">
select * from user
<where>
<!-- collection:指定输入对象中的集合属性 item:每次遍历生成的对象 open:开始遍历时的拼接字符串 close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串 select * from user where 1=1 and (id=1 or id=2 or
id=3) -->
<foreach collection="ids" item="id" open="and (" close=")"
separator="or">
id=#{id}
</foreach>
</where>
</select>
测试
//根据id集合查询user表数据
@Test
public void testSelectUserByListId(){
UserVo uv = new UserVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
uv.setIds(ids);
List<User> listUser = mapper.selectUserByListId(uv);
for(User u : listUser){
System.out.println(u);
}
session.close();
}
- 我们用 foreach 来改写 select * from user where id in (1,2,3)
<select id="selectUserByListId" parameterType="User" resultType="User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and id in (1,2,3)
-->
<foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
#{id}
</foreach>
</where>
</select>
- 单参数List的类型
<select id="dynamicForeachTest" resultType="User">
select * from user where id in
<foreach collection="list" index="index" item="item" open="("
separator="," close=")">
#{item}
</foreach>
</select>
测试
public List<User> dynamicForeachTest(List<Integer> ids);
@Test
public void testSelectUserByListId1(){
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
List<User> listUser = mapper.dynamicForeachTest(ids);
for(User u : listUser){
System.out.println(u);
}
}
- 单参数array数组的类型
<select id="dynamicForeach2Test" resultType="User">
select * from user where id in
<foreach collection="array" index="index" item="item" open="("
separator="," close=")">
#{item}
</foreach>
</select>
public List<User> dynamicForeach2Test(Integer[] ids);
@Test
public void dynamicForeach2Test(){
Integer[] ids={1,2,3};
List<User> listUser = mapper.dynamicForeach2Test(ids);
for(User u : listUser){
System.out.println(u);
}
}
- 将参数封装成Map的类型
<select id="dynamicForeach3Test" resultType="User">
select * from user where username like "%"#{username}"%" and id in
<foreach collection="ids" index="index" item="item" open="("
separator="," close=")">
#{item}
</foreach>
</select>
public List<User> dynamicForeach3Test(Map params);
@Test
public void dynamicForeach3Test() {
List ids = new ArrayList();
ids.add(28);
ids.add(29);
ids.add(30);
Map params = new HashMap();
params.put("ids", ids);
params.put("username", "张");
List<User> listUser = mapper.dynamicForeach3Test(params);
for (User u : listUser) {
System.out.println(u);
}
}