MyBatis 增删改查案例
准备环境
- 为了开发更加方便推荐安装 MyBatisX 插件
- 数据库:准备数据库表 tb_brand
- pom.xml 文件:已导入 MaBatis 和 MySQL 坐标
- 已配置好 mybatis-config.xml 配置文件
- 设置好了 typeAliases 标签
- 设置好了数据库的连接信息
- 实体类:在项目下新建 pojo 软件包,创建 Brand 实体类
- 测试用类:将主程序写在 test 文件夹的项目包下
测试用类及项目结构如下图所示
MyBatisX 插件
MyBatisX 是一款基于 IDEA 的快速开发插件,为效率而生
插件的主要功能有:
- XML 和接口方法相互跳转
- 根据接口方法生成 statement
安装完重启 IDEA,相应的 xml 文件图标就变成小鸟的图标了
红色的小鸟:SQL 的映射文件
蓝色的小鸟:Mapper 的接口
查询所有数据
1、编写接口方法:Mapper 接口
- 参数:无
- 返回类型:
List<Brand>
1 | List<Brand> selectAll(); |
2、编写 Mapper 配置文件
1 | <!--查询标签,id为这条SQL语句的唯一标识,resultType为返回结果类型,不区分大小写--> |
3、执行方法,测试
MaBatis 完成操作只需要三步
- 编写接口方法
- 编写 SQL
- 执行方法
查询结果中部分字段显示为 null
查询出来的结果中,部分字段显示 NULL,为什么呢?
因为这些字段,原本在数据库中比如说 brand_name
,到了 Java 当中变量名就变成了 brandName
(在 POJO 的实体类),数据库表的字段名称和实体类的属性名称不一样,所有就不会为我们自动封装了
解决办法 1
在 SQL 查询语句中为相应的字段设置别名,让别名和实体类的属性名一致即可
原来的 SQL 查询
1 | <select id="selectAll" resultType="brand"> |
修改后的 SQL 查询
1 | <select id="selectAll" resultType="brand"> |
但是这样每进行一次查询都需要重新设置一次,而且不能复用
解决办法 2
使用 resultMap 标签(最为常用)
1 | <resultMap id="brandResultMap" type="brand"> |
resultMap 标签中:
- id:唯一标识
- type:映射的类型,支持别名
- id 子标签:完成主键字段的映射,具有属性 column 表的列名和 property 实体类的属性,以上代码中并没有演示 id 子标签的使用
- result 子标签:完成一半字段的映射,具有属性 column 表的列名和 property 实体类的属性
注意:select 标签中原先的 resultType="brand"
改为了 resultMap="brandResultMap"
,因为在 resultMap 标签中 type
属性也定义了类型
根据 id 查询数据
1、编写接口方法:Mapper 接口
- 参数:id
- 返回类型:Brand 类
1 | //传入一个id,最终返回一个Brand类型 |
2、编写 SQL 语句
1 | <select id="selectById" resultMap="brandResultMap" > |
3、执行方法,测试
只需要修改执行方法这一部分代码,其他地方不需要改动
1 | //4.执行方法 |
条件查询 - 多条件查询
1、编写接口方法:Mapper 接口
- 参数:所有查询条件
- 返回类型:
List<Brand>
2、编写 SQL 语句
1 | <select id="selectByCondition" resultMap="brandResultMap"> |
注意:以上代码中需输入三个参数才能查出结果,如果某一个参数没有填写就查询不出来,因此需要 SQL 语句动态变化(SQL 语句会随着用户的输入或外部条件的变化而变化)
解决方案 1:在 where 后面添加一个恒等式,然后所有的判断条件语句中都可以加上 and
这个关键词
1 | select * |
解决方案 2:利用 MaBatis 提供的 where 标签
这样的话,就不需要管到底用户传递了几个参数过来,会动态判断是否需要添加 and
关键字
1 | <where> |
3、执行方法
1 | //执行方法 |
三种接收参数的格式
1. 散装参数格式
1 | //1.散装参数的格式,需要使用@Param("SQL参数占位符名称") |
2. 对象参数
对象的属性名称要和参数占位符一致
1 | List<Brand> selectByCondition(Brand brand); |
注意:对于对象参数,在处理参数的时候,需要有封装对象的处理
1 | //接收参数 |
三、Map 对象格式
1 | //接收参数 |
MaBatis 对动态 SQL 有很强大的支撑
- if
- choose(when,otherwise)
- trim(where,set)
- foreach
条件查询 - 多个条件中选择一个
多个条件中选择一个,如下图片所示的下拉选项一样
1 | <!--单条件查询--> |
对于如上代码,当给定一个值时好说,但是如果一个值都没有给的话,此时就需要一个保底的方案,不然 SQL 会报语法错误。
那么我们可以使用 <otherwise>
标签,标签中边放一个恒定值,那么当用户给了一个参数之后,就不会执行 otherwise 中的内容,如果没有给参数的话,恒等式派上用场
1 | <choose><!--相当于switch--> |
那么更进一步,可以和前面的 where 标签结合起来,就不需要使用到 otherwise 标签了
1 | <where> |
添加数据
1、编写接口方法:Mapper 接口
- 参数:除了 id 主键之外的所有参数
- 结果:void
1 | void add(Brand brand); |
2、编写 SQL 语句
1 | <!--添加功能--> |
3、执行方法
1 | //4.执行方法 |
在没有手动提交事务以前,虽然没有报错,但是查看数据库,并没有添加数据,原来是 autocommit
设置成了 false,所以需要我们手动提交一下
![[Pasted image 20221203151443.png]]
注:那么每次都需要手动提交事务,那不是挺麻烦。可以在获取 sqlSession 对象的时候就设置好自动提交事务
1 | SqlSession sqlSession = sqlSessionFactory.openSession(true); |
MaBatis 事务
- openSession ():默认开启事务,进行增删改操作后需要使用 sqlSession.commit () 手动提交事务
- openSession (true):可以设置为自动提交事务(即关闭事务)
添加数据并主键返回
在数据添加成功后,需要获取插入数据库数据的主键的值
通过之前的添加方法,实际上是已经添加了一条数据,但是这个新添加的数据的 id 并没有绑定在这个新添加的对象上,所以 brand.getId()
返回的是一个 null
1 | //4.执行方法 |
那么如何在添加数据的时候,将主键值和这个新添加的对象绑定在一起呢?
1 | <!--添加功能并主键返回--> |
在 insert 标签中添加两个属性 useGeneratedKeys="true"
和 keyProperty="id"
修改 - 修改全部字段
1、编写接口方法:Mapper 接口
- 参数:所有数据
- 返回类型:void
1 | void update(Brand brand); //不返回结果值 |
2、编写 SQL 语句
1 | <!--修改功能--> |
3、执行方法
需要在传入参数的时候把 id 也传进去,然后封装对象的时候,也 brand.setid (id)
1 | //4.执行方法 |
会存在一个问题,当我们之传入了某几个参数之后,另外几个没有传参,那么运行的话,就会把没有传参的字段赋值为 NULL
修改 - 动态修改字段
比如设置新密码,此时账号就不需要修改,而是只需要修改某几个数据,那么就需要使用到动态 SQL
1 | <!--修改动态字段--> |
那么此时,我们可以之修改某几个参数,另外几个参数没有修改的话,也不会被 NULL 值覆盖了
删除一个
1、编写接口方法:Mapper 接口
- 参数:id
- 返回类型:void
1 | void deleteById(int id); |
2、编写 SQL 语句
1 | <delete id="deleteById"> |
3、执行方法
传递参数的时候只要将 id 传进来就可以了
1 | //4.执行方法 |
批量删除
1、编写接口方法:Mapper 接口
- 参数:id 数组
- 返回类型:void
1 | void deleteByIds(int[] ids) ; |
如果没有加注解的话,就报了下面的错误
1 | Parameter 'ids' not found. Available parameters are [array, arg0] |
2、编写 SQL 语句
占位符的个数需要根据删除的个数进行变化,也就是需要遍历这个数组,MyBatis 中提供了相应的标签 <foreach>
,可以用来遍历数组
1 | <!--MaBatis会将数组参数封装为一个Map集合,默认情况下 |
以上 SQL 语句仍然有问题,当有多个 id 时,就变成了如下
1 | <foreach collection="ids" item="id"> |
占位符之间少了逗号分隔符,因此还需要在 foreach 标签中添加属性
1 | <!--MaBatis会将数组参数封装为一个Map集合,默认情况下 |
3、执行方法
传入一个 ids 数组
1 | //4.执行方法 |
改进
如下代码可以正常执行功能,但是 foreach 标签包裹在小括号中,可不可以去掉外边的小括号呢?
1 | <!--MaBatis会将数组参数封装为一个Map集合,默认情况下 |
改为如下代码,即在 foreach 标签中添加 open 和 close 属性
1 | <delete id="deleteByIds"> |
MyBatis 参数传递
MyBatis 接口方法中可以接收各种各样的参数,MyBatis 底层对于这些参数进行不同的封装处理方式。扩展阅读:Mybatis @Param 注解的作用_DoUUnderstand 的博客 - CSDN 博客
- 单个参数
- POJO 类型:直接使用,属性名和参数占位符名称一致
- Map 集合:直接使用,键名和参数占位符名称一致
- Collection:封装为 Map 集合
- map.put (“arg0”,collection 集合)
- map.put (“collection”,collection 集合)
- List:封装成 Map 集合
- map.put (“arg0”,list 集合)
- map.put (“collection”,list 集合)
- map.put (“list”,list 集合)
- Array:封装成 Map 集合
- map.put (“arg0”, 数组)
- map.put (“array”, 数组)
- 其他类型:直接使用
- 多个参数
- 比如查询时的散装参数
总之,当有多个参数时,不要使用 Map 集合中的默认键名,使用 @Param
注解的方式来替换默认的键名
Map 集合中默认的键名如下
1 | map.put("arg0",参数值1) |
设置好 @Param
注解之后呢,会将新的键名覆盖掉默认的 arg0、arg1 键名,可读性更强
1 | List<Brand> selectByCondition(int status, String companyName) ; |
1 | <!--添加@Param前--> |