Mybatis 映射文件深入
动态sql
if 标签
if 标签:
判断语句(单条件分支)。必须结合 test 属性联合使用。
常用场景:
在 WHERE 条件中使用 if 标签。根据条件判断动态拼接查询条件。
在 UPDATE 更新列中使用 if 标签。只更新有变化的字段, 空值不更新。
在 INSERT 动态插入中使用 if 标签。只有非空属性才插入。
在 SELECT 动态查询字段使用 if 标签。根据条件动态确定查询字段。
mapper接口方法:
public List<User> findByIdAndUsernameIf(User user);
xml文件:
<select id="findByIdAndUsernameIf" parameterType="user" resultType="user"> select * from user <!-- where 1=1 是多条件拼接时的小技巧,后面的条件查询就可以都用and了 --> <!-- where 1 = 1 --> <!-- where 标签:相当于 where 1=1 功能, 当if条件都不满足时,where 将不会拼接在sql语句中 当if有条件满足时,where 将会拼接在sql语句中,且去掉第一个成立的 if 标签下的 and | or 等 --> <where> <if test="id != null"> and id = #{id} </if> <if test="username != null"> and username like concat('%',#{username}),'%') </if> </where> </select>
choose 标签
choose when otherwise 标签可以帮我们实现 if else 的逻辑(或 java 中的 switch 语句)。
一个 choose 标签至少有一个 when,最多一个otherwise。
mapper接口方法:
public List<User> findByIdAndUsernameChoose(User user); • 1
xml文件:
<!-- choose标签 相当于 switch语句 when标签 相当于 case+break语句 otherwise 相当于 default语句 --> <select id="findByIdAndUsernameChoose" parameterType="user" resultType="user"> select * from user <where> <choose> <when test="id != null"> and id = #{id} </when> <when test="username != null"> and username like concat(concat('%',#{username}),'%') </when> <otherwise> and 1 = 1 </otherwise> </choose> </where> </select>
set 标签
可以去掉 set 标签中的最后一个条件的 逗号
mapper接口方法:
public void updateIf(User user);
xml方法:
<update id="updateIf" parameterType="user"> update user <set> <if test="username != null"> username=#{username}, </if> <if test="birthday != null"> birthday=#{birthday}, </if> <if test="sex != null"> sex = #{sex}, </if> <if test="address != null"> address = #{address}, </if> </set> where id = #{id} </update>
foreach 标签
做数据的循环遍历
* 例如: select * from user where id in (1,2,3) 在这样的语句中,传入的参数部分必须依靠 foreach遍历才能实现。
foreach 标签中的属性:
collection:必填, 被遍历的集合(list)/数组(array)/Map的名称
item:变量名。即从迭代的对象中取出的每一个值
index:索引的属性名。当迭代的对象为 Map 时, 该值为 Map 中的 Key.
open:遍历前拼接的字符串
close:遍历后拼接的字符串
separator:分隔符
where 中使用 foreach
mapper接口方法:
// foreach标签 遍历 public List<User> findByList(List<Integer> ids); public List<User> findByArray(Integer[] ids); // 传递引用数据类型 list属性或者array属性 public List<User> findByPojo(QueryVo queryVo);
xml文件:
<!-- 传递 普通类型list集合 collection="" 取值:collection、list --> <select id="findByList" parameterType="list" resultType="user"> select * from user where <foreach collection="list" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </select> <!-- 传递 普通类型array数组 collection="" 取值:array --> <select id="findByArray" parameterType="int[]" resultType="user"> select * from user where <foreach collection="array" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </select> <!-- 传递 引用数据类型list集合属性 collection="" 取值 集合或数组的 属性名 --> <select id="findByPojo" parameterType="QueryVo" resultType="user"> select * from user where <foreach collection="ids" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </select>
foreach 实现批量插入
mapper接口方法:
// 批量插入学生 int insertList(List<Student> students);
xml文件:
<insert id="insertList"> insert into student(name, phone, email, sex, locked) values <foreach collection="list" item="student" separator=","> ( #{student.name}, #{student.phone},#{student.email}, #{student.sex},#{student.locked} ) </foreach> </insert>
trim 标签
trim 标签的属性
prefix: 当 trim 元素包含有内容时, 增加 prefix 所指定的前缀
prefixOverrides: 当 trim 元素包含有内容时, 去除 prefixOverrides 指定的前缀
suffix: 当 trim 元素包含有内容时, 增加 suffix 所指定的后缀
suffixOverrides:当 trim 元素包含有内容时, 去除 suffixOverrides 指定的后缀、
set 和 where 其实都是 trim 标签的一种类型, 该两种功能都可以使用 trim 标签进行实现。
trim 标签来实现 where 标签
当 trim 标签中含有内容时,添加 where,且第一个为 and 或 or 时,会将其去掉;而如果没有内容,则不添加 where
<trim prefix="where" prefixOverrides="AND |OR"> </trim>
trim 标签来实现 set 标签
当 trim 标签中含有内容时,添加 set,且最后的内容为 , 时,会将其去掉;而没有内容,不添加 set
<trim prefix="SET" suffixOverrides=","> </trim>
bind 标签
bind 元素允许在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。
如在 selectByStudentSelective 方法中,有如下
<if test="name != null and name !=''"> and name like concat('%', #{name}, '%') </if>
在 MySQL 中,concat 函数支持多参数,但在 Oracle 中只支持两个参数。通过使用 bind 来让该 SQL 达到支持两个数据库的作用
<if test="name != null and name !=''"> <bind name="nameLike" value="'%'+name+'%'"/> and name like #{nameLike} </if>
include 标签(引入sql片段)
<!-- 可以将公共部分进行抽取 --> <sql id="userSelect"> select * from user </sql> <!-- 引入sql片段 --> <select id="findByArray" parameterType="int[]" resultType="user"> <include refid="userSelect"></include> where <foreach collection="array" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </select> <select id="findByPojo" parameterType="QueryVo" resultType="user"> <include refid="userSelect"></include> where <foreach collection="ids" open="id in (" close=")" item="id" separator=","> #{id} </foreach> </select>
生成并返回主键
往数据库中插入记录时,生成主键并将主键返回到原实体对象中。
方式1:<selectKey>【复杂,但通用】
既支持主键自增类型,也支持 UUID 类型的主键的生成与返回
方式2:useGeneratedKeys【简单,但不通用】
注意:这种方式仅支持主键自增类型的数据库,如 MySQL 和 sqlServer,oracle不支持
xml文件:
<!-- 方式1:<selectKey> keyColumn 数据库表的主键字段 keyProperty 指定实体的主键属性名。selectKey语句结果应该被设置的目标属性 resultType 指定实体主键的类型。 可以不写,Mybatis通常可以推算出来。Mybatis允许任何简单类型作为主键的类型,包括字符串。 order="AFTER" selectKey的sql语句是在insert执行之前(执行),还是之后(执行) AFTER 之后执行。适合返回自增id的情况(主键是自增列,插入之后才能获知)。SELECT LAST_INSERT_ID() FROM DUAL BEFORE 之前执行。适合返回UUID的情况(主键不是自增列,需要预先生成)。SELECT UUID() FROM DUAL --> <insert id="save2" parameterType="user"> <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER"> SELECT LAST_INSERT_ID() FROM DUAL </selectKey> insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address}) </insert> <!-- 方式2:useGeneratedKeys useGeneratedKeys="true" 开启返回主键功能 keyColumn 数据库表的主键字段 keyProperty 指定实体的主键属性名 --> <insert id="save1" parameterType="user" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert>
多表查询
表关系及xml标签配置介绍
一对一 订单与用户。查询站在订单角度:一个订单只能属于一个用户 一对多 用户和订单。查询站在用户角度:一个用户可以拥有多个订单 多对一 订单和用户。多个订单,可以被一个用户拥有 多对多 用户和角色。 - 站在用户角度:一个用户可以拥有多个角色 - 站在角色角度:一个角色可以被多个用户拥有 mybatis:一个表是一个实体的映射(user orders) 实体: 实体属性、集合属性 1 v 1: 实体属性 1 v N: 1的一方有多的一方的集合属性 多的一方有1的一方的实体对象 * 一对一配置:使用<resultMap>+<association>做配置 association: property:关联的实体属性名 javaType:关联的实体类型(使用别名) * 一对多配置:使用<resultMap>+<collection>做配置 collection: property:关联的集合属性名 ofType:关联的集合泛型类型(使用别名) * 多对多配置:使用<resultMap>+<collection>做配置 collection: property:关联的集合属性名 ofType:关联的集合泛型类型(别名) * 多对多的配置跟一对多很相似,区别在于SQL语句的编写。
多表查询和嵌套查询的实体类
Orders(订单)实体类
@Data public class Orders { private Integer id; private Date ordertime; private Double money; // 订单所属的用户 private User user; }
User(用户)实体类
@Data public class User { private Integer id; private String username; private Date birthday; private String sex; private String address; // 一个用户有多个角色 private List<Role> roleList; // 一个用户有多个订单 private List<Orders> orders; }
Role(角色)实体类
@Data public class Role { private Integer id; private String roleName; private String roleDesc; }
一对一(多对一)
一对一查询的实例:
需求:查询一个订单,与此同时查询出该订单所属的用户(1 v 1)
OrdersMapper.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="cn.test.dao.OrdersDao"> <!--手动封装--> <resultMap id="findOrderswithUserResultMap" type="orders"> <!--订单orders--> <id column="iid" property="id"></id> <result column="ordertime" property="ordertime"></result> <result column="money" property="money"></result> <!--订单中的用户User(一对一的封装) association标签 property:订单实体类中的用户属性名 javaType:要封装数据的属性属于什么类型。不写则自动映射 --> <association property="user" javaType="cn.test.domain.User"> <id column="uid" property="id"></id> <result column="username" property="username"></result> <result column="birthday" property="birthday"></result> <result column="sex" property="sex"></result> <result column="address" property="address"></result> </association> </resultMap> <!-- 查询当前订单以及所属用户的信息 --> <select id="findOrderswithUser" parameterType="int" resultMap="findOrderswithUserResultMap"> select *, o.id iid from orders o left join user u on o.uid=u.id where o.id=#{id} </select> </mapper>
一对多
一对多查询的实例:
需求:查询一个用户,与此同时查询出该用户具有的所有订单(1 v N)
UserMapper.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="cn.test.dao.UserDao"> <!--手动封装--> <resultMap id="findUserwithOrdersResultMap" type="user"> <!--封装user数据--> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="birthday" property="birthday"></result> <result column="sex" property="sex"></result> <result column="address" property="address"></result> <!--封装user的list集合属性(一对多) collection标签 property:用户的订单集合属性名 ofType:要封装的list集合中的泛型类型 --> <collection property="list" ofType="orders"> <id column="iid" property="id"></id> <result column="ordertime" property="ordertime"></result> <result column="money" property="money"></result> </collection> </resultMap> <!-- 查询一个用户及订单的信息 --> <select id="findUserwithOrders" parameterType="int" resultMap="findUserwithOrdersResultMap"> select *, o.id as iid from user u left join orders o on u.id=o.uid where u.id=#{id} </select> </mapper>
多对多(二个一对多)
多对多的查询的实例:
需求: 查询某个用户的所有角色或者是某个角色的所有用户
- 站在用户角度,一个用户可以拥有多个角色
- 站在角色角度,一个角色可以被多个用户拥有
- 所以 mybatis框架多对多的实现,跟一对多的步骤是一样的,区别点在于sql语句
UserMapper.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="cn.test.dao.UserDao"> <resultMap id="findUserwithRoleResult" type="user"> <!--用户数据--> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="birthday" property="birthday"></result> <result column="sex" property="sex"></result> <result column="address" property="address"></result> <!--用户的角色集合(一对多) collection标签 property:用户的角色集合属性名 oftype:要封装的类型 --> <collection property="roleList" ofType="Role"> <id column="rid" property="id"></id> <result column="role_name" property="roleName"></result> <result column="role_desc" property="roleDesc"></result> </collection> </resultMap> <!-- 查询某个用户及角色信息 --> <select id="findUserwithRole" parameterType="int" resultMap="findUserwithRoleResult"> select * from user u left join user_role ur on u.id=ur.uid left join role r on r.id=ur.rid where u.id=#{id} </select> </mapper>