之前介绍了MyBatis的快速入门以及Mapper代理开发,作为一款优秀的持久层框架,就不得不探讨一下MyBatis实现的增删改查功能了,而这也是我们学习的重点和核心所在。这次通过b站黑马的品牌数据增删改查案例,来学习MyBatis实现的增删改查功能,内容如下。

一、环境准备

1、创建tb_brand表,添加数据

打开Navicat,连接MySQL,选择一个数据库,通过新建查询的方式创建tb_brand表,并添加数据。

对应的sql代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand
(
-- id 主键
id int primary key auto_increment,
-- 品牌名称
brand_name varchar(20),
-- 企业名称
company_name varchar(20),
-- 排序字段
ordered int,
-- 描述信息
description varchar(100),
-- 状态:0:禁用 1:启用
status int
);
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status)
values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1);

SELECT * FROM tb_brand;

2、编写实体类 Brand

新建一个mybatis-demo1的Maven项目,pom.xml里的坐标信息只需要将上次的复制过来就行。在其java目录下创建一个Brand实体类(com.itweb.pojo.Brand)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.itweb.pojo;
/**
* alt + 鼠标左键:整列编辑
* 在实体类中,基本数据类型建议使用其对应的包装类型
*/
public class Brand {
// id 主键
private Integer id;
// 品牌名称
private String brandName;
// 企业名称
private String companyName;
// 排序字段
private Integer ordered;
// 描述信息
private String description;
// 状态:0:禁用 1:启用
private Integer status;

// 提供get、set方法(鼠标右键快捷生成)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Integer getOrdered() {
return ordered;
}
public void setOrdered(Integer ordered) {
this.ordered = ordered;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
// 提供重写toString方法(鼠标右键快捷生成)
@Override
public String toString() {
return "Brand{" +
"id=" + id +
", brandName='" + brandName + '\'' +
", companyName='" + companyName + '\'' +
", ordered=" + ordered +
", description='" + description + '\'' +
", status=" + status +
'}';
}
}

3、准备测试用例

在项目的 test目录下创建一个MyBatisTest的测试类(com.itweb.test.MyBatisTest),如下

4、安装MyBatisX插件

MybatisX是一款基于IDEA的快速开发插件,为效率而生。
主要功能:
1)XML和接口方法相互跳转
2)根据接口方法生成statement
安装步骤:
File->settings->Plugins,搜索MybatisX,点击install安装即可,如下。

二、查询功能

1、查询所有数据

1.1 实现步骤

1)编写接口方法(Mapper接口)

参数:无
结果:List<Brand>

在java目录下创建一个BrandMapper接口(com.itweb.mapper.BrandMapper),代码如下

1
2
3
4
5
6
7
8
9
10
11
package com.itweb.mapper;

import com.itweb.pojo.Brand;
import java.util.List;

public interface BrandMapper {
/**
* 查询所有
*/
public List<Brand> selectAll();
}

2)编写SQL语句(SQL映射文件)
在resources目录下创建一个mapper目录(com/itweb/mapper),并在该目录下创建一个BrandMapper.xml文件。同时直接在resources目录下复制之前用到的logback.xml和mybatis-config.xml文件。

注:在resources目录下创建分层的包要用 / 而不是 .



BrandMapper映射文件里的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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">
<!--
namespace:名称空间
-->
<mapper namespace="com.itweb.mapper.BrandMapper">

<select id="selectAll" resultType="com.itweb.pojo.Brand">
select * from tb_brand;
</select>
</mapper>

3)编写测试用例并执行方法
在之前准备好的MyBatisTest的测试类下,编写如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.itweb.test;

import com.itweb.mapper.BrandMapper;
import com.itweb.pojo.Brand;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisTest {
@Test
public void testSelectAll() throws IOException {
// 1.加载核心配置文件,获取 SqlSessionFactory 对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 2.获取SqlSession对象,用来执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();

// 3.1 通过SqlSession的getMapper方法获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 3.2 调用对应方法完成sql的执行
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);

// 4.释放资源
sqlSession.close();
}
}

执行方法,结果如下,虽然成功查询出了所有数据,但是存在字段为null的问题,这是由于实体类的属性名与数据库表的列名不一致造成的。

4)解决属性名与列名不一致问题
在BrandMapper.xml映射文件中做相应的修改。
法1:

起别名,对不一样的类名起别名,别名与实体类属性名一样。缺点是每次查询都要定义一次别名。解决方案 => 定义 sql片段,提高复用性(但该方法又有不灵活的缺点)。

1
2
3
4
5
6
7
8
9
10
<!-- sql片段 -->
<sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>

<select id="selectAll" resultType="com.itweb.pojo.Brand">
select
<include refid="brand_column" />
from tb_brand;
</select>

法2:resultMap(最常用)
(1)定义<resultMap>标签
(2)在<select>标签中,使用resultMap属性替换resultType属性

定义 resultMap 完成不一致的属性名和列名的映射。其中id是主键字段的映射,result是一般字段的映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--
id:唯一标识
type:映射的类型,支持别名
-->
<resultMap id="brandResultMap" type="com.itweb.pojo.Brand">
<!--
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
result:完成一般字段的映射
column:表的列名
property:实体类的属性名
-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>

<select id="selectAll" resultMap="brandResultMap">
select * from tb_brand;
</select>

修改完成后,再次运行测试类,就可以正常显示了。

至此,查询所有数据的功能已实现。

2、查看详情

2.1 实现步骤

1)编写接口方法(Mapper接口)

参数: id
结果: Brand

在BrandMapper接口里添加方法

1
2
3
4
/**
* 查看详情:根据ID查询
*/
Brand selectById(int id); // alt+enter

2)编写SQL语句(SQL映射文件)
在BrandMapper.xml映射文件里添加SQL

1
2
3
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id = #{id};
</select>

3)编写测试用例并执行方法
在MyBatisTest测试类里面添加测试方法(直接复制一份之前的把方法名还有其他一些稍作改动即可)

执行结果如下图

4)相关总结

3、条件查询

3.1 多条件查询实现步骤

sql语句分析

注:like为模糊查询

1)编写SQL语句(SQL映射文件)
在BrandMapper.xml映射文件里添加SQL

1
2
3
4
5
6
7
<!--条件查询-->
<select id="selectByCondition" resultMap="brandResultMap">
select * from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>

2)编写接口方法(Mapper接口)

参数:所有查询条件
结果:List<Brand>

SQL 语句设置多个参数的方式(3种)

在BrandMapper接口里添加方法

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 条件查询
* 参数接收
* 1.散装参数:需要使用 @Param (" SQL 中的参数占位符名称")
* 2.对象参数:只需要保证 SQL 中的参数名和实体类属性名对应上,即可设置成功
* 3.Map集合参数:只需要保证 SQL 中的参数名和 map 集合的键的名称对应上,即可设置成功
*/
List<Brand> selectByCondition(@Param("status")int status,@Param("companyName")String companyName,@Param("brandName")String brandName);

List<Brand> selectByCondition(Brand brand);

List<Brand> selectByCondition(Map map);

3)编写测试用例并执行方法
在MyBatisTest测试类里面添加测试方法(直接复制一份之前的把方法名还有其他一些稍作改动即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    @Test
public void testSelectByCondition() throws IOException {
// 1.接收参数(散装参数)
int status = 1;
String companyName = "华为";
String brandName = "华为";

// 处理参数
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";

// 2.封装对象
/* Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);*/

// 3.Map集合参数
/*Map map = new HashMap();
map.put("status" , status);
map.put("companyName", companyName);
map.put("brandName" , brandName);*/

// 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
// 执行方法
List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
// List<Brand> brands = brandMapper.selectByCondition(brand);
// List<Brand> brands = brandMapper.selectByCondition(map);

System.out.println(brands);

// 释放资源
sqlSession.close();
}

运行结果,查询成功。同理可运行2或3,打开注释运行即可,结果一样都能查询成功。
4)存在问题分析

上述虽然可以查询成功,但却是基于用户将各搜索框都输入了之后才能查询成功,比如有3个搜索框,当用户只输入1个时,就不能查询成功了。因此,接下来的动态条件查询就是用于解决该问题。

3.2 多条件动态查询

动态SQL:SQL语句会随着用户的输入或外部条件的变化而变化。

参考官方文档:https://mybatis.org/mybatis-3/dynamic-sql.html

Mapper接口不用变,只需要改造SQL语句就行。在BrandMapper.xml映射文件里修改SQL,将上一次条件查询的代码注释掉,添加如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--
动态条件查询
* if: 条件判断
* test:逻辑表达式
* 问题:
* 恒等式 where 1 = 1
* <where>标签 替换 where 关键字
-->
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
/* where 1 = 1*/
<where>
<if test="status != null">
and status = #{status}
</if>
<if test="companyName != null and companyName != '' ">
and company_name like #{companyName}
</if>
<if test="brandName != null and brandName != '' ">
and brand_name like #{brandName}
</if>
</where>
</select>

执行测试方法,只往Map集合里添加一个字段如companyName,即相当于用户查询时只输入一个条件,发现成功查询出数据。

3.3 单条件动态查询

1.从多个条件中选择一个
2.choose (when, otherwise):选择,类似于Java中的 switch语句

1)编写接口方法(Mapper接口)

参数:brand
结果:List<Brand>

在BrandMapper接口里添加方法

1
2
3
4
5
6
/**
* 单条件动态查询
* @param brand
* @return
*/
List<Brand> selectByConditionSingle(Brand brand);

2)编写SQL语句(SQL映射文件)
在BrandMapper.xml映射文件里添加SQL(用了<where>标签)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="selectByConditionSingle" resultMap="brandResultMap">
select * from tb_brand
<where>
<choose> <!--相当于switch-->
<when test="status != null"> <!--相当于case-->
status = #{status}
</when>
<when test="companyName != null and companyName != '' "> <!--相当于case-->
company_name like #{companyName}
</when>
<when test="brandName != null and brandName != ''"> <!--相当于case-->
brand_name like #{brandName}
</when>
</choose>
</where>

</select>

3)编写测试用例并执行方法
在MyBatisTest测试类里面添加测试方法(直接复制一份之前的把方法名还有其他一些稍作改动即可)

三、添加功能

1、实现步骤

1)编写接口方法(Mapper接口)
在BrandMapper接口里添加方法

1
2
3
4
5
/**
* 添加
* @param brand
*/
void add(Brand brand);

2)编写SQL语句(SQL映射文件)
在BrandMapper.xml映射文件里添加SQL

1
2
3
4
5
<!--添加-->
<insert id="add">
insert into tb_brand(brand_name,company_name,ordered,description,status)
values (#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>

3)编写测试用例并执行方法
在MyBatisTest测试类里面添加测试方法(直接复制一份之前的把方法名还有其他一些稍作改动即可)

注:不要忘记提交事务,非常关键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/ 添加
@Test
public void testAdd() throws IOException {
// 接收参数(散装参数)
int status = 1;
String companyName = "波导手机";
String brandName = "波导";
String description = "手机中的战斗机";
int ordered = 100;

// 封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);

//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
brandMapper.add(brand);

// 提交事务(关键)
sqlSession.commit(); // 手动提交事务

//5. 释放资源
sqlSession.close();
}

上面的方法为手动提交事务,除此之外,还可以设置自动提交事务。

1
2
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);

执行测试方法,测试成功后返回 Navicat查询数据库表,发现添加成功。

2、主键返回


返回添加数据的主键

1
<insert useGeneratedKeys="true" keyProperty="id">

四、修改功能

1、修改全部字段

1.1 实现步骤

1)编写接口方法(Mapper接口)

参数:所有数据
结果: void

在BrandMapper接口里添加方法

1
2
3
4
5
6
/**
* 修改
* @param brand
* @return
*/
int update(Brand brand);

2)编写SQL语句(SQL映射文件)
在BrandMapper.xml映射文件里添加SQL

1
2
3
4
5
6
7
8
9
10
11
<!--修改全部字段-->
<update id="update">
update tb_brand
set
brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
description = #{description},
status = #{status}
where id = #{id}
</update>

3)编写测试用例并执行方法
在MyBatisTest测试类里面添加测试方法(直接复制一份之前的把方法名还有其他一些稍作改动即可),如下

执行测试方法,测试成功后返回 Navicat查询数据库表,发现修改成功。

2、修改动态字段


接口方法和之前的一样,SQL语句的修改见上图,测试方法如下(只接收某个字段如 status)

执行测试方法,测试成功后返回 Navicat查询数据库表,发现修改成功。

五、删除功能

1、删除一个


接口方法和SQL语句见上图,测试代码也只需简单修改即可,如下

执行测试方法,发现数据成功被删除。

2、批量删除

所谓批量删除,就是可以一次选中多个并删除。

接口方法和SQL语句见上图,测试代码也只需简单修改即可。关于SQL语句的编写,需要注意如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--
mybatis会将数组参数,封装为一个Map集合。
* 默认:array = 数组
* 使用@Param注解改变map集合的默认key的名称
-->
<delete id="deleteByIds">
delete from tb_brand where id
in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
;
</delete>

执行测试方法


返回 Navicat查询结果,发现批量删除成功。

六、参数传递

结论:建议将来都使用 @Param注解来修改 Map集合中默认的键名,并使用修改后的名称来获取值,这样可读性更高!

1、多个参数

封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名

1
2
3
4
5
6
7
8
9
map.put("arg0",参数值1)
map.put("param1",参数值1)
map.put("param2",参数值2)
map.put("agr1",参数值2)
---------------@Param("username")
map.put("username",参数值1)
map.put("param1",参数值1)
map.put("param2",参数值2)
map.put("agr1",参数值2)
1
User select(@Param( "username") string username ,string password);

2、单个参数

2.1 POJO类型

直接使用,属性名 和 参数占位符名称一致

2.2 Map集合

直接使用,键名 和 参数占位符名称一致

2.3 Collection

封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名

1
2
map.put("arg0",collection集合);
map.put("collection",collection集合);

2.4 List

封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名

1
2
3
map.put("arg0",list集合);
map.put("collection",list集合);
map.put("list",list集合);

2.5 Array

封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名

1
2
map.put("arg0",数组);
map.put("array",数组);

2.6 其他类型

直接使用

七、注解开发


使用注解开发,编写的接口方法,如

1
2
@Select("select * from tb_user where id = #{id}")
User selectById(int id);

可以看出,确实更加方便简洁,但注解不是任何情况下都是好用的。下面截取的是官方文档原话。

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java注解不仅力不从心,还会让你本就复杂的SQL语句更加混乱不堪。因此,如果你需要做一些很复杂的操作,最好用XML来映射语句。