简介
MyBatis 官网 是这么介绍它自己的:
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
示例代码
- awesome-spring-boot-examples
依赖
这里仅展示和 MyBatis 相关的数据库依赖项,完整的示例,请下载示例代码。
1 | <!--mysql--> |
spring-boot-start
系列的包,真是给 Spring Boot 开发带来了极大的便利,它的项目地址是:
- Github-mybatis/spring-boot-starter
- 官宣-What is MyBatis-Spring-Boot-Starter? 推荐
配置
创建 users
表的 SQL:
1 | SET FOREIGN_KEY_CHECKS=0; |
说明:
- id 设置的是自增,当插入数据时,可以不传或者传
null
值,都可以; - 插入数据,可以指定
column
也可以不指定;
application-dev.properties
:
1 | swagger.enable=true |
- 配置驼峰属性自动映射,例如实体中属性为
userSex
,数据库属性为user_sex
,MyBatis 默认是不能自动转换的。我们可以配置mybatis.configuration.map-underscore-to-camel-case
实现自动映射。如果不进行此配置,通常我们要自定义以下结果集映射:
1 | ({ |
在很多 Select
语句需要做结果映射时,自然是相当麻烦。除了上面配置「驼峰属性自动映射」,也可以用在 @Results
中使用 id
来标识一个映射关系,然后可以用 @ResultMap
复用这个映射关系:
1 | "SELECT * FROM users") ( |
代码
这里仅展示关键的部分的代码,完整可看下文的示例代码。
实体类:
1 |
|
dao/mapper
接口,数据库交互(Data Access Object
)层:
1 | public interface UserMapper { |
说明:
insert
这里用了一个@Options
的注解,实现了「主键回填」的功能,也就是说,再创建好一个user
之后,user
请求体中的id
属性会自动赋值好;@SelectKey
注解被注释掉了,这个注解也同样可以实现「主键回填」的功能;
service 接口:
1 | public interface UserService { |
service 接口的实现类:
1 |
|
controller 类:
1 |
|
启动类:
1 |
|
@MapperScan("com.winter.mapper")
这个注解非常的关键,这个对应了项目中mapper/dao
所对应的包路径。- 如果不用上面的方式,就需要在每个
mapper/dao
类上使用@Mapper
注解;
分页
通常,在进行查询时,我们为了避免一次性返回所有结果,造成接口响应慢等问题。通常会进行分页。比如查询所有用户的接口,实际应用中,用户数据可能会很多,如果全部一次返回,明显不合适。这时候,就需要进行分页查询。
本文我们选用插键 pagehelper-spring-boot-starter
要进行分页。
添加依赖
1 | <!-- 分页插件 --> |
分页配置
需要添加相应的配置:
1 | pagehelper分页插件 |
分页插键参数介绍:
helperDialect
:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式reasonable
:分页合理化参数,默认值为 false。当该参数设置为 true 时,pageNum<=0
时会查询第一页,pageNum>pages
(超过总数时),会查询最后一页。默认 false 时,直接根据参数进行查询params
:为了支持startPage(Object params)
方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置pageNum,pageSize,count,pageSizeZero,reasonable
,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
。supportMethodsArguments
:支持通过 Mapper 接口参数来传递分页参数,默认值 false,分页插件会从查询方法的参数值中,自动根据上面params
配置的字段中取值,查找到合适的值时就会自动分页pageSizeZero
:默认值为 false,当该参数设置为 true 时,如果pageSize=0
或者RowBounds.limit = 0
就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。我测试时,发现不设置,pageSize=0
也会返回全部;
代码变动
mapper
中查找全部用户的方法改成如下:
1 | "SELECT * FROM users") ( |
service 接口和其实现类的方法改成:
1 | PageInfo<UserEntity> getAll(int pageNum, int pageSize); |
service 接口实现类:
1 |
|
注意点:
PageHelper.startPage(pageNo,pageSize);
只对其后的第一个查询有效;
controller 类:
1 | "获取用户列表", notes = "获取全部用户信息") (value = |
进一步
上面的分页结果返回的内容有点多,一些属性并不想放在返回体中。可以进一步优化。编写工具类限定关心的属性。
分页查询结果封装类:
1 |
|
分页查询工具类:
1 | public class PageUitls { |
接口方法:
1 | /** |
接口实现类:
1 |
|
这样改写后,返回体就简洁许多了:
1 | { |
IN 查询
关于 MyBatis 的 IN 查询,也是试验了很久,才 OK 的。 StackOverflow 上就有类似的问题 How to use Annotations with iBatis (myBatis) for an IN query?。
为了测试 MyBatis IN 查询,我们将之前的根据 ID 查询用户信息的接口进行修改,让它支持根据输入的 ID 列表查询多用户信息。
controller:
1 | "查询指定 ID 的用户", notes = "根据用户 id 列表查询其信息") (value = |
这里有个小注意点,@ApiImplicitParam
注解中的 paramType = "path"
记得修改为 path
,因为请求参数中包含路径变量了,否则渲染 URL 时,会出问题。
mapper 类:
1 | ({ |
说明:
item
标识集合中每一个元素进行迭代是的别名,很多教程中设置为item
,我这里改为id
也是 OK,而且也易于理解;index
指定一个名字,用于表示在迭代过程中,每次迭代到的位置,从 0 开始;open
表示该语句以什么开始;separator
表示在每次进行迭代之间以什么符号作为分隔符;close
表示以什么结束;collection
属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的;@Param
的设置比较关键,相当于给其修饰的参数指定一个别名:- 使用
@Param
,默认会和参数名同名,或者以注解传入的变量名为准。变量名将作为@Select
中的可用参数,比如,我这里这样定义@Param("ids2") List<String> ids
,那么,@Select
中可用参数名将是ids2
,collection
也须定义为ids2
,否则会报错:nested exception is org.apache.ibatis.binding.BindingException: Parameter 'list' not found. Available parameters are [ids2, param1]
; - 不使用
@Param
时,那么,此时collection
需要定义为list
,否则会报错:nested exception is org.apache.ibatis.binding.BindingException: Parameter 'list2' not found. Available parameters are [collection, list]
;
- 使用
上面的说明参考自 mybatis查询sql中in条件使用(foreach) ,没有找到官方文档支撑,待补充。
动态 SQL
待后续补充
- spring boot(8)-mybatis三种动态sql
- MyBatis注解应用之动态SQL语句
FAQ
MyBatis 中 # 和 $ 的区别
#{}
解析为一个 JDBC 预编译语句(Prepared Statement
)的参数标记符?
。#{}
是经过预编译的,是安全的。因为 SQL 语句已经预编译好了,传入参数的时候,不会重新生产 SQL 语句。${}
是非安全的,存在 SQL 注入风险。${}
将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。
例如:
1 | select * from emp where ename = '用户名' |
如果使用 ${}
,用户名被传入例如‘smith or 1 = 1’
,那无论 ename
是否匹配都能查到结果。
使用 ${}
的情况,order by
、like
语句只能用 ${}
,用 #{}
会多个 ' '
导致 SQL 语句失效。此外动态拼接 SQL,模糊查询时也要用 ${}
。
MyBatis 官网 —— Parameters 的例子值得阅读:
1 | "select * from user where id = #{id}") ( |
上面的写法,可以直接使用如下替换:
1 | "select * from user where ${column} = #{value}") ( |
关于这个问题的讨论:
- 知乎——mybatis中的#和$的区别 ?最好能说的稍微详细点 谢谢
- CSDN——mybatis中的#和$的区别
参考
- 官宣-MyBatis XML
- 纯洁的微笑-Spring Boot(六):如何优雅的使用 Mybatis 本文的主要参考文章之一,入门挺好
- CSDN-larger5-[增删改查] SpringBoot + MyBatis(注解版) 这位博主的示例,代码结构和风格都比较规范,值得学习
- CSDN-LuisChen的博客-Spring boot Mybatis 整合(完整版)
- 博客园-Ruthless-SpringBoot+Mybatis+Pagehelper分页 查询结果关系映射那块,该文章介绍的
@ResultMap
比较方便;
分页
- CSDN——分页查询效率为什么高? 关于分页的讨论
- CSDN-SpringBoot使用Mybatis注解开发教程-分页-动态sql 分页参考
- 博客园-朝雨忆轻尘-Spring Boot:实现MyBatis分页 推荐,
PageResult
的优化,参考此文 - PageHelper-官宣-如何使用分页插件
- 廖雪峰——分页查询
FAQ
- 知乎——这也许是你不曾留意过的 Mybatis 细节