JavaWeb笔记——Mybatis的xml映射文件、动态SQL
黑马程序员2023年javaweb网课笔记,自用
tip 1:全限定名/相对地址
指相对地址,右击实体类的名称可以查到
tip 2:Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ’ ’
- 运行报错时从下向上看,找到caused by部分的报错注释
- 该报错是SQL语法错误,可以在use near’ '中找到报错的语法位置。
一、XML映射文件基础配置
- 理论:xml映射文件可以和注释交叉使用,取决于sql语句的复杂程度和团队需求
1)@Select等注释配置sql语句:完成简单的增删查改功能
2)xml文件做sql语句映射:实现复杂的sql功能 - 应用规范:
1)同包同名:XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下。在resources包下创建的目录包,直接使用.来分割是不能达到层级效果的,使用/分割。
2)XML映射文件的namespace属性为Mapper接口全限定名一致。
mybatis官方入门网址:https://mybatis.org/mybatis-3/zh/getting-started.html
已映射的 SQL 语句配置文件头如下,可以在入门网址中找到。
此处的全限定名一致是指从java包下的相对目录地址<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.liquor.mybatis_01.mapper.EmpMapper"> </mapper>
3)XML映射文件中sql语句的id与Mapper 接口中的方法名一致,并保持返回类型一致。返回类型指单条数据的封装类型,如果是实体类,是该实体类的全限定名。 如,接口中的方法返回类型为List,则resultType为Emp的全限定名com.liquor.mybatis_01.pojo.Emp。
Mapper接口中的方法:
public List<Emp> selectList(String name,short gender);
xml文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.liquor.mybatis_01.mapper.EmpMapper"> <select id="selectList" resultType="com.liquor.mybatis_01.pojo.Emp"> SELECT * from tb_emp where name like concat('%',#{name},'%') and gender = #{gender} >order by update_time desc </select> </mapper>
二、mybatisx插件
- 在settinngs安装插件
2.1 定位功能
xml文件和接口文件左侧会有图标,单击可以快速切换定位
2.2 自动生成接口对应标签
- 未配置sql语句的接口会爆红,单击爆红名称,快捷键alt+enter,自动生成
- 效果:自动生成id和resultType的属性值
三、xml映射文件中的动态SQL
- 原因:在查询语句中,如果多条件查询(如图,可以通过姓名、性别、入职时间进行查询),则外部输入的条件个数不确定(如,只输入姓名、性别),如果使用固定的sql语句,则会导致查询失败(如,只输入姓名和性别,入职时间会传回一个null值,导致查询失败)。
3.1 if 和 where 标签——动态多条件
- 理论:
<if>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
<where>:where 元素只会在子元素有内容的情况下才插入where子句。而且会自动去除子句的开头的AND 或OR。
- 实例:
1)原固定sql语句
SELECT * from tb_emp where name like concat('%',#{name},'%') and gender = #{gender} order by update_time desc
2)动态sql语句在xml中配置
<select id="selectList" resultType="com.liquor.mybatis_01.pojo.Emp">
SELECT *
from tb_emp
where
<if test="name!=null">
name like concat('%',#{name},'%')
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
order by update_time desc
</select>
3)接口:因为需要给gender赋值为null,此处需要使用Short类型,而非short类型(首字母大小写),否则会报错。
public List<Emp> selectList(String name,Short gender);
4)测试代码1:当gender为null
@Test
public void testSelectList(){
List<Emp> emps =empMapper.selectList("汤",null);
System.out.println(emps);
}
5)结果1:可以看出,执行的sql语句只拼接了name的条件,没有gender
6)测试2:name为null,结果2:出现sql语法报错,原因是使用当前sql语句时,如果name的条件不拼接,则实际执行sql语句如下,where后面自然衔接了and,出现语法问题。
SELECT * from tb_emp where and gender = #{gender} order by update_time desc
7)测试2的解决方案:将where字段换成where标签,会自动删除where后紧接的and或者or,如果没有条件符合,则不会显示where,默认为全查询。
<select id="selectList" resultType="com.liquor.mybatis_01.pojo.Emp">
SELECT *
from tb_emp
<where>
<if test="name!=null">
name like concat('%',#{name},'%')
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</where>
order by update_time desc
</select>
3.2 set 标签——动态更新
- 理论:
<set>:动态地在行首插入 SET 关键字,并会删掉末尾额外的逗号。(用在update语句中)
- 实例:只更新传入值非null的数值
1)原固定sql语句
@Update("update tb_emp set username = #{username}, name = #{name}, gender = #{gender}, image = #{image}," +
" job = #{job}, entrydate = #{entrydate}, dept_id = #{deptId},update_time = #{updateTime} " +
"where id = #{id}")
2)动态sql语句在xml中配置:此处的接口返回值为void,所以不需要配置resultType
<update id="update">
update tb_emp
<set>
<if test="username!=null">
username = #{username},
</if>
<if test="name!=null">
name = #{name},
</if>
<if test="gender!=null">
gender = #{gender},
</if>
<if test="image!=null">
image = #{image},
</if>
<if test="job!=null">
job = #{job},
</if>
<if test="entrydate!=null">
entrydate = #{entrydate},
</if>
<if test="deptId!=null">
dept_id = #{deptId},
</if>
<if test="updateTime!=null">
update_time = #{updateTime}
</if>
</set>
where id = #{id}
</update>
3)接口:
public void update(Emp emp);
4)测试代码:设置部分值为null
@Test
public void testUpdate(){
Emp emp = new Emp();
emp.setId(20);
emp.setUsername("K");
emp.setName(null);
emp.setJob(null);
emp.setEntrydate(LocalDate.of(2001,2,3));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
empMapper.update(emp);
}
5)结果:传入非null的数据被更新
3.3 foreach 标签——批量操作
- 原理:一般和in一起用,类似于循环语句
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
- 属性:
- 实例:根据id批量删除员工:
1)原固定sql语句
delete from tb_emp where id in (#{id1},#{id2},#{id3})//此处是固定给出了3个id号,没做动态的个数
2)动态sql语句在xml中配置:collections名称与接口形参一致;item与foreach中#{id}一致;separator是传入参数的分隔符,open和close是除数据外的始末片段
<delete id="deleteByIds">
delete from tb_emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
3)接口:
public void deleteByIds(List<Integer> ids);
4)测试代码:
@Test
public void testDeleteById(){
List<Integer> ids= Arrays.asList(19,20);
empMapper.deleteByIds(ids);
}
5)结果:
3.4 sql 和 include 标签
- 问题:考虑到select查询多个属性值需要罗列属性名称,后期维护困难,所以将高频重复的sql语句段抽取出来封装到sql标签中
- 理论:
1) <sql>:定义可重用的 SQL 片段。属性id作为标识符。
2) <include>:属性refid指定sql标签的Id,引入对应的sql片段。该标签可以直接用/自闭环。
- 实例:
1)sql标签抽取的sql片段
<sql id="EmpAllSelect">
SELECT id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time
from tb_emp
</sql>
2)使用include标签引入
<select id="selectList" resultType="com.liquor.mybatis_01.pojo.Emp">
<include refid="EmpAllSelect"/>
<where>
<if test="name!=null">
name like concat('%',#{name},'%')
</if>
<if test="gender!=null">
and gender = #{gender}
</if>
</where>
order by update_time desc
</select>