MyBatis最佳实践
MyBatis是一款非常流行的ORM框架相信很多小伙伴都在使用。我们经常会把它和MyBatis-Plus或者MBG一起使用用多了之后对于其一些常规操作就不太熟悉了。最近总结了下MyBatis的实用用法和技巧希望对大家有所帮助MyBatis简介MyBatis是一款优秀的开源持久层框架支持自定义SQL查询、存储过程和高级映射目前在Github上已有17kStar。在MyBatis中我们可以在XML中编写SQL语句然后绑定到Java方法中通过参数和结果集的自动映射来实现复杂的查询逻辑。MyBatis消除了几乎所有JDBC操作和手动绑定参数操作使用起来非常方便在SpringBoot中集成下面我们来聊聊MyBatis在SpringBoot中的使用首先我们需要集成它。在pom.xml中添加MyBatis提供的Spring Boot Starterdependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.2.2/version /dependency然后在application.yml中配置好编写SQL实现的xml文件路径这里我们存放在resources/dao目录下mybatis: mapper-locations: - classpath:dao/*.xml然后添加Java配置通过MapperScan配置好Dao接口路径这样就可以开始使用了。/** * MyBatis配置类 * Created by macro on 2019/4/8. */ Configuration MapperScan(com.macro.mall.tiny.dao) public class MyBatisConfig { }基本使用下面我们来聊聊MyBatis的基本使用方法涵盖了基本的增删改查操作。表结构说明这里将以mall项目中权限管理模块相关表为例进行介绍具体表结构如下。项目结构说明本文示例使用了mall-learning项目中的mall-tiny-mybatis模块代码具体项目结构如下。select首先是查询操作这里我们以后台用户表ums_admin为例编写一个根据ID查询用户的方法先创建实体类UmsAdminpublic class UmsAdmin implements Serializable { private Long id; private String username; private String password; ApiModelProperty(value 头像) private String icon; ApiModelProperty(value 邮箱) private String email; ApiModelProperty(value 昵称) private String nickName; ApiModelProperty(value 备注信息) private String note; ApiModelProperty(value 创建时间) private Date createTime; ApiModelProperty(value 最后登录时间) private Date loginTime; ApiModelProperty(value 帐号启用状态0-禁用1-启用) private Integer status; }然后创建数据操作的接口UmsAdminDao再添加对应的方法/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 根据ID查询用户 */ UmsAdmin selectByIdSimple(Long id); }再创建xml文件UmsAdminDao.xml添加查询方法的SQL实现select idselectByIdSimple resultTypecom.macro.mall.tiny.model.UmsAdmin select * from ums_admin where id #{id} /select然后编写测试类注入Dao调用Dao方法来进行测试/** * MyBatis基本操作测试 * Created by macro on 2022/10/20. */ SpringBootTest public class MyBatisBaseTest { private static final Logger LOGGER LoggerFactory.getLogger(MyBatisBaseTest.class); Autowired private UmsAdminDao umsAdminDao; Test void testSelectByIdSimple(){ UmsAdmin umsAdmin umsAdminDao.selectByIdSimple(1L); LOGGER.info(testSelectByIdSimple result{},umsAdmin); } }此时你会发现对于一些数据库表中以下划线分割的返回字段无法自动映射可以通过对字段取别名的方式来进行映射select idselectById resultTypecom.macro.mall.tiny.model.UmsAdmin select username, password, icon, email, nick_name as nickName, note, create_time as createTime, login_time as loginTime, status from ums_admin where id #{id} /select如果你觉得这种方式比较麻烦也可以通过在application.yml中开启全局下划线自动转驼峰功能来解决个人习惯使用第一种。mybatis: configuration: # 下划线自动转驼峰 map-underscore-to-camel-case: trueinsert接下来我们来编写一个插入单个用户的方法/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 插入用户 */ int insert(UmsAdmin entity); }然后在xml中编写对应的SQL实现这里需要注意的是如果想返回插入后的自增ID的话需要使用selectKey标签进行配置。insert idinsert insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time) values (#{username}, #{password}, #{icon}, #{email}, #{nickName}, #{note}, #{createTime}, #{loginTime}) selectKey keyColumnid resultTypelong keyPropertyid orderAFTER SELECT LAST_INSERT_ID() /selectKey /insertupdate接下来我们来编写一个根据ID修改用户信息的方法/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 根据ID修改用户信息 */ int updateById(UmsAdmin entity); }然后在xml中编写对应的SQL实现。update idupdateById update ums_admin set username #{username}, password #{password}, icon #{icon}, email #{email}, nick_name #{nickName}, note #{note}, create_time #{createTime}, login_time #{loginTime} where id #{id} /updatedelete接下来我们来编写一个根据ID删除用户的方法/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 根据ID删除用户 */ int deleteById(Long id); }然后在xml中编写对应的SQL实现。delete iddeleteById delete from ums_admin where id #{id} /delete动态SQL通过MyBatis的动态SQL功能我们可以灵活地在xml中实现各种复杂的操作动态SQL功能需要依赖MyBatis的各种标签下面我们就来学习下。ifif标签可以实现判断逻辑这里我们以根据用户名和Email模糊查询用户为例来聊聊它的使用/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 根据用户名和Email模糊查询用户 * 不输入查询所有 */ ListUmsAdmin selectByUsernameAndEmailLike(Param(username) String username, Param(email) String email); }xml中添加对应的SQL实现如下。select idselectByUsernameAndEmailLike resultTypecom.macro.mall.tiny.model.UmsAdmin select username, password, icon, email, nick_name as nickName, note, create_time as createTime, login_time as loginTime, status from ums_admin where 11 if testusername!null and username! and username like concat(%,#{username},%) /if if testemail!null and email! and email like concat(%,#{email},%) /if /selectchoosechoose标签也可以实现判断逻辑上面的例子中当我们不输入用户名和Email时会查询出全部用户我们如果想不查询出用户可以使用它select idselectByUsernameAndEmailLike2 resultTypecom.macro.mall.tiny.model.UmsAdmin select username, password, icon, email, nick_name as nickName, note, create_time as createTime, login_time as loginTime, status from ums_admin where 11 choose when testusername!null and username! and username like concat(%,#{username},%) /when when testemail!null and email! and email like concat(%,#{email},%) /when otherwise and 12 /otherwise /choose /selectwhere上面的例子中我们为了SQL拼接不出错添加了where 11这样的语句其实可以使用where标签来实现查询条件当标签内没有内容时会自动去除where关键字同时还会去除开头多余的and关键字。select idselectByUsernameAndEmailLike3 resultTypecom.macro.mall.tiny.model.UmsAdmin select username, password, icon, email, nick_name as nickName, note, create_time as createTime, login_time as loginTime, status from ums_admin where if testusername!null and username! and username like concat(%,#{username},%) /if if testemail!null and email! and email like concat(%,#{email},%) /if /where /selectset当我们拼接更新字段的语句时也会面临同样的问题此时可以使用set标签来解决比如我们现在想写一个根据ID选择性修改用户信息的方法/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 根据ID选择性修改用户信息 */ int updateByIdSelective(UmsAdmin entity); }方法对应的SQL实现如下这里既避免了使用set关键字也会将多余的逗号去除。update idupdateByIdSelective update ums_admin set if testusername!null and username! username #{username}, /if if testpassword!null and password! password #{password}, /if if testicon!null and icon! icon #{icon}, /if if testemail!null and email! email #{email}, /if if testnickName!null and nickName! nick_name #{nickName}, /if if testnote!null and note! note #{note}, /if if testcreateTime!null create_time #{createTime}, /if if testloginTime!null login_time #{loginTime}, /if /set where id #{id} /updateforeach通过foreach我们可以实现一些循环拼接SQL的逻辑例如我们现在需要编写一个批量插入用户的方法/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 批量插入用户 */ int insertBatch(Param(entityList) ListUmsAdmin adminList); }在xml中的对应SQL实现如下在foreach标签中的内容会根据传入的集合参数进行循环拼接insert idinsertBatch insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time) values foreach collectionentityList separator, itemitem (#{item.username}, #{item.password}, #{item.icon}, #{item.email}, #{item.nickName}, #{item.note}, #{item.createTime}, #{item.loginTime}) /foreach /insert再例如我们现在需要编写一个根据用户ID批量查询的方法/** * 自定义UmsAdmin表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsAdminDao { /** * 根据用户ID批量查询 */ ListUmsAdmin selectByIds(Param(ids) ListLong ids); }在xml中的对应SQL实现如下我们可以使用open、close属性指定拼接语句的前后缀。select idselectByIds resultTypecom.macro.mall.tiny.model.UmsAdmin select username, password, icon, email, nick_name as nickName, note, create_time as createTime, login_time as loginTime, status from ums_admin where id in foreach collectionids itemitem open( close) separator, #{item} /foreach /select高级查询介绍完MyBatis的基本操作后我们再来介绍下MyBatis的高级查询功能。一对一映射在我们平时进行SQL查询时往往会有一对一的情况比如说我们这里有资源分类ums_resource_category和资源ums_resource两张表资源和分类就是一对一的关系如果你不想改动原实体类的话可以编写一个扩展类继承UmsResource并包含UmsResourceCategory属性/** * UmsResource扩展类 * Created by macro on 2022/10/20. */ Data public class UmsResourceExt extends UmsResource { private UmsResourceCategory category; }例如我们需要编写一个根据资源ID获取资源及分类信息的方法/** * 自定义UmsResource表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsResourceDao { /** * 根据资源ID获取资源及分类信息 */ UmsResourceExt selectResourceWithCategory(Long id); }在xml中的具体SQL实现如下我们可以通过给ums_resource_category表中字段取以category.xxx的别名来自动进行自动映射select idselectResourceWithCategory resultTypecom.macro.mall.tiny.domain.UmsResourceExt select ur.id, ur.create_time as createTime, ur.name, ur.url, ur.description, ur.category_id as categoryId, urc.id as category.id, urc.name as category.name, urc.sort as category.sort, urc.create_time as category.createTime from ums_resource ur left join ums_resource_category urc on ur.category_id urc.id where ur.id #{id} /select当然除了这种方式以外我们还可以通过ResultMapassociation标签来实现不过在此之前我们在编写xml文件的时候一般习惯于先给当前文件写一个BaseResultMap用于对当前表的字段和对象属性进行直接映射例如在UmsResourceCategoryDao.xml中这样实现?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.macro.mall.tiny.dao.UmsResourceCategoryDao resultMap idBaseResultMap typecom.macro.mall.tiny.model.UmsResourceCategory id propertyid columnid/ result propertycreateTime columncreate_time/ result propertyname columnname/ result propertysort columnsort/ /resultMap /mapper在UmsResourceDao.xml中我们可以这样实现?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.macro.mall.tiny.dao.UmsResourceDao resultMap idBaseResultMap typecom.macro.mall.tiny.model.UmsResource id propertyid columnid/ result propertycreateTime columncreate_time/ result propertyname columnname/ result propertyurl columnurl/ result propertydescription columndescription/ result propertycategoryId columncategory_id/ /resultMap /mapper编写完成后我们的一对一ResultMap实现就很简单了我们可以使用association标签进行一对一管理配置columnPrefix属性将匹配到的字段直接映射到关联对象中去resultMap idResourceWithCategoryMap typecom.macro.mall.tiny.domain.UmsResourceExt extendsBaseResultMap association propertycategory resultMapcom.macro.mall.tiny.dao.UmsResourceCategoryDao.BaseResultMap columnPrefixcategory_/ /resultMap然后再编写下Dao中方法对应SQL实现即可这里直接使用上面的ResultMap同时给ums_resource_category表中的字段指定了category_前缀以便于映射。select idselectResourceWithCategory2 resultMapResourceWithCategoryMap select ur.id, ur.create_time, ur.name, ur.url, ur.description, ur.category_id, urc.id as category_id, urc.name as category_name, urc.sort as category_sort, urc.create_time as category_create_time from ums_resource ur left join ums_resource_category urc on ur.category_id urc.id where ur.id #{id} /select一对多映射在编写SQL查询时一对多的情况也比较常见例如这里的分类和资源就是一对多的情况/** * UmsResourceCategory扩展类 * Created by macro on 2022/10/20. */ Data public class UmsResourceCategoryExt extends UmsResourceCategory { private ListUmsResource resourceList; }例如我们现在需要编写一个根据分类ID获取分类及对应资源的方法/** * 自定义UmsResourceCategory表查询 * Created by macro on 2022/10/20. */ Repository public interface UmsResourceCategoryDao { /** * 根据分类ID获取分类及对应资源 */ UmsResourceCategoryExt selectCategoryWithResource(Long id); }在实现具体SQL前我们需要先在xml中配置一个ResultMap通过collection标签建立一对多关系resultMap idselectCategoryWithResourceMap typecom.macro.mall.tiny.domain.UmsResourceCategoryExt extendsBaseResultMap collection propertyresourceList columnPrefixresource_ resultMapcom.macro.mall.tiny.dao.UmsResourceDao.BaseResultMap/ /resultMap然后在xml中编写具体的SQL实现使用该ResultMap。select idselectCategoryWithResource resultMapselectCategoryWithResourceMap select urc.id, urc.create_time, urc.name, urc.sort, ur.id resource_id, ur.create_time resource_create_time, ur.name resource_name, ur.url resource_url, ur.description resource_description, ur.category_id resource_category_id from ums_resource_category urc left join ums_resource ur on urc.id ur.category_id where urc.id #{id} /select分页插件我们平时实现查询逻辑时往往还会遇到分页查询的需求直接使用开源的PageHelper插件即可首先在pom.xml中添加它的Starter!--MyBatis分页插件-- dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version1.4.2/version /dependency然后在查询方法之前使用它的startPage方法传入分页参数即可分页后的得到的数据可以在PageInfo中获取到。/** * UmsResource的Service接口实现类 * Created by macro on 2022/10/20. */ Service public class UmsResourceServiceImpl implements UmsResourceService { Autowired private UmsResourceDao umsResourceDao; Override public PageInfoUmsResource page(Integer pageNum, Integer pageSize,Long categoryId) { PageHelper.startPage(pageNum,pageSize); ListUmsResource resourceList umsResourceDao.selectListByCategoryId(categoryId); PageInfoUmsResource pageInfo new PageInfo(resourceList); return pageInfo; } }总结本文主要介绍了MyBatis中一些比较常规的用法涵盖了SpringBoot集成、基本查询、动态SQL和高级查询建议大家收藏起来在对MyBatis的用法有所遗忘的时候拿出来看看。项目源码地址https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-mybatis

相关新闻

【快速检索 | Springer出版】第六届机械设计与仿真国际学术会议(MDS 2026)

【快速检索 | Springer出版】第六届机械设计与仿真国际学术会议(MDS 2026)

第六届机械设计与仿真国际学术会议(MDS 2026) 2026 6th International Conference on Mechanical Design and Simulation 2026年3月20-22日 匈牙利德布勒森 大会官网:www.icmds.net 截稿时间:见官网(早投稿&…

2026/5/17 3:41:46 阅读更多 →
【快速检索 | SPIE出版 | 连续4年稳定EI检索 | ISBNISSN双刊号 | 双一流高校主办】第五届光学与机器视觉国际学术会议(ICOMV 2026)

【快速检索 | SPIE出版 | 连续4年稳定EI检索 | ISBNISSN双刊号 | 双一流高校主办】第五届光学与机器视觉国际学术会议(ICOMV 2026)

第五届光学与机器视觉国际学术会议(ICOMV 2026) 2026 5th International Conference on Optics and Machine Vision 2026年4月10-12日 中国郑州 大会官网:http://www.icomv.com 截稿时间:见官网(早投稿,…

2026/7/3 0:55:13 阅读更多 →
AI Agent记忆系统完全指南(超详细)从入门到精通,一篇收藏就够了!

AI Agent记忆系统完全指南(超详细)从入门到精通,一篇收藏就够了!

《Memory in the Age of AI Agents》是由多所顶尖大学联合推出的系统性综述,创新提出"形态-功能-动力学"三维框架分析200最新论文。文章突破传统记忆二分法,提出Token-level/Parametric/Latent三大记忆形态,系统阐述记忆形成、演化…

2026/5/17 3:41:45 阅读更多 →

最新新闻

QooBot:全栈开源的仿生人操作系统——软硬一体,自由制造

QooBot:全栈开源的仿生人操作系统——软硬一体,自由制造

QooBot:全栈开源的仿生人操作系统——软硬一体,自由制造 摘要:QooBot 是一个面向仿生人的开源全栈生态,涵盖从机械图纸、电路设计到操作系统、AI 算法的完整技术栈。本文从架构全景、大脑核心、推理引擎、开发者生态等维度全面解读…

2026/7/6 2:53:55 阅读更多 →
可变级数LC无源自均压海量级联多电平拓扑机理研究——代替传统LCC/MMC的新一代特高压直流逆变架构

可变级数LC无源自均压海量级联多电平拓扑机理研究——代替传统LCC/MMC的新一代特高压直流逆变架构

可变级数LC无源自均压海量级联多电平拓扑机理研究——取代传统LCC/MMC的新一代特高压直流逆变架构 ----------作者:杨连江 摘要 针对我国特高压直流输电现有两大技术体系(LCC电网换相直流、MMC柔性直流)存在的底层机理缺陷,本文提…

2026/7/6 2:53:55 阅读更多 →
卡梅德生物技术快报| KM13 辅助噬菌体的天然 VHH 噬菌体文库全套构建流程与数据验证

卡梅德生物技术快报| KM13 辅助噬菌体的天然 VHH 噬菌体文库全套构建流程与数据验证

一、提出问题:实验室自建纳米抗体文库常遇四大工程化痛点 食品检测实验室自主构建 VHH 噬菌体文库时,普遍存在工程化落地难题:其一,普通单轮 PCR 扩增 VHH 基因存在大量缺失,文库多样性不足;其二&#xff…

2026/7/6 2:51:55 阅读更多 →
Variance Reduction with Baseline 补充 - 加基线使得方差降低

Variance Reduction with Baseline 补充 - 加基线使得方差降低

什么叫基线 基线就是一个只和当前状态s有关、和动作a无关的数值 b(s),用来做 “参考平均分”假设某状态s平均长期收益 b(s)10 某条轨迹 G_t18:A_t18-108>0,动作比平均更好,加大该动作概率 某条轨迹 G_t3:A_t3-10-7…

2026/7/6 2:51:55 阅读更多 →
MP1584 降压电源 PCB 布局 5 大要点:实测 SW 节点尖峰降低 60%

MP1584 降压电源 PCB 布局 5 大要点:实测 SW 节点尖峰降低 60%

MP1584降压电源PCB布局实战:5大核心技巧让SW节点尖峰直降60%作为一名长期奋战在电源设计一线的工程师,我深知PCB布局对开关电源性能的决定性影响。今天我们就以MP1584这款经典降压芯片为例,通过实测数据揭示那些手册上不会告诉你的布局奥秘。…

2026/7/6 2:49:55 阅读更多 →
非线性字符串数据结构串讲

非线性字符串数据结构串讲

书接去年,今天作业不想写了,滚过来写总结。顺便保留我刚略微学会的串串。 声明:作者由于水平不高,所以有些定理不能严谨证明,所以若是初学者请移步别处。 1.Trie树 定义 Trie树又叫字典树,是非常显然的…

2026/7/6 2:47:55 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻