6. Mybatis映射文件深入 – 新增数据后返回主键ID – 动态SQL – SQL片段
前言
在前面的篇章中,我们已经认识了如何使用 resultMap标签 映射查询的结果集字段、多条件查询、模糊查询。
下面我们继续来深入认识一下 MyBatis 的映射文件。
新增数据后,返回主键 ID
应用场景
向数据库保存一个user对象后, 然后在控制台打印此新增user的主键值(id)
# 点外卖
1. 点一份饭 -> 产生一个订单, 美团会往订单表插入一条数据(主键)
2. 需要返回这条记录的主键, 然后给第三方配送平台, 送外卖
实现案例一:基于MyBatis 框架自带的主键返回功能
① UserMapper接口
/*
* # 复杂操作:插入一条数据返回对应的主键
* sql:
* insert into user values(null,?,?,?,?);
* 参数: user(username,birthday,sex,address)
* 返回值: int(其实是被影响的行数)
* void
*
* 对应的主键: user.id
* */
int addUser(@Param("user") User user);
② UserMapper.xml
<!--
方案一: 这表的主键必须是自增长的 auto_increment
useGeneratedKeys="true" 让自增长的主键开启返回功能
keyColumn="id" user表中主键列
keyProperty="id" user实体主键属性
注意:支持主键自增类型的数据库 MySQL 和 SqlServer , oracle不支持
-->
<insert id="addUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into user values(null,#{user.username},#{user.birthday},#{user.sex},#{user.address})
</insert>
③ 测试
// 测试插入数据, 返回主键ID
@Test
public void test11() throws Exception {
// 1. 获取 sqlSession 数据库连接会话
MyBatisUtil myBatisUtil = new MyBatisUtil();
SqlSession sqlSession = myBatisUtil.getSqlSession();
//2. 会话对象得到UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//3. 执行插入一个user对象, 返回主键ID
//创建user对象
User user = new User();
//设置username
user.setUsername("李云龙");
//设置字符串的日期
String str = "2021-03-15";
// 使用SimpleDateFormat将其解析为 Date 对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = simpleDateFormat.parse(str);
// 再将date设置为 java.sql.Date
java.sql.Date date1 = new java.sql.Date(date.getTime());
// System.out.println(date1);
// 设置user的生日
user.setBirthday(date1);
//设置 sex
user.setSex("男");
//设置 address
user.setAddress("湖南嚼槟榔的那嘎达地方");
//调用插入数据的方法
int i = userMapper.addUser(user);
System.out.println("插入数据的行数: " + i);
//查询插入后返回的主键ID
System.out.println("插入数据的主键ID: " + user.getId());
//7. 关闭会话
myBatisUtil.commitAndClose(sqlSession);
}
实现案例二:基于SQL的 SELECT LAST_INSERT_ID() 语句
上面的案例一是采用MyBatis框架自动的主键返回功能,而有些时候,我们的主键有可能不是自增 ID,有可能是自己生成的 UUID。
这时候就需要采用一些 SQL 语句进行查询了。下面我们来模拟查询,如下:
-- 查询数据的同时,查询返回自增的ID
-- 注意:这两行SQL要按照顺序同时执行,否则单独查询 SELECT LAST_INSERT_ID(); 不会返回最新的自增ID
insert into user values(null, "张飞", "2021-03-15", "男", "三国"); -- 插入数据
SELECT LAST_INSERT_ID(); -- 查询最新的自增ID
在这里我们在插入数据之后,同时执行了查询最新自增ID的操作,从而获取自增的ID。
下面我们在代码来实现一下这个效果。
① UserMapper接口
public interface UserMapper {
/*
* # 复杂操作:插入一条数据返回对应的主键
* sql:
* insert into user values(null,?,?,?,?);
* 参数: user(username,birthday,sex,address)
* 返回值: int(其实是被影响的行数)
* void
*
* 对应的主键: user.id
* */
//int addUser(@Param("user") User user); // 设置 @Param 参数注解后,在映射xml文件必须写 #{user.属性名}
int addUser(User user); // 没有设置 @Param 参数注解,直接写 #{属性名} 即可
}
② UserMapper.xml
<!--
方案二: <selectKey>
keyColumn="id" user表中主键列
keyProperty="id" user实体主键属性
resultType="int" user实体主键属性类型
order="AFTER" 表示此标签内部sql语句在insert执行之前(执行),还是之后执行(执行)
AFTER 之后执行【在自增主键时】
BEFORE 之前执行【使用指定主键时】
-->
<insert id="addUser">
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
insert into user values(null, #{username},#{birthday},#{sex},#{address})
</insert>
③ 测试
// 测试插入数据, 返回主键ID
@Test
public void test11() throws Exception {
// 1. 获取 sqlSession 数据库连接会话
MyBatisUtil myBatisUtil = new MyBatisUtil();
SqlSession sqlSession = myBatisUtil.getSqlSession();
//2. 会话对象得到UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//3. 执行插入一个user对象, 返回主键ID
//创建user对象
User user = new User();
//设置username
user.setUsername("李云龙");
//设置字符串的日期
String str = "2021-03-15";
// 使用SimpleDateFormat将其解析为 Date 对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = simpleDateFormat.parse(str);
// 再将date设置为 java.sql.Date
java.sql.Date date1 = new java.sql.Date(date.getTime());
// System.out.println(date1);
// 设置user的生日
user.setBirthday(date1);
//设置 sex
user.setSex("男");
//设置 address
user.setAddress("湖南嚼槟榔的那嘎达地方");
//调用插入数据的方法
int i = userMapper.addUser(user);
System.out.println("插入数据的行数: " + i);
//查询插入后返回的主键ID
System.out.println("插入数据的主键ID: " + user.getId());
//7. 关闭会话
myBatisUtil.commitAndClose(sqlSession);
}
2.2 动态SQL
2.2.1 什么是动态SQL
动态SQL就是基于不同的查询情况,动态改变不同的查询条件,组合查询出来的结果。
下面我们使用一个超简单的查询需求来说明什么是动态SQL。
需求
基于【编号】和 【用户名】来搜索用户,而【编号】和【用户名】都可能为空。
实现方式
把id和username封装到user对象中,将user对象中不为空的属性作为查询条件。
这个时候我们执行的sql就有多种可能。
-- 如果id和用户名不为空
select * from user where id= #{id} and username = #{username}
-- 如果只有id
select * from user where id= #{id}
-- 如果只有用户名
select * from user where username = #{username}
-- 如果id和用户名都为空
select * from user
像上面这样, 根据传入的参数不同, 需要执行的SQL的结构就会不同,这就是动态SQL
而对于在映射配置文件中动态设置SQL的话,我们就需要熟悉一下相关判断的标签文件。
2.2.2 if 条件判断
需求
把id和username封装到user对象中,将user对象中不为空的属性作为查询条件
① UserMapper接口
public interface UserMapper {
/*
* # 动态sql
* 需求: 查询符合一定条件的用户(条件是可选的)
* select * from user
* where
* id = ? -- 可能有
* and username = ? -- 可能有
*
* 参数: int id, String username
* 返回值: List<U>
* */
List<User> findUsersByIdAndUserNameIf(@Param("id") String id, @Param("username") String username);
}
② UserMapper.xml
<!--
java 逻辑:
username = null; 都没有意义
username = "";
if(username != null && !username.equals("")){
and username = ?
}
sql逻辑:
if标签: 必要属性test (写判断条件)
满足条件,拼接sql
where标签: 根据最终的条件,动态修改成合适的语法
-->
<select id="findUsersByIdAndUserNameIf" resultType="user">
select * from user
<where>
<if test="id != null and id != ''">
id = #{id}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
</where>
</select>
③ 测试
// 测试根据 id 以及 username 查询用户
@Test
public void test12() throws Exception {
// 1. 获取 sqlSession 数据库连接会话
MyBatisUtil myBatisUtil = new MyBatisUtil();
SqlSession sqlSession = myBatisUtil.getSqlSession();
//2. 会话对象得到UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//3. 执行根据 id 以及 username 查询用户
//3.1 同时查询 id 、username
List<User> users = userMapper.findUsersByIdAndUserNameIf("20", "李云龙");
for (User user : users) {
System.out.println("3.1 同时查询 id 、username: " + user);
}
//3.2 单独查询 id
List<User> users2 = userMapper.findUsersByIdAndUserNameIf("19", "");
for (User user : users2) {
System.out.println("3.2 单独查询 id: " + user);
}
//3.3 单独查询 username
List<User> users3 = userMapper.findUsersByIdAndUserNameIf("", "豹子精");
for (User user : users3) {
System.out.println("3.2 单独查询 username: " + user);
}
//7. 关闭会话
myBatisUtil.commitAndClose(sqlSession);
}
2.2.3 set 用于update语句
需求
动态更新user表数据,如果该属性有值就更新,没有值不做处理
① UserMapper接口
/*
* # 动态sql
* 需求: 修改某个用户信息
* update user
* set username = ?, -- 动态
* birthday = ?, -- 动态
* sex = ?, -- 动态
* address = ? -- 动态
* where id = ?;
* 参数: User
* 返回值: void
*
* */
int updateUserById(User user);
② UserMapper.xml
<!--
set标签: 用在update语句中,动态修改sql语法
-->
<update id="updateUserById">
update user
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="birthday != null and birthday != ''">
birthday = #{birthday},
</if>
<if test="sex != null and sex != ''">
sex = #{sex},
</if>
<if test="address != null and address != ''">
address = #{address}
</if>
</set>
where id = #{id}
</update>
③ 测试
// 测试根据 id 修改用户信息
@Test
public void test13() throws Exception {
// 1. 获取 sqlSession 数据库连接会话
MyBatisUtil myBatisUtil = new MyBatisUtil();
SqlSession sqlSession = myBatisUtil.getSqlSession();
//2. 会话对象得到UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//3. 更具ID修改用户信息
User user = new User();
user.setUsername("小王");
user.setSex("女");
user.setId(20); // 修改ID为20的用户
//update user SET username = ?, sex = ? where id = ?
userMapper.updateUserById(user);
//7. 关闭会话
myBatisUtil.commitAndClose(sqlSession);
}
2.2.4 foreach 用于循环遍历【重点】
“
当如果查询条件是 in (….) 之类的需要遍历查询条件的情况,此时就需要使用 foreach 标签来处理了。
”
需求
根据多个id查询,user对象的集合
select * from user where id in (41,43,46);
* <foreach>标签用于遍历集合,它的属性:
• collection:代表要遍历的集合元素
• open:代表语句的开始部分
• close:代表结束部分
• item:代表遍历集合的每个元素,生成的变量名
• sperator:代表分隔符
练习二个版本
-
普通list集合 作为 查询条件参数的容器 -
普通array数组 作为 查询条件参数的容器
① UserMapper
/*
* # 动态sql3
* 需求: 查询某几个用户信息
* select * from user where id in
* (1,3,4) -> 动态的
*
* 参数: List<Integer> list / int[] array
* 返回值: List<User>
* */
List<User> findUsersByIds(List<Integer> list); // 普通list集合 作为 查询条件参数的容器
List<User> findUsersByIds2(int[] array); // 普通array数组 作为 查询条件参数的容器
② UserMapper.xml
<!--
foreach 标签(遍历)
1. collection属性: 被遍历的容器类型
list/array
2. item : 被遍历出来的元素
3. open: 遍历开始时的内容
4. close: 遍历结束的内容
5. separator : 每遍历一次就添加一次的分隔符(最后一次遍历不加)
距离: list = {1,2,3}
遍历: (1,2,3)
-->
<select id="findUsersByIds" resultType="user">
select * from user where id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<select id="findUsersByIds2" resultType="user">
select * from user where id in
<foreach collection="array" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
③ 测试
// 测试 in 条件的查询
@Test
public void test14() throws Exception {
// 1. 获取 sqlSession 数据库连接会话
MyBatisUtil myBatisUtil = new MyBatisUtil();
SqlSession sqlSession = myBatisUtil.getSqlSession();
//2. 会话对象得到UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 以集合保存 in 条件参数
List<Integer> list = new ArrayList<>();
Collections.addAll(list,1,3,4);
List<User> result = userMapper.findUsersByIds(list);
System.out.println("以集合保存 in 条件参数: " + result);
// 以数组保存 in 条件参数
int[] array = {2,4,6};
List<User> result2 = userMapper.findUsersByIds2(array);
System.out.println("以数组保存 in 条件参数: " + result2);
//7. 关闭会话
myBatisUtil.commitAndClose(sqlSession);
}
2.3 SQL片段
应用场景
映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的。
应用案例
① 寻找重复的 sql
② UserMapper.xml
<select id="findUsersByIds" resultType="user">
-- select * from user
<include refid="selectUser"/>
where id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<select id="findUsersByIds2" resultType="user">
-- select * from user
<include refid="selectUser"/>
where id in
<foreach collection="array" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<!--
sql片段: 抽取重复出现的sql语句
在其他的语句中可以
include标签进行引用
-->
<sql id="selectUser">
select * from user
</sql>
设置之后,再使用上面 foreach 标签的测试代码执行一下验证是否正常执行即可。
2.4 知识小结
MyBatis映射文件配置
<select>:查询
<insert>:插入
<update>:修改
<delete>:删除
<selectKey>:插入返回主键
<where>:where条件
<if>:if判断
<foreach>:for循环
<set>:set设置
<sql>:sql片段抽取
原文始发于微信公众号(海洋的渔夫):6. Mybatis映射文件深入 – 新增数据后返回主键ID – 动态SQL – SQL片段
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/33740.html