一、Mybatis 基础操作
1 需求
功能列表:
- 查询
根据主键ID查询
条件查询- 新增
- 更新
- 删除
根据主键ID删除
根据主键ID批量删除
2 准备
实施前的准备工作:
- 准备数据库表
- 创建一个新的 springboot 工程,选择引入对应的起步依赖(mybatis、mysql 驱动、lombok)
- application.properties 中引入数据库连接信息
- 创建对应的实体类 Emp(实体类属性采用驼峰命名)
- 准备 Mapper 接口 EmpMapper
3 删除
3.1 功能实现
当我们点击后面的"删除"按钮时,前端页面会给服务端传递一个参数,也就是该行数据的 ID。我们接收到 ID 后,根据 ID 删除数据即可。
@Mapper
public interface EmpMapper {
//根据ID删除数据
@Delete("delete from emp where id = #{id}")
public void delete(Integer id);
//public int delete(Integer id);
}
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
//根据ID删除
@Test
public void testDelete(){
//int delete = empMapper.delete(16);
//System.out.println(delete);
empMapper.delete(18);
}
}
3.2 日志输入
在 Mybatis 当中我们可以借助日志,查看到 sql 语句的执行、执行传递的参数以及执行结果。具体操作如下:
- 打开 application.properties 文件
- 开启 mybatis 的日志,并指定输出到控制台
#指定mybatis输出日志的位置,输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3.3 预编译 SQL
预编译 SQL 有两个优势:
- 性能更高
- 更安全(防止 SQL 注入)
SQL 注入:是通过操作输入的数据来修改事先定义好的 SQL 语句,以达到执行代码对服务器进行攻击的方法。
在 Mybatis 中提供的参数占位符有两种:${…} 、#{…}
- #{…}
执行 SQL 时,会将#{…}替换为?,生成预编译 SQL,会自动设置参数值
使用时机:参数传递,都使用#{…}- ${…}
拼接 SQL。直接将参数拼接在 SQL 语句中,存在 SQL 注入问题
使用时机:如果对表名、列表进行动态设置时使用
4 新增
4.1 基本新增
@Mapper
public interface EmpMapper {
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
" values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public void insert(Emp emp);
}
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
//新增员工
@Test
public void testInsert(){
//构造员工对象
Emp emp = new Emp();
emp.setUsername("Tom3");
emp.setName("汤姆3");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//执行新增员工信息操作
empMapper.insert(emp);
}
}
4.2 主键返回
默认情况下,执行插入操作时,是不会主键值返回的。如果我们想要拿到主键值,需要在 Mapper 接口中的方法上添加一个 Options 注解,并在注解中指定属性 useGeneratedKeys = true 和 keyProperty ="实体类属性名
@Mapper
public interface EmpMapper {
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)" +
" values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
public void insert(Emp emp);
}
5 更新
@Mapper
public interface EmpMapper {
//更新员工
@Update("update emp set username = #{username}, name = #{name}, gender = #{gender}, image = #{image}," +
" job = #{job}, entrydate = #{entrydate}, dept_id = #{deptId},update_time = #{updateTime} where id = #{id}")
public void update(Emp emp);
}
6 查询
6.1 根据ID查询
@Mapper
public interface EmpMapper {
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
}
6.2 数据封装
实体类属性名 和 数据库表查询返回的字段名一致,mybatis 会自动封装。
如果实体类属性名 和 数据库表查询返回的字段名不一致,不能自动封装。
- 起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样。
//方案一: 给字段起别名, 让别名与实体类属性一致
@Select("select id, username, password, name, gender, image, job, entrydate, " +
"dept_id deptId, create_time createTime, update_time updateTime from emp where id = #{id}")
public Emp getById(Integer id);
- 手动结果映射:通过 @Results及@Result 进行手动结果映射。
//方案二: 通过@Results, @Result注解手动映射封装
@Results({
@Result(column = "dept_id", property = "deptId"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
@Select("select * from emp where id = #{id}")
public Emp getById(Integer id);
- 开启驼峰命名:如果字段名与属性名符合驼峰命名规则,mybatis 会自动通过驼峰命名规则映射。在 application.properties 中添加:
#开启驼峰命名自动映射,即从数据库字段名 a_column 映射到Java 属性名 aColumn。
mybatis.configuration.map-underscore-to-camel-case=true
6.3 条件查询
- 方式一
模糊查询使用${…}进行字符串拼接,这种方式呢,由于是字符串拼接,并不是预编译的形式,所以效率不高、且存在sql注入风险。
@Mapper
public interface EmpMapper {
//条件查询员工
//方式一
@Select("select * from emp where name like '%${name}%' and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc ")
public List<Emp> list(String name, Short gender, LocalDate begin , LocalDate end);
}
- 方式二(解决SQL注入风险)
使用 MySQL 提供的字符串拼接函数:concat(‘%’ , ‘关键字’ , ‘%’)
@Mapper
public interface EmpMapper {
//条件查询员工
//方式二
@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc ")
public List<Emp> list(String name, Short gender, LocalDate begin , LocalDate end);
}
6.4 参数名说明
二、Mybatis 的 XML 配置文件
1 XML 配置文件规范
规范
- XML 映射文件的名称与 Mapper 接口名称一致,并且将 XML 映射文件和 Mapper 接口放置在相同包下 (同包同名)。
- XML 映射文件的 namespace 属性为 Mapper 接口全限定名一致。
- XML 映射文件中sql语句的 id 与 Mapper 接口中的方法名一致,并保持返回类型一致。
2 XML 配置文件实现
@Mapper
public interface EmpMapper {
//动态条件查询
public List<Emp> list(String name, Short gender, LocalDate begin , LocalDate end);
}
<?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.itheima.mapper.EmpMapper">
<!--resultType: 单条记录封装的类型-->
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>
3 MybatisX 的使用
MybatisX 是一款基于 IDEA 的快速开发 Mybatis 的插件,为效率而生。
使用 Mybatis 的注解,主要是来完成一些简单的增删改查功能。如果需要实现复杂的 SQL 功能,建议使用 XML 来配置映射语句。
官方说明:https://mybatis.net.cn/getting-started.html
三、Mybatis 动态 SQL
1 什么是动态SQL
SQL 语句会随着用户的输入或外部条件的变化而变化,我们称为:动态 SQL。
2 动态 SQL-if
2.1 条件查询
- <if>:用于判断条件是否成立。使用 test 属性进行条件判断,如果条件为 true,则拼接 SQL。
- <where>:where 元素只会在子元素有内容的情况下才插入 where 子句。而且会自动去除子句的开头的 AND 或 OR。
<?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.itheima.mapper.EmpMapper">
<!--resultType: 单条记录封装的类型-->
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
<where>
<if test="name != null">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
</mapper>
2.2 更新员工
- <set>:动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)
<?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.itheima.mapper.EmpMapper">
<!-- 动态更新员工-->
<update id="update2">
update 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>
</mapper>
3 动态 SQL-foreach
@Mapper
public interface EmpMapper {
//批量删除员工
public void deleteByIds(List<Integer> ids);
}
<?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.itheima.mapper.EmpMapper">
<!--批量删除员工 (18,19,20)-->
<!--
collection: 遍历的集合
item: 遍历出来的元素
separator: 分隔符
open: 遍历开始前拼接的SQL片段
close: 遍历结束后拼接的SQL片段
-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</mapper>
4 动态 SQL-sql&include
- :定义可重用的 SQL 片段。
- :通过属性 refid,指定包含的 sql 片段。