news 2026/9/11 18:26:43

3-8 spring boot如何集成mybatis?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
3-8 spring boot如何集成mybatis?

背景

小白:师傅,现在的java开发,统一的都是spring boot,使用原生mybatis或者spring+mybatis的都很少,能否讲讲sprng boot怎么集成mybaits的?集成的原理是什么?

扫地僧:得益于spring boot各种开发好的starters,spring boot集成mybatis相对容易。其中的原理则是利用Spring boot的自动注册原理,来集成SqlSessionFactory,最终在spring boot容器中生成各种mapper的bean。代码撸下,看看就知道了。

Spring Boot集成mybatis实例

准备工作

mysql数据库,本实例的版本为:8.0.16

mysql客户端SQLyog(免费,不需要注册码)或者navicat for mysql

创建数据库davidwang456和表

CREATEdatabase davidwang456;use davidwang456;DROPTABLEIFEXISTSstudent;CREATETABLE`student`(`id`int(11)NOTNULLAUTO_INCREMENT,`first_name`varchar(100)DEFAULTNULL,`last_name`varchar(100)DEFAULTNULL,`age`int(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;

mock数据

INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(1,'wang1','david1',21);INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(2,'wang2','david2',22);INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(3,'wang3','david3',23);INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(4,'wang4','david4',24);INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(5,'wang5','david5',25);INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(6,'wang6','david6',26);INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(7,'wang7','david7',27);INSERTINTO`student`(`id`,`first_name`,`last_name`,`age`)VALUES(8,'wang8','david8',28);

创建maven项目

添加依赖

pom.xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.davidwang456.integrate</groupId> <artifactId>SpringBootIntegrateMybatis</artifactId> <version>3.8.0-SNAPSHOT</version> <name>SpringBootIntegrateMybatis</name> <description>Spring Boot integrate mybatis</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> <scope>provided</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
实体

数据库实体

packagecom.davidwang456.mybatis.integrate;importjava.io.Serializable;importlombok.Data;@DatapublicclassStudentDTOimplementsSerializable{privatestaticfinallongserialVersionUID=1L;//字段privateIntegerid;privateStringfirstName;privateStringlastName;privateIntegerage;@OverridepublicStringtoString(){return"student [id="+id+", firstName="+firstName+", lastName="+lastName+", age="+age+']';}}

查询实体

packagecom.davidwang456.mybatis.integrate;importlombok.Data;@DatapublicclassStudentQueryDTO{//字段privateIntegerid;privateStringfirstName;privateStringlastName;privateIntegerage;//排序列privateStringsort;//排序 DESC|ASCprivateStringorderBy;}
配置

数据库配置

application.properties

mybatis.mapper-locations=classpath:mapper/*.xml mybatis.configuration.map-underscore-to-camel-case=true mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl spring.datasource.url=jdbc:mysql://localhost:3306/davidwang456?characterEncoding=UTF-8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=wangwei456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # Hikari will use the above plus the following to setup connection pooling spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.maximum-pool-size=15 spring.datasource.hikari.auto-commit=true spring.datasource.hikari.idle-timeout=30000 spring.datasource.hikari.pool-name=HikariCP spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.connection-timeout=30000

其中,配置mybatis.configuration.map-underscore-to-camel-case=true定义了支持驼峰形式,配置mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl定义了日志打印的类,类似与xml配置:

<settings><settingname="logImpl"value="STDOUT_LOGGING"/><settingname="mapUnderscoreToCamelCase"value="true"/></settings>

在src/main/resources目录下,定义**mapper.xml文件。

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.davidwang456.mybatis.integrate.StudentMapper"><selectid="getStudentInfoByCondition"parameterType="com.davidwang456.mybatis.integrate.StudentQueryDTO"resultType="com.davidwang456.mybatis.integrate.StudentDTO"useCache="false"><bindname="first"value="'%'+firstName+'%'"/><bindname="last"value="'%'+lastName+'%'"/>select id, first_name , last_name , age from student where 1=1<iftest="id!=null">and id=#{id}</if><iftest="firstName!=null and firstName!=''">and first_name like #{first}</if><iftest="lastName!=null and lastName!=''">and last_name like #{last}</if><iftest="age!=null and age!=0">and age=#{age}</if></select></mapper>

Mapper文件

packagecom.davidwang456.mybatis.integrate;importjava.util.List;importorg.apache.ibatis.annotations.Mapper;@MapperpublicinterfaceStudentMapper{publicList<StudentDTO>getStudentInfoByCondition(StudentQueryDTOstudentQueryDTO);}
测试程序
packagecom.davidwang456.mybatis.integrate;importjava.util.List;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.ResponseBody;@ControllerpublicclassTestController{@AutowiredprivateStudentMapperstudentMapper;@PostMapping("/list")@ResponseBodypublicList<StudentDTO>list(@RequestBodyStudentQueryDTOstudentQueryDTO){returnstudentMapper.getStudentInfoByCondition(studentQueryDTO);}}

启动spring主程序:

packagecom.davidwang456.mybatis.integrate;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublicclassSpringBootIntegrateMybatisApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SpringBootIntegrateMybatisApplication.class,args);}}

此时默认启动端口8080.使用postman进行测试:

curl--location--requestPOST'http://127.0.0.1:8080/list' \--header 'Content-Type:application/json' \--data-raw '{"id":null,"firstName":"wang","lastName":"david","age":null}'

返回结果符合预期:

[{"id":1,"firstName":"wang1","lastName":"david1","age":21},{"id":2,"firstName":"wang2","lastName":"david2","age":22},{"id":3,"firstName":"wang3","lastName":"david3","age":23},{"id":4,"firstName":"wang4","lastName":"david4","age":24},{"id":5,"firstName":"wang5","lastName":"david5","age":25},{"id":6,"firstName":"wang6","lastName":"david6","age":26},{"id":7,"firstName":"wang7","lastName":"david7","age":27},{"id":8,"firstName":"wang8","lastName":"david8","age":28}]

至此,spring boot集成mybatis就完成了。如果是老系统升级到spring boot,则可以利用配置属性mybatis.config-location来定义MyBatis.xml配置文件。其它mybatis的属性可自行参考。接下来我们看看spring boot集成mybatis的内部原理吧。

spring boot集成mybatis的内部原理

我们接下来说:springboot是如何和mybatis进行整合的

1.首先,springboot中使用mybatis需要用到mybatis-spring-boot-start,可以理解为mybatis开发的整合springboot的jar包

有一个关键点先说明:前面也提到过,不管是mybatis和spring整合,还是和springboot整合,都需要做两个操作:

1.把当前接口和对应的mapperProxyFactory存入到knownMappers中,

2.把sql包装成mappedStatement,存入到mappedStatements这个map中

1.springboot项目中,@SpringbootApplication注解上,有一个@EnableAutoConfiguration注解,而@EnableAutoConfiguration注解又利用了@Import注解,注入了一个ImportSelector的实现类 AutoConfigurationImportSelector.class

2.AutoConfigurationImportSelector在selectImports()方法中有一行重要的代码:

List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

这行代码内部,会从所有jar包中,META-INF/spring.factories文件中,加载EnableAutoConfiguration对应的实现类,那mybatis-spring-boot-autoconfigure.jar包中,配置了两个实现类,

1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ 3 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

3.我们来说 MybatisAutoConfiguration.class

1 @org.springframework.context.annotation.Configuration 2 @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) 3 @ConditionalOnSingleCandidate(DataSource.class) 4 @EnableConfigurationProperties(MybatisProperties.class) 5 @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) 6 public class MybatisAutoConfiguration implements InitializingBean { 7 8 9 }

这里有一个点,就是 @EnableConfigurationProperties(MybatisProperties.class);点开MybatisProperties.class文件会发现,这里面声明的就是,在application.properties配置文件中,mybatis提供的配置信息:比如mybatis.mapper-locations=classpath:mapping/*Mapper.xml;这个点,不细说了,后面可能会写一篇自定义starter的学习笔记,到时候 再详细写

4.在MybatisAutoConfiguration中有一个静态内部类 AutoConfiguredMapperScannerRegistrar 实现了ImportBeanDefinitionRegistrar;

所以,spring在refresh的时候,会执行这个类的 registerBeanDefinitions()方法,将 MapperScannerConfigurer存到了beanDefinitionMap中

1 @Override 2 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 3 4 5 if (!AutoConfigurationPackages.has(this.beanFactory)) { 6 logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); 7 return; 8 } 9 //中间删除了部分代码 10 registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); 11 }

5.MapperScannerConfigurer是BeanDefinitionRegistryPostProcessor的实现类,在refresh() --> invokeBeanFactoryPostProcessors(beanFactory)中,会遍历所有beanFactoryPostProcessor和BeanDefinitionRegistrtPostProcessor的实现类,依次执行postProcessorBeanDefinitionRegistrar()方法

MapperScannerConfigurer的postProcessBeanDefinitionRegistry()方法,会执行扫描方法,这里的扫描方法,和mapperScannerRegistrar的registerBeanDefinitions中的doScan是一样的,这里扫描的包是在,初始化MapperScannerConfigurer的时候,在执行完属性注入之后,调用了截图中的方法,把当前pvs中的basePackage传到MapperScannerConfigurer中,这里是如何传过去的,待研究

6.在将mapper扫描完之后,需要进行sql的解析,在和springboot整合之后,需要在配置文件中配置当前要扫描的mapper.xml文件,

mybatis.mapper-locations=classpath:mapping/*Mapper.xml

这里的mapperLocation,是在sqlSesionFactorybean中进行解析的,在第3步中的自动配置类中,通过@Bean,注入了SqlSessionFactory,

在sqlSessionFactory()方法最后,会调用factoryBean.getObject()方法,这里其实调用的就是SqlSessionFactory的getObject()方法,

1 @Override 2 public SqlSessionFactory getObject() throws Exception { 3 if (this.sqlSessionFactory == null) { 4 afterPropertiesSet(); 5 } 6 7 8 return this.sqlSessionFactory; 9 }

从afterPropertiesSet()方法,一直往下追,会追到同类中的buildSqlSessionFactory()方法,在这个方法中,判断如果当前mapperlocation不为null,就进行解析

1 if (this.mapperLocations != null) { 2 if (this.mapperLocations.length == 0) { 3 LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found."); 4 } else { 5 for (Resource mapperLocation : this.mapperLocations) { 6 if (mapperLocation == null) { 7 continue; 8 } 9 try { 10 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), 11 targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); 12 xmlMapperBuilder.parse(); 13 } catch (Exception e) { 14 throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); 15 } finally { 16 ErrorContext.instance().reset(); 17 } 18 LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'"); 19 } 20 } 21 }

在xmlMapperBuilder.parse()就是原生mybatis在解析xml文件时,需要调用的方法

7.在service中注入mapper接口,在初始化service,注入依赖的mapper接口时,还是调用的mapperFactorybean.getObject()方法来获取代理对象

springboot整合mybatis 和 spring+mybatis整合时,解析xml文件有一个区别:

spring-mybatis是利用mapperFactorybean的checkDao()方法来解析xml,put数据到mappedStatement和knowmappers

springboot是利用SqlSessionFactoryBean的getObject()来解析xml,put数据到mappedStatement和knowMappers

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 16:02:12

Continuous Thought Machine 源码级评审:连续思考机制与工程实践

Sakana AI 的 Continuous Thought Machine 最近在 Hacker News 上引发了源码级讨论。名字听起来很“学术”&#xff0c;但它并不是一篇停留在纸面的论文&#xff0c;而是一个可以打开仓库、逐行阅读、本地复现的真实项目。这次我们直接从源码层面切入&#xff0c;拆解它的核心设…

作者头像 李华
网站建设 2026/9/5 12:55:50

Agentic Commerce与Vibe Commerce:构建可审计可验证的购物Agent事件链

好的&#xff0c;这是一篇关于 Agentic Commerce World 的 CSDN 技术博客正文。 最近在技术圈里&#xff0c;一个叫 “Agentic Commerce” 的概念开始密集出现&#xff0c;旁边还经常跟着一个更有趣的词——“Vibe Commerce”。如果你第一反应是“这不就是让 AI 帮用户买东西嘛…

作者头像 李华
网站建设 2026/9/5 11:10:41

STM32MP157F-DK2 M4核烧录实战:从CubeProgrammer到remoteproc部署

很多人第一次拿到STM32MP157F-DK2&#xff0c;第一反应是先折腾A7双核&#xff0c;跑个Linux、点个屏幕、看看桌面。但真正把这块板子的价值发挥出来&#xff0c;绕不开那个藏在里面的Cortex-M4核。M4核负责实时控制、高速IO、低延迟中断&#xff0c;跟A7上跑Linux做业务是完全…

作者头像 李华
网站建设 2026/9/5 13:17:41

产品团队协作工具从0到1:线程化沟通与实时推送实现

做产品的人应该都有这种感觉&#xff1a;需求、评审、排期、上线反馈&#xff0c;散落在 IM 群聊、邮件、文档和会议纪要里。每次对齐都要翻聊天记录&#xff0c;一个话题聊完就没了上下文&#xff0c;新同学加入团队之后&#xff0c;根本不知道之前为什么做这个决定。如果有一…

作者头像 李华
网站建设 2026/9/5 22:47:17

工业自动化中的技术考古:CODESYS 2.3.9.47的寻获与实战应用

简介&#xff1a;本资源为工控领域经典开发工具 CODESYS 2.3.9.47 完整安装包&#xff0c;专为维护老旧PLC系统、适配嵌入式软PLC平台及开展工业自动化教学实验的工程师与技术人员提供。面对新版CODESYS V3.x难以兼容早期设备的现实困境&#xff0c;该版本仍广泛用于梯形图&…

作者头像 李华
网站建设 2026/9/2 21:49:05

世界模型到心智世界建模:从物理预测到人类意图理解

世界模型是最近 AI 技术社区里讨论热度非常高的话题。过去一段时间&#xff0c;大家比较熟悉的可能是视频生成、自动驾驶仿真、机器人操作这类方向。而近期牛津大学与新加坡国立大学&#xff08;NUS&#xff09;相关团队提出的“心智世界建模”&#xff08;Mental World Modeli…

作者头像 李华