1. 为什么选择SpringBoot整合MyBatis XML方式在Java企业级开发中持久层框架的选择往往决定了项目的开发效率和运行性能。传统SSMSpringSpringMVCMyBatis架构虽然成熟稳定但需要配置大量的XML文件从web.xml到spring-mybatis.xml再到各种bean的配置一个中型项目动辄需要维护十几个配置文件。SpringBoot的出现彻底改变了这种局面。我清楚地记得2017年第一次使用SpringBoot整合MyBatis时的震撼——原来只需要一个starter依赖和几行配置就能完成过去需要数百行XML才能实现的功能。特别是对于习惯使用XML配置SQL的开发人员来说MyBatis的XML映射方式相比注解方式有着不可替代的优势SQL与代码分离复杂的多表关联查询在XML中可以保持清晰的结构避免了Java注解中SQL语句的混乱拼接动态SQL支持if、choose、foreach等标签在XML中能够更直观地表达条件逻辑易于维护DBA可以直接查看和优化SQL而不需要深入Java代码版本控制友好XML文件的diff比注解中的字符串变更更易于代码审查不过在实际项目中我发现很多团队对SpringBootMyBatis XML的整合只停留在能用层面没有充分发挥其优势。接下来我将通过一个电商用户模块的完整案例展示如何构建一个生产可用的整合方案。2. 环境准备与项目初始化2.1 创建项目时的关键选择使用Spring Initializr创建项目时我建议采用以下组合打包方式选择Maven而非Gradle。虽然Gradle构建速度更快但国内企业Maven仓库更稳定且MyBatis的XML文件在Maven标准目录结构下更易管理Java版本推荐JDK11LTS版本平衡了现代特性和稳定性依赖选择除了基础的Web和MyBatis依赖建议直接添加Lombok减少样板代码HikariCP高性能连接池Spring Boot Actuator监控!-- pom.xml关键依赖示例 -- dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.2.2/version /dependency dependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies2.2 配置文件的最佳实践我强烈推荐使用YAML代替properties文件它的层次结构更适合复杂配置。以下是一个生产级配置示例# application.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ecommerce?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai username: app_user password: ${DB_PASSWORD:defaultPassword} hikari: pool-name: HikariCP maximum-pool-size: 20 minimum-idle: 5 idle-timeout: 30000 max-lifetime: 1800000 mybatis: mapper-locations: classpath*:mapper/**/*.xml type-aliases-package: com.example.ecommerce.domain configuration: map-underscore-to-camel-case: true default-fetch-size: 100 default-statement-timeout: 30关键提示数据库密码应该使用环境变量注入${DB_PASSWORD}不要直接写在配置文件中。HikariCP的连接池参数需要根据实际负载调整上述配置适用于中小型应用。3. 领域模型与Mapper设计3.1 实体类设计技巧对于电商用户模型我通常会这样设计Data Builder NoArgsConstructor AllArgsConstructor public class User { private Long id; private String username; private String encryptedPassword; // 加密后的密码 private String email; private String phone; private UserStatus status; // 枚举类型 private LocalDateTime createTime; private LocalDateTime updateTime; // 嵌套的地址列表 private ListAddress addressList; } public enum UserStatus { ACTIVE, LOCKED, DELETED }使用Lombok可以减少getter/setter的样板代码Builder模式让对象创建更灵活。注意密码字段应该存储加密后的值而非明文。3.2 Mapper接口的设计原则UserMapper接口应该遵循以下规范Repository public interface UserMapper { // 使用Optional包装返回值 OptionalUser findById(Param(id) Long id); // 分页查询 ListUser findByCondition(Param(condition) UserQueryCondition condition, RowBounds rowBounds); // 批量插入 int batchInsert(Param(users) ListUser users); // 使用Param明确参数名 int updateStatus(Param(id) Long id, Param(status) UserStatus status); }特别说明返回Optional可以避免NPERowBounds实现内存分页大数据量时应改用物理分页Param注解在多个参数时必须添加否则MyBatis无法识别4. XML映射文件的进阶用法4.1 动态SQL实践下面是一个包含多种动态SQL技巧的示例!-- UserMapper.xml -- select idfindByCondition resultTypeUser SELECT * FROM t_user where if testcondition.username ! null and condition.username ! AND username LIKE CONCAT(%, #{condition.username}, %) /if if testcondition.status ! null AND status #{condition.status} /if choose when testcondition.createTimeStart ! null and condition.createTimeEnd ! null AND create_time BETWEEN #{condition.createTimeStart} AND #{condition.createTimeEnd} /when when testcondition.createTimeStart ! null AND create_time #{condition.createTimeStart} /when when testcondition.createTimeEnd ! null AND create_time lt; #{condition.createTimeEnd} /when /choose /where ORDER BY id DESC /select4.2 结果映射的高级技巧对于包含嵌套集合的复杂映射resultMap iduserWithAddresses typeUser id propertyid columnid/ result propertyusername columnusername/ !-- 其他字段映射... -- collection propertyaddressList ofTypeAddress id propertyid columnaddr_id/ result propertyprovince columnprovince/ result propertycity columncity/ !-- 更多地址字段... -- /collection /resultMap select idfindWithAddressesById resultMapuserWithAddresses SELECT u.*, a.id as addr_id, a.province, a.city FROM t_user u LEFT JOIN t_address a ON u.id a.user_id WHERE u.id #{id} /select5. 事务管理与性能优化5.1 声明式事务配置在SpringBoot中启用事务非常简单Configuration EnableTransactionManagement public class MyBatisConfig { Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } } Service RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserMapper userMapper; Transactional(rollbackFor Exception.class) Override public void updateUserWithAddress(User user, ListAddress addresses) { userMapper.update(user); addressMapper.deleteByUserId(user.getId()); addressMapper.batchInsert(addresses); } }关键点使用Transactional注解而非XML配置明确指定rollbackFor默认只回滚RuntimeException事务方法应该放在Service层而非Controller层5.2 二级缓存与批处理MyBatis二级缓存可以显著提升查询性能!-- 在mapper.xml中启用缓存 -- cache evictionLRU flushInterval60000 size512 readOnlytrue/ !-- 批处理示例 -- insert idbatchInsert useGeneratedKeystrue keyPropertyid INSERT INTO t_user (username, email) VALUES foreach collectionusers itemuser separator, (#{user.username}, #{user.email}) /foreach /insert缓存使用注意事项确保实体类实现Serializable接口更新操作多的表不适合开启缓存分布式环境需要改用Redis等集中式缓存6. 测试与问题排查6.1 单元测试方案使用SpringBootTest进行集成测试SpringBootTest Transactional class UserMapperTest { Autowired private UserMapper userMapper; Test void testFindById() { User user User.builder() .username(testUser) .email(testexample.com) .build(); userMapper.insert(user); OptionalUser found userMapper.findById(user.getId()); assertThat(found).isPresent(); assertThat(found.get().getUsername()).isEqualTo(testUser); } }6.2 常见问题排查Mapper找不到的问题检查MapperScan路径是否正确确认XML文件在resources/mapper目录下检查target目录下是否生成了对应的XML文件SQL语法错误使用MyBatis的日志级别为DEBUG在控制台检查最终执行的SQL事务不生效确保使用的是Transactional而非javax.transaction.Transactional检查方法是否是publicSpring AOP要求确认没有在同一个类中调用事务方法7. 生产环境建议经过多个项目的实践我总结了以下经验SQL管理为每个XML文件添加注释说明业务场景复杂SQL应该包含版本变更记录使用sql片段复用公共SQL部分监控指标通过Actuator暴露的/metrics端点监控mybatis.sql.executionSQL执行时间和次数hikaricp.connections连接池状态性能优化大数据量查询使用分页物理分页优于内存分页频繁查询但很少变更的数据使用二级缓存批量操作使用foreach标签代码生成考虑使用MyBatis Generator生成基础CRUD代码自定义模板生成符合项目规范的代码!-- MyBatis Generator配置示例 -- generatorConfiguration context idmysql targetRuntimeMyBatis3 plugin typeorg.mybatis.generator.plugins.SerializablePlugin/ commentGenerator property namesuppressAllComments valuefalse/ property namesuppressDate valuetrue/ /commentGenerator !-- 数据源配置... -- table tableNamet_user domainObjectNameUser generatedKey columnid sqlStatementMySql identitytrue/ /table /context /generatorConfiguration在大型项目中我会将MyBatis XML文件按业务模块分包管理每个模块有独立的mapper目录。同时建立统一的异常处理机制将MyBatis异常转换为业务异常。
企业数字化 ERP 产品动态
相关推荐
C++向量化编程:从SIMD原理到高性能计算实战优化 1. 项目概述:为什么向量化是C高性能计算的“核武器”?在C高性能计算(HPC)的圈子里混久了,你会发现一个现象:同样是用C写计算密集型任务,有的人写的代码跑起来像飞一样,而你的代码却像… · 2026/9/19 17:22:52
国产AI编程工具如何突破Copilot中文与企业级瓶颈 1. 项目概述:当国产AI编程工具开始“抄作业”并反向优化“GitHub Copilot不再是唯一”——这句话最近在开发者社区里刷屏,不是因为Copilot崩了,而是因为一批国产AI编程工具突然集体亮出实测数据:在中文注释理解、国内主流框架&… · 2026/9/15 23:45:51
C++实现仿Linux资源管理器:从文件系统操作到GUI开发实战 1. 项目概述:为什么用C写一个“仿Linux”资源管理器?最近在整理自己那台老旧的开发机时,Windows资源管理器又一次在打开包含数千个文件的目录时卡死闪退,这让我想起了多年前在Linux终端里用ls、find、mv命令行云流水操作文件的日子… · 2026/9/20 13:49:50
Win11直装ISE 14.7:跳过虚拟机,老FPGA工具链完美运行 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:55:49
SoC存储体系详解:从Cache到eFuse,嵌入式芯片存储选型与设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:55:43
QNX内存排查利器:pmap命令详解与实战技巧 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:55:43
Windows内核I2C驱动实战:从用户态到KMDF的完整通信链路 简介:Sensy 是一套面向嵌入式与 Windows 内核驱动学习者的教育性质源码项目,围绕 I2C 设备通信展开,从用户模式逐步深入到 KMDF 驱动开发,适合具备一定 C 基础、希望理解 Windows 驱动框架与 SPB 总线机制的开发者参考实践。资源包… · 2026/9/28 1:55:43
C#上位机集成Unet语义分割:ONNX模型GPU推理实战与踩坑 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:55:42
OpenCV预处理+CRNN识别:车牌识别毕设落地全链路 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 1:55:42
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
制作网页比较方便的软件怎么选?一文搞懂避坑指南 制作网页比较方便的软件怎么选?一文搞懂避坑指南 很多老板一上来就问:做个网站多少钱?但我反问他:你的域名买了吗?服务器租了吗?他一脸懵。这就是典型的“域名服务器搞不懂”。别急,今天咱们不聊虚的,直接 一文搞懂 那些让你头秃的技术名词。… · 2026/9/28 0:00:06
婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 找婚恋网站建站公司,最怕的就是被坑高价。很多同行跟我吐槽,报价单上写得模棱两可,功能栏里全是“高级定制”、“专属UI”,结果落地全是套壳。今天不聊虚的,直接甩几个我经手的 实战案例… · 2026/9/28 0:00:19
济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 上周济南一个做建材的老板找我,脸都绿了。他的官网首页弹出了赌博广告,后台被植入了挖矿脚本。他慌得问我:“网站被黑挂马不知道怎么办?能不能直接找之前的外包公司要源码下载,看看哪里被动了手脚?… · 2026/9/28 0:00:25