MyBatis Series
MyBatis 安装与入门: https://blog.yexca.net/archives/215
MyBatis 使用: 本文
删除
使用 #{}
为占位符,内部为参数名
@Mapper
public interface EmpMapper {
// 删除
@Delete("delete from mybatis.emp where id = #{id}")
public void delete(Integer id);
}
测试
@SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
void contextLoads() {
empMapper.delete(17);
}
}
一般不需要返回值,返回值为此次操作影响的行数
@Mapper
public interface EmpMapper {
// 删除
@Delete("delete from mybatis.emp where id = #{id}")
public int delete(Integer id);
}
占位符
参数占位符有 #{}
和 ${}
占位符 | #{} |
${} |
---|---|---|
形式 | 预编译 | 拼接 |
使用时机 | 参数传递、登录等 | 对表名、列表动态设置 |
优劣 | 安全,性能高 | 存在 SQL 注入问题 |
插入 (新增)
同样使用占位符,不过若传递的参数过多可以使用对象封装,形式参数名为对象的参数名
@Mapper
public interface EmpMapper {
// 增加
@Insert("insert into mybatis.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 Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
Emp emp = new Emp();
emp.setUsername("Tom");
emp.setName("汤姆");
emp.setGender((short) 1);
emp.setImage("tom.jpg");
emp.setJob((short) 1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setDeptId(1);
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
empMapper.insert(emp);
}
}
获取主键值
某些情况下在数据添加成功后,需要获取插入数据库数据的主键
@Mapper
public interface EmpMapper {
// 获取主键,第一个属性定义主键赋值在emp对象的id属性,第二个属性代表需要获取返回主键值
@Options(keyProperty = "id", useGeneratedKeys = true)
// 增加
@Insert("insert into mybatis.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);
}
更新 (修改)
与增加类似,可以封装到一个对象里
@Mapper
public interface EmpMapper {
// 修改
@Update("update mybatis.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);
}
测试
@SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testUpdate(){
Emp emp = new Emp();
emp.setUsername("Tom2");
emp.setName("汤姆2");
emp.setGender((short) 1);
emp.setImage("tom.jpg");
emp.setJob((short) 1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setDeptId(1);
emp.setUpdateTime(LocalDateTime.now());
emp.setId(19);
empMapper.update(emp);
}
}
查询
查询有两种,根据 id 查询全部属性,根据条件查询
根据 ID 查询
接口
@Mapper
public interface EmpMapper {
// 根据ID查询
@Select("select * from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
测试
@SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testIdSelect(){
Emp emp = empMapper.idSelect(19);
System.out.println(emp);
}
}
结果
Emp(id=19, username=Tom2, password=123456, name=汤姆2, gender=1, image=tom.jpg, job=1, entrydate=2000-01-01, deptId=null, createTime=null, updateTime=null)
这样查询出来的因为类 Emp 与数据库的部分字段名字不同,从而结果为 null
有三种方法解决
使用别名
@Mapper
public interface EmpMapper {
// 方法一:别名
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id deptId, create_time createTime, update_time updateTime" +
" from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
使用 @Results 注解
@Mapper
public interface EmpMapper {
// 方法二:@Results @Result 注解
@Results({
@Result(column = "dept_id", property = "deptId"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
@Select("select * from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
使用自动封装
如果字段名与属性名符合驼峰命名规则,可以开启驼峰命名自动封装,Mybatis 会自动通过驼峰命名规则映射,具体为数据库是下划线 a_column
映射到 Java 属性 aColumn
开启驼峰命名需在 application.properties
加入
mybatis.configuration.map-underscore-to-camel-case=true
然后直接使用最初的代码
@Mapper
public interface EmpMapper {
// 根据ID查询
@Select("select * from mybatis.emp where id = #{id}")
public Emp idSelect(Integer id);
}
为了方便使用,可以在 IDEA 中安装 MyBatisX 插件
根据条件查询
需求:根据员工姓名 (模糊匹配)、性别 (精准匹配)、入职时间 (范围) 搜索满足条件的员工信息
@Mapper
public interface EmpMapper {
// 根据条件查询
// 注意此处name like '%${name}%' 使用的${},可以在''内使用
@Select("select * from mybatis.emp where name like '%${name}%' and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
因为使用了插值,进行字符串拼接,所以此方法不安全
@Mapper
public interface EmpMapper {
// 更安全的条件查询
@Select("select * from mybatis.emp where name like concat('%',#{name},'%') and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
使用了 concat 函数拼接字符串。测试
@SpringBootTest
class Mybatis02ApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testConditionSelect(){
List<Emp> empList = empMapper.conditionSelect("张", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
System.out.println(empList);
}
}
参数名说明
在 SQL 中使用的 #{}
中的变量名在 SpringBoot 2.x 以后的版本会被自动识别,但在 1.x 版本或者单独使用 MyBatis 时方法定义的变量名需要使用注解指定才可被识别
@Mapper
public interface EmpMapper {
// 更安全的条件查询
@Select("select * from mybatis.emp where name like concat('%',#{name},'%') and gender = #{gender} and " +
"entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
// 上述代码在1.x或单独使用MyBatis时不生效,函数要添加注解
public List<Emp> conditionSelect(@Param("name") String name, @Param("gender") short gender, @Param("begin") LocalDate begin, @Param("end") LocalDate end);
使用 XML 映射文件
如果 SQL 代码比较简短使用注解很方便,但若是 SQL 较长或复杂则会显得稍乱,为此可以使用 XML 映射文件。以下为规范
- XML 映射文件的名称与 Mapper 接口名称一致,并且将 XML 映射文件和 Mapper 接口放在相同包下 (同包同名)
- XML 中 namespace 属性为 Mapper 接口全限定名一致
- XML 中 SQL 语句的 id 属性与 Mapper 接口中方法名一致,并且返回类型一致
XML
在 Maven 工程,非 Java 文件放在 src/main/resources
下,在此目录下创建与 Mapper 接口一致的目录
如接口为 net.yexca.mapper.EmpMapper.java
,则 XML 文件在 net.yexca.mapper.EmpMapper.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">
<!-- 上方头文件到官方网站寻找 -->
<!-- namespace属性为接口 -->
<mapper namespace="net.yexca.mapper.EmpMapper">
<!-- id属性为方法名 -->
<!-- resultType为单条记录所封装的类型 -->
<select id="conditionSelect" resultType="net.yexca.pojo.Emp">
select * from mybatis.emp where name like concat('%',#{name},'%') and gender = #{gender} and
entrydate between #{begin} and #{end} order by update_time desc
</select>
</mapper>
接口类
@Mapper
public interface EmpMapper {
public List<Emp> conditionSelect(String name, short gender, LocalDate begin, LocalDate end);
}
动态 SQL
动态 SQL 为随着用户的输入或外部条件的变化而变化的 SQL 语句
例如上述最后一个条件查询,必须输入全部三个条件才能查询,可实际使用或许可以仅指定其中一两个或不指定 (查询全部),如果按照上述指令执行将返回空结果
where if
用于判断条件是否成立,使用 test 属性进行条件判断,如果条件为 true,则拼接 SQL
例如,当姓名不为空时,拼接条件通过姓名查询
<if test="name!=null">
name like contact('%',#{name},'%')
</if>
而 where 标签则管理是否生成 where 关键字并自动去除 and 和 or 关键字
<select id="conditionSelect" resultType="net.yexca.pojo.Emp">
select * from mybatis.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>
set
替换 SQL 中 set 关键字,并自动去除多余的逗号,用于 UPDATE 语句中
例如以下 SQL 语句修改为动态 SQL
update mybatis.emp
set username = #{username},
name = #{name},
gender = #{gender},
image = #{image},
job = #{job},
entrydate = #{entrydate},
dept_id = #{deptId},
update_time = #{updateTime}
where id = #{id};
XML
<update id="update2">
update mybatis.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>
foreach
foreach 标签用于遍历
<!--遍历的集合 遍历出来的元素 分隔符 开始前后拼接的SQL片段-->
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
假如 ids 为 [13, 14, 15],以上将生成 (13,14,15)
需求,遍历删除 id 为 1,2,3。SQL 语句为
delete from emp where id in(1,2,3)
接口方法
public void deleteByIds(List<Integer> ids);
XML
<delete id="deleteByIds">
delete from mybatis.emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
测试
@Test
public void testDeleteByIds(){
List<Integer> ids = Arrays.asList(13,14,15);
empMapper.deleteByIds(ids);
}
sql 与 include
在开发过程中可能会出现大量重复的 SQL 语句,可以同 sql 标签定义可重用的 SQL 片段,再通过 include 标签重用
<!-- 通过id属性唯一标识语句 -->
<sql id = "commonCode">
<!-- SQL语句 -->
</sql>
<!-- 通过refid属性指定引用的语句 -->
<include refid = "commonCode" />