ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Gradle集成MyBatis实战:依赖配置、代码生成与问题排查全解

Gradle集成MyBatis实战:依赖配置、代码生成与问题排查全解 Gradle与MyBatis集成这件事看起来简单实际一上手就会碰到一堆坑。依赖版本冲突、代码生成器配置繁琐、构建速度慢、Java版本与Gradle版本不匹配这些问题我基本都踩过一遍。这篇内容不打算讲什么高深理论就是把我在实际项目里验证过的一套配置方案、代码生成落地方式、以及排查问题的思路完整梳理出来给正在折腾这件事的朋友一份可以直接参考的实践记录。1. 整体设计思路为什么这么配1.1 先从构建工具说起Gradle在Java后端项目里已经是很主流的选择了尤其在Spring Boot生态里用Gradle管理依赖、处理多模块构建、跑代码生成任务都比Maven要灵活不少。但灵活的另一面是配置项多稍不注意就容易出问题。我见过太多项目卡在Gradle版本和项目需求对不上或者依赖解析慢到让人怀疑人生。拿我手头这个项目举例技术栈是Spring Boot 2.7 MyBatis MySQL构建工具选了Gradle 7.6。为什么要选这个组合Spring Boot 2.7对应Gradle 7.x比较稳妥Gradle 8.x虽然也能用但有些老插件不一定兼容尤其是MyBatis Generator这类不怎么频繁更新的插件。版本不是越新越好稳定才是第一位的。1.2 核心需求拆解这篇文章要解决的实质问题有三个第一Gradle依赖怎么配MyBatis相关组件之间不打架。第二MyBatis Generator代码生成怎么跑通不依赖IDE插件直接在命令行或构建过程中完成。第三集成过程中常见的问题有哪些怎么快速定位和解决。这三个问题分别对应依赖管理、代码生成、问题排查三条线也是很多人在项目初期耗费大量时间的痛点。2. 依赖配置的核心要点2.1 最小依赖集合MyBatis在Spring Boot项目里最基础的依赖其实就两个组合。mybatis-spring-boot-starter是首选它把MyBatis的核心库、Spring集成、自动配置都打包好了不需要自己去拼装mybatis和mybatis-spring。数据库驱动根据实际使用的数据库选MySQL就是com.mysql:mysql-connector-j。我这里用的是Groovy DSL的Gradle配置build.gradle关键部分如下dependencies { implementation org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1 runtimeOnly com.mysql:mysql-connector-j:8.0.33 }注意到几个细节数据库驱动用runtimeOnly就够了因为编译期不需要直接引用驱动类MySQL 8以上版本的驱动坐标已经从mysql-connector-java改成了mysql-connector-j这个细节容易踩坑尤其在看老教程的时候。2.2 分页插件和通用Mapper的取舍很多人会把PageHelper和tk.mybatis的通用Mapper一起加上我个人的建议是能少加就少加。PageHelper用起来方便但它会拦截所有查询偶尔会出现分页参数串了的情况。通用Mapper更不用说了理念很好但国内版本更新偏慢和Spring Boot 2.7以上版本配合时偶尔会有兼容问题。如果项目对分页和CRUD的简化确实有需求优先考虑MyBatis-Plus它的分页插件和条件构造器设计更贴近现代开发习惯。但这个要看团队的整体技术选型如果项目就是纯MyBatis路线只加一个starter就够了其他都是加分项不是必需品。2.3 排除冲突依赖的姿势实战中最常见的问题是javax和jakarta命名空间冲突尤其在Spring Boot 2.x和3.x混用的团队里。假如你引入了一个第三方库它内部传递依赖了Spring Boot 3的组件项目就会炸。这时候可以在Gradle里用全局排除策略configurations.all { exclude group: org.springframework.boot, module: spring-boot-starter-logging }但我提醒一下排除依赖是双刃剑。能精准定位再排除别一把梭。我见过有人为了修一个警告信息把日志依赖全排了结果项目启动后没有任何日志输出排查问题全靠猜。3. 代码生成器MyBatis Generator的落地3.1 为什么用XML配置MyBatis GeneratorMBG从1.4.0开始支持纯Java代码配置但实际项目里XML配置的使用量还是很大。原因很简单XML配置里可以直接看到数据库连接、表名、生成策略这些核心信息团队协作时容易过审也方便临时改参数。Java配置虽然类型安全但复杂参数的可读性并不好。MBG的版本也有讲究。老项目的1.3.x系列用org.mybatis.generator这个groupId下的mybatis-generator-core新项目直接用1.4.x。1.4.x在反注释、批量语句生成、Java8时间类型支持上都做了不少优化体验比1.3.x好很多。3.2 generatorConfig.xml配置详解我项目里的配置文件是这样的!DOCTYPE generatorConfiguration PUBLIC -//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd generatorConfiguration properties resourcegenerator.properties/ context idmysqlContext targetRuntimeMyBatis3Simple defaultModelTypeflat property namejavaFileEncoding valueUTF-8/ property nameuseMapperCommentGenerator valuetrue/ jdbcConnection driverClass${jdbc.driver} connectionURL${jdbc.url} userId${jdbc.username} password${jdbc.password}/ javaModelGenerator targetPackagecom.example.model targetProjectsrc/main/java/ sqlMapGenerator targetPackagemapper targetProjectsrc/main/resources/ javaClientGenerator targetPackagecom.example.mapper targetProjectsrc/main/java typeXMLMAPPER/ table tableNameuser domainObjectNameUser property nameuseActualColumnNames valuefalse/ generatedKey columnid sqlStatementJDBC/ /table /context /generatorConfiguration几个配置点我展开说一下。targetRuntimeMyBatis3Simple这是我最常用的设置。它生成的Mapper极其精简基础的单表CRUD都有但不会生成一大堆用不上的ByExample方法。如果你真的需要动态查询再改成MyBatis3否则Simple足够用了。defaultModelTypeflat意思是每个表只生成一个实体类不做主键类和BLOB类拆分。现在基本没人会去拆分这些了纯属增加管理成本。generatedKey加上之后生成器会在insert语句里自动添加useGeneratedKeystrue插入数据后实体的主键会回填这个对后续业务处理很重要。3.3 通过Gradle Task跑代码生成不依赖IDE插件的话最优雅的方式是写一个Gradle Task来触发MBG。这里我踩过一个坑MBG的运行时依赖要单独加在buildscript或者其他自定义配置里不能直接加到主dependencies块否则会把生成器代码打进业务包里。我推荐的方式是单独声明一个自定义配置configurations { mybatisGenerator } dependencies { mybatisGenerator org.mybatis.generator:mybatis-generator-core:1.4.2 mybatisGenerator com.mysql:mysql-connector-j:8.0.33 } task mbgGenerate { doLast { def configFile file(src/main/resources/generatorConfig.xml) def ant antBuilder() ant.taskdef( name: mbg, classname: org.mybatis.generator.ant.GeneratorAntTask, classpath: configurations.mybatisGenerator.asPath ) ant.mbg(configfile: configFile.path, overwrite: true) } }然后在命令行执行gradle mbgGenerate整个过程就通了。overwritetrue这个参数要注意生成器会直接覆盖已有的同名文件。如果团队里有人在实体类上手动加了字段跑一次生成就全没了。我在项目里的做法是overwritefalse生成失败的再人工确认安全第一。3.4 代码生成后的手动调整清单生成完之后我建议按这个顺序检查实体类是否有TableName注解如果用MyBatis-Plus的话Mapper接口是否加了Mapper注解或者在启动类上加了MapperScanXML文件里的resultMap是否符合实际字段映射主键回填有没有生效尤其是MapperScan很多项目跑起来提示Invalid bound statement (not found)一查就是Mapper接口没被Spring扫描到。这和MBG无关但和整个集成链路密切相关。4. 实际集成中的关键环节4.1 配置文件里的必备项代码生成完要确保application.yml里MyBatis的配置正确。我通常这样配mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.model configuration: map-underscore-to-camel-case: truemapper-locations指定XML映射文件的位置type-aliases-package让实体类可以用简单名字代替全限定名map-underscore-to-camel-case更是必备数据库的user_name字段就能自动映射到Java的userName属性不用手动写一大堆resultMap了。4.2 日志打印排查SQL的唯一手段MyBatis开发过程中SQL日志的打印极其重要。在application.yml里加上logging: level: com.example.mapper: debug注意这里的com.example.mapper要改成你Mapper接口所在的包路径。配置成debug级别后MyBatis会把执行的SQL、参数值、返回行数都打印出来配合mybatis-log-plugin之类的IDEA插件看动态SQL很方便。有些项目图省事直接把root日志级别调成debug结果日志量大到IDEA控制台直接卡死这个真不推荐。4.3 缓存机制先用好再说MyBatis有一级缓存和二级缓存这个话题面试被问烂了但实际项目里很多人根本不关心默认配置直接用。一级缓存是SqlSession级别的默认开启在同一个SqlSession里重复查询同一条数据会直接命中缓存。二级缓存是Mapper级别的默认关闭。我在项目中一般不开二级缓存原因很简单多表关联查询的缓存失效不好控制一旦脏数据被缓存了排查起来特别痛苦。如果某个查询真是热点我会用Redis做业务级缓存可控性更强。!-- 不推荐默认开启 -- cache evictionLRU flushInterval60000 size512 readOnlytrue/这段配置我见过很多项目都在用但说实话单机环境下开了二级缓存效果不大分布式环境下缓存一致性问题更突出。建议项目初期不开等真的遇到性能瓶颈再考虑。5. 常见问题与排查技巧实录5.1 Gradle下载依赖慢构建卡死这是Gradle使用中反馈最多的问题。中央仓库在海外依赖解析慢甚至超时。标准解法是换国内镜像源。GitHub上有不少Gradle镜像仓库的地址配置在repositories块里即可。repositories { maven { url https://maven.aliyun.com/repository/central } maven { url https://maven.aliyun.com/repository/public } maven { url https://mirrors.tencent.com/nexus/repository/maven-public/ } mavenCentral() }阿里云和腾讯云的Maven镜像都有比较高的可用性我实测下来阿里云的速度最快。注意要放在mavenCentral()前面这样Gradle会优先匹配镜像仓库。5.2 Java版本与Gradle版本不匹配Gradle对Java版本是有要求的。比如Gradle 7.6最高支持Java 19Gradle 8.8开始支持Java 21。如果本机是Java 21但项目用的Gradle版本是7.x构建时会直接提示Unsupported Java version 21.0.4这种问题的核心是统一版本管理。项目里加上.sdkmanrc或者.java-version文件把Java版本和Gradle版本都固定下来团队协作就不会因为各人本地环境不同而出现奇奇怪怪的问题。5.3 提示找不到Statement这个报错信息一般是Invalid bound statement (not added to mapper registry)。排查顺序如下Mapper接口是否存在且被Spring扫描到XML文件路径和mapper-locations是否匹配XML文件里的namespace是否和Mapper接口全限定名一致方法ID和Mapper接口方法名是否一致90%的情况都出在这四点上尤其是XML文件在src/main/java目录下但没有配置build.gradle里的resources识别Gradle默认不会把src/main/java下的XML打包到classpath里。5.4 MyBatis批量操作报错批量插入时很多人会在URL上配置allowMultiQueriestrue然后拼多条insert语句。这个方案有SQL注入风险也不利于SQL执行计划复用。更规范的做法是SqlSession sqlSession sqlSessionFactory.openSession(ExecutorType.BATCH); try { UserMapper mapper sqlSession.getMapper(UserMapper.class); for (User user : userList) { mapper.insert(user); } sqlSession.commit(); } finally { sqlSession.close(); }或者直接用MyBatis-Plus的saveBatch方法。批处理模式下MyBatis会复用PreparedStatement性能提升很可观。5.5 项目写好了第一次用Gradle构建却一直卡在下载这一条单独拿出来说因为真的太常见了。新项目或者重装系统的电脑上第一次gradle build时Gradle会先下载Gradle发行版本身然后又下载一堆依赖。如果发行版下载速度不行构建进度条就可能一直停留在下载状态。解决办法两个方向。一个是用Gradle Wrapper并修改gradle-wrapper.properties里的distributionUrl为国内镜像地址另一个是直接手动下载好离线包放到对应目录。都有不少博主分享过操作步骤核心就是别让发行版也走官方渠道速度差异不是一点半点。6. 实操心得与避坑经验这套方案我在多个Spring Boot项目中验证过稳定性和可维护性都过关。最后分享几点实在的体会依赖版本尽量统一管理。Spring Boot的BOM机制能帮我们锁定大部分依赖版本但MyBatis Generator这类独立工具不在BOM管理范围内要么单独建gradle.properties维护版本号要么用Gradle的Version Catalog把依赖和版本集中起来管理。项目一多集中管理的优势会非常明显。生成代码只当脚手架。MyBatis Generator生成的实体类和Mapper只是基础业务上复杂查询还是要手写XML或通过注解自定义SQL。不要迷信“一键生成全部搞定”生成完之后根据业务场景改造是常态。构建脚本一定要写注释。这种没什么技术含量但很重要。Gradle的Groovy DSL写起来简洁但项目交接到下一任手里时没有注释的build.gradle真的是灾难现场。特别是那些藏着特定版本锁定、排除项、自定义Task的配置没注释就等于没有维护性。每次跑完代码生成后立刻编译。这是我在一个项目后养成的习惯。生成器跑完先执行gradle compileJava验证代码能过编译再往下开发。如果等到写了很多业务代码才编译一旦发现生成结果有问题改起来就麻烦多了。实际开发中我推荐这套组合的最终形态是Gradle统一管理依赖和构建任务MyBatis Generator只负责生成基础CRUD代码XML文件放在resources目录下规范管理关键配置全部外显在配置文件中。这样既保留Gradle构建的灵活性又不会因为MyBatis配得不够精细而在运行期踩坑。
返回列表