Mybatis集成篇(一)

Spring 框架集成Mybatis

目前主流Spring框架体系中,可以集成很多第三方框架,方便开发者利用Spring框架机制使用第三方框架的功能。就例如本篇Spring集成Mybatis

简单集成案例:

Config配置:

@Configuration
@MapperScan(basePackages = {"com.czy.mapper"})
public class MybatisConfig {@Beanpublic SqlSessionFactory getSqlSessionFactory() throws Exception {org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();// 可以配置Mybatis相关配置configuration.setLogImpl(StdOutImpl.class);SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource());factoryBean.setConfiguration(configuration);return factoryBean.getObject();}private DataSource dataSource() {MysqlDataSource dataSource = new MysqlDataSource();dataSource.setUrl("jdbc:mysql://xxxx:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true");dataSource.setPassword("xxx");dataSource.setUser("xxx");return dataSource;}
}

Mapper文件:

public interface UserMapper {@Select("SELECT * FROM user WHERE id = #{id}")List<User> selectUser(int id);
}

主函数:

public class Main {public static void main(String[] args) throws Exception {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MybatisConfig.class);UserMapper userMapper = applicationContext.getBean(UserMapper.class);List<User> users = userMapper.selectUser(1);users.forEach(System.out::println);}
}

上面的案例是通过注解的方式进行Mybatis相关信息配置和相关Mapper使用。

原理解析

@MapperScan

在上面案例的配置文件中有个@MapperScan注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};Class<?>[] basePackageClasses() default {};Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;Class<? extends Annotation> annotationClass() default Annotation.class;Class<?> markerInterface() default Class.class;String sqlSessionTemplateRef() default "";String sqlSessionFactoryRef() default "";Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;String lazyInitialization() default "";String defaultScope() default "";boolean processPropertyPlaceHolders() default true;Filter[] excludeFilters() default {};
}

该注解中有个很重要的@Import注解;@Import是Spring框架中的一个重要特性,主要用于导入配置类或普通类,实现配置的模块化和动态配置的加载。通过使用@Import注解,可以将多个配置类组合在一起,便于管理和维护大型应用程序的配置。

也就是说MapperScan的相关处理是由MapperScannerRegistrar这个类进行逻辑处理

MapperScannerRegistrar 核心代码

@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {var mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));if (mapperScanAttrs != null) {registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,generateBaseBeanName(importingClassMetadata, 0));}}void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) {// MapperScannerRegistrar相关核心处理是由MapperScannerConfigurer处理var builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);builder.addPropertyValue("processPropertyPlaceHolders", annoAttrs.getBoolean("processPropertyPlaceHolders"));Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");if (!Annotation.class.equals(annotationClass)) {builder.addPropertyValue("annotationClass", annotationClass);}Class<?> markerInterface = annoAttrs.getClass("markerInterface");if (!Class.class.equals(markerInterface)) {builder.addPropertyValue("markerInterface", markerInterface);}Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");if (!BeanNameGenerator.class.equals(generatorClass)) {builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));}Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);}var sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");if (StringUtils.hasText(sqlSessionTemplateRef)) {builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));}var sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");if (StringUtils.hasText(sqlSessionFactoryRef)) {builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));}List<String> basePackages = new ArrayList<>(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));if (basePackages.isEmpty()) {basePackages.add(getDefaultBasePackage(annoMeta));}var excludeFilterArray = annoAttrs.getAnnotationArray("excludeFilters");if (excludeFilterArray.length > 0) {List<TypeFilter> typeFilters = new ArrayList<>();List<Map<String, String>> rawTypeFilters = new ArrayList<>();for (AnnotationAttributes excludeFilters : excludeFilterArray) {if (excludeFilters.getStringArray("pattern").length > 0) {// in oder to apply placeholder resolverrawTypeFilters.addAll(parseFiltersHasPatterns(excludeFilters));} else {typeFilters.addAll(typeFiltersFor(excludeFilters));}}builder.addPropertyValue("excludeFilters", typeFilters);builder.addPropertyValue("rawExcludeFilters", rawTypeFilters);}var lazyInitialization = annoAttrs.getString("lazyInitialization");if (StringUtils.hasText(lazyInitialization)) {builder.addPropertyValue("lazyInitialization", lazyInitialization);}var defaultScope = annoAttrs.getString("defaultScope");if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {builder.addPropertyValue("defaultScope", defaultScope);}builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));// for spring-nativebuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(beanName, builder.getBeanDefinition());}

MapperScannerConfigurer

核心方法

@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {processPropertyPlaceHolders();}// MapperScannerConfigurer后置处理则由ClassPathMapperScanner进行处理var scanner = new ClassPathMapperScanner(registry, getEnvironment());scanner.setAddToConfig(this.addToConfig);scanner.setAnnotationClass(this.annotationClass);scanner.setMarkerInterface(this.markerInterface);scanner.setExcludeFilters(this.excludeFilters = mergeExcludeFilters());scanner.setSqlSessionFactory(this.sqlSessionFactory);scanner.setSqlSessionTemplate(this.sqlSessionTemplate);scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);scanner.setResourceLoader(this.applicationContext);scanner.setBeanNameGenerator(this.nameGenerator);scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);if (StringUtils.hasText(lazyInitialization)) {scanner.setLazyInitialization(Boolean.parseBoolean(lazyInitialization));}if (StringUtils.hasText(defaultScope)) {scanner.setDefaultScope(defaultScope);}scanner.registerFilters();// 扫描basePackage相关的类型注入到Spring中scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));}

ClassPathMapperScanner

核心方法

public int scan(String... basePackages) {int beanCountAtScanStart = this.registry.getBeanDefinitionCount();this.doScan(basePackages);if (this.includeAnnotationConfig) {AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);}return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;}@Overridepublic Set<BeanDefinitionHolder> doScan(String... basePackages) {var beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {if (printWarnLogIfNotFoundMappers) {LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)+ "' package. Please check your configuration.");}} else {// 处理Spring BeanDefinitionprocessBeanDefinitions(beanDefinitions);}return beanDefinitions;}/***
** 这个方法主要是给Bean的属性赋能,会生成一个definition.setBeanClass(this.mapperFactoryBeanClass);
而private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
则得出一个结论:definition生成的Bean应该是MapperFactoryBean类
**
**/
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {AbstractBeanDefinition definition;var registry = getRegistry();for (BeanDefinitionHolder holder : beanDefinitions) {definition = (AbstractBeanDefinition) holder.getBeanDefinition();var scopedProxy = false;if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {definition = (AbstractBeanDefinition) Optional.ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));scopedProxy = true;}var beanClassName = definition.getBeanClassName();LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName+ "' mapperInterface");// the mapper interface is the original class of the bean// but, the actual class of the bean is MapperFactoryBeandefinition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59try {Class<?> beanClass = Resources.classForName(beanClassName);// Attribute for MockitoPostProcessor// https://github.com/mybatis/spring-boot-starter/issues/475definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClass);// for spring-nativedefinition.getPropertyValues().add("mapperInterface", beanClass);} catch (ClassNotFoundException ignore) {// ignore}//private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;// 定义了Bean的类型是MapperFactoryBean,也就相当xml配置class部分//<bean id="xxx" class="org.mybatis.spring.mapper.MapperFactoryBean">definition.setBeanClass(this.mapperFactoryBeanClass);// 设置MapperFactoryBean的相关属性definition.getPropertyValues().add("addToConfig", this.addToConfig);var explicitFactoryUsed = false;// 设置sqlSessionFactory属性值if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {definition.getPropertyValues().add("sqlSessionFactory",new RuntimeBeanReference(this.sqlSessionFactoryBeanName));explicitFactoryUsed = true;} else if (this.sqlSessionFactory != null) {definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);explicitFactoryUsed = true;}// 设置sqlSessionTemplate属性值if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {if (explicitFactoryUsed) {LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");}definition.getPropertyValues().add("sqlSessionTemplate",new RuntimeBeanReference(this.sqlSessionTemplateBeanName));explicitFactoryUsed = true;} else if (this.sqlSessionTemplate != null) {if (explicitFactoryUsed) {LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");}definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);explicitFactoryUsed = true;}if (!explicitFactoryUsed) {LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}definition.setLazyInit(lazyInitialization);if (scopedProxy) {continue;}if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {definition.setScope(defaultScope);}// 后续交由spring框架把bean注入spring上下文中if (!definition.isSingleton()) {var proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {registry.removeBeanDefinition(proxyHolder.getBeanName());}registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());}}}

从上面分析可以得到一个核心类MapperFactoryBean

MapperFactoryBean

核心方法

  @Overrideprotected void checkDaoConfig() {super.checkDaoConfig();notNull(this.mapperInterface, "Property 'mapperInterface' is required");var configuration = getSqlSession().getConfiguration();if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {try {// 在这里是我们属性的代码,Mybatis增加了mapperconfiguration.addMapper(this.mapperInterface);} catch (Exception e) {logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);throw new IllegalArgumentException(e);} finally {ErrorContext.instance().reset();}}}

有些人可能会很好奇为啥程序调用checkDaoConfig这个方法?
那我们先看这个类继承示意图:
在这里插入图片描述
一层一层父类找你会发现
DaoSupport类的afterPropertiesSet调用

public abstract class DaoSupport implements InitializingBean {protected final Log logger = LogFactory.getLog(this.getClass());public DaoSupport() {}public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {this.checkDaoConfig();try {this.initDao();} catch (Exception var2) {throw new BeanInitializationException("Initialization of DAO failed", var2);}}protected abstract void checkDaoConfig() throws IllegalArgumentException;protected void initDao() throws Exception {}
}

到这里本篇就完结了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/62348.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

路面交通工具和个数识别,支持YOLO,COCO,VOC三种格式,带标注可识别自行车,摩的,公共汽车,装载机,面包车,卡车,轿车等

预处理 自动定向&#xff1a; 已应用 调整大小&#xff1a; 拉伸至 640x640 增强 每个训练示例的输出&#xff1a; 3 翻转&#xff1a; 水平 自行车 公交车

长时间无事可做是个危险信号

小马加入的是技术开发部&#xff0c;专注于Java开发。团队里有一位姓隋的女同事&#xff0c;是唯一的web前端工程师&#xff0c;负责页面开发工作&#xff0c;比小马早两个月入职。公司的项目多以定制化OA系统为主&#xff0c;后端任务繁重&#xff0c;前端工作相对较少。在这样…

LABVIEW Modbus 串口

VISA写入&#xff1a; 写入缓冲区改为“十六进制显示”&#xff0c;并输入数据。 数据缓冲区显示&#xff1a;使用“十六进制”显示。

如何寻找适合的HTTP代理IP资源?

一、怎么找代理IP资源&#xff1f; 在选择代理IP资源的时候&#xff0c;很多小伙伴往往将可用率作为首要的参考指标。事实上&#xff0c;市面上的住宅IP或拨号VPS代理IP资源&#xff0c;其可用率普遍在95%以上&#xff0c;因此IP可用率并不是唯一的评判标准 其实更应该关注的…

FCBP 认证考试要点摘要

理论知识 数据处理与分析&#xff1a;包括数据的收集、清洗、转换、存储等基础操作&#xff0c;以及数据分析方法&#xff0c;如描述性统计分析、相关性分析、数据挖掘算法等的理解和应用 。数据可视化&#xff1a;涉及图表类型的选择与应用&#xff0c;如柱状图、折线图、饼图…

初识C++:指针与引用的异同,inline关键字

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 一.指针和引用的关系 1.1 概念 1.2 相似点 1.3 不同点 二.inline关键字 2.1 概念 2.2 工作原理 2.3 使用场景 2.4 注意事项 三.nullptr 3.1 引入背景 3.2 语义和类型 3.3 使用场景 兄弟们共勉 &#xff01;&…

DICOM医学影像应用篇——伪彩色映射 在DICOM医学影像中的应用详解

目录 引言 伪彩色映射的概念 基本原理 查找表&#xff08;Look-Up Table, LUT&#xff09; 步骤 示例映射方案 实现伪彩色映射的C代码 代码详解 伪彩色处理效果展示 总结 扩展知识 LUT 的基本概念 LUT 在伪彩色映射中的应用 示例 引言 在医学影像处理中&#xff0c…

韦东山stm32hal库--定时器喂狗模型按键消抖原理+实操详细步骤

一.定时器按键消抖的原理: 按键消抖的原因: 当我们按下按键的后, 端口从高电平变成低电平, 理想的情况是, 按下, 只发生一次中断, 中断程序只记录一个数据. 但是我们使用的是金属弹片, 实际的情况就是如上图所示, 可能会发生多次中断,难道我们要记录3/4次数据吗? 答:按键按下…

【Linux网络编程】第二弹---Socket编程入门指南:从IP、端口号到传输层协议及编程接口全解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、Socket 编程预备 1.1、理解源 IP 和目的 IP 1.2、认识端口号 1.2.1、端口号范围划分 1.2.2、理解 &q…

如何借助AI生成PPT,让创作轻松又高效

PPT是现代职场中不可或缺的表达工具&#xff0c;但同时也可能是令人抓狂的时间杀手。几页幻灯片的制作&#xff0c;常常需要花费数小时调整字体、配色与排版。AI的飞速发展为我们带来了革新——AI生成PPT的技术不仅让制作流程大大简化&#xff0c;还重新定义了效率与创意的关系…

基于时间维度优化“开源 AI 智能名片 S2B2C 商城小程序”运营策略:提升触达与转化效能

摘要&#xff1a; 随着数字化商业生态的蓬勃发展&#xff0c;“开源 AI 智能名片 S2B2C 商城小程序”融合前沿技术与创新商业模式&#xff0c;为企业营销与业务拓展带来新机遇。本文聚焦于用户时间场景维度&#xff0c;深入剖析如何依据不同时段用户行为特征&#xff0c;精准适…

【消息序列】详解(8):探秘物联网中设备广播服务

目录 一、概述 1.1. 定义与特点 1.2. 工作原理 1.3. 应用场景 1.4. 技术优势 二、截断寻呼&#xff08;Truncated Page&#xff09;流程 2.1. 截断寻呼的流程 2.2. 示例代码 2.3. 注意事项 三、无连接外围广播过程 3.1. 设备 A 启动无连接外围设备广播 3.2. 示例代…

vue3的prop

- 父组件需要传多个值给子组件 把值放对象&#xff0c;通过v-bind传整个对象 父组件 <script setup> import BlogPost from ./BlogPost.vue import { reactive } from vue; // 要传给子组件的所有值&#xff0c;用reactive包了该对象后&#xff0c;父组件的值变了&#…

Ubuntu下的Doxygen+VScode实现C/C++接口文档自动生成

Ubuntu下的DoxygenVScode实现C/C接口文档自动生成 1、 Doxygen简介 Doxygen 是一个由 C 编写的、开源的、跨平台的文档生成系统。最初主要用于生成 C 库的 API 文档&#xff0c;但目前又添加了对 C、C#、Java、Python、Fortran、PHP 等语言的支持。其从源代码中提取注释&…

uniapp强制修改radio-group内单选组件的状态方法

在uniapp开发中&#xff0c;需要在radio-group内部切换时做判断&#xff0c;提醒客户是否要变换radio的值&#xff0c;但是大家知道radio是单选组件&#xff0c;往往你点击后&#xff0c;是不能再修改状态的&#xff0c;就算你在点击后做判断&#xff0c;修改current的值&#…

数据结构-最短路径问题

一.问题分类 二.无权图单源最短路算法 dist[]数组记录的是个个顶点到源点的距离这个数组的下标表示顶点 源点到自己的距离是0,dist[s]0 path[]数组记录的是这个顶点的前驱&#xff0c;可以同过这个数组找到源点到个个顶点的距离 代码如下 void Unweighted(MGraph Graph, Ver…

Vue.js 实现用户注册功能

在本篇博客中&#xff0c;我们将通过一个简单的例子来展示如何使用 Vue.js 来实现一个用户注册功能。我们将创建一个包含用户名、邮箱和密码输入的表单&#xff0c;并在用户点击“创建账号”按钮时进行简单的验证。 完整代码 <!DOCTYPE html> <html lang"en&q…

【Java 学习】面向程序的三大特性:封装、继承、多态

引言 1. 封装1.1 什么是封装呢&#xff1f;1.2 访问限定符1.3 使用封装 2. 继承2.1 为什么要有继承&#xff1f;2.2 继承的概念2.3 继承的语法2.4 访问父类成员2.4.1 子类中访问父类成员的变量2.4.2 访问父类的成员方法 2.5 super关键字2.6 子类的构造方法 3. 多态3.1 多态的概…

impala入门与实践

1.impala基本介绍 impala是cloudera提供的一款高效率的sql查询工具&#xff0c;提供实时的查询效果&#xff0c;官方测试性能比hive快10到100倍&#xff0c;其sql查询比sparkSQL还要更加快速&#xff0c;号称是当前大数据领域最快的查询sql工具。impala是参照谷歌的新三篇论文…

结构方程模型(SEM)入门到精通:lavaan VS piecewiseSEM、全局估计/局域估计;潜变量分析、复合变量分析、贝叶斯SEM在生态学领域应用

目录 第一章 夯实基础 R/Rstudio简介及入门 第二章 结构方程模型&#xff08;SEM&#xff09;介绍 第三章 R语言SEM分析入门&#xff1a;lavaan VS piecewiseSEM 第四章 SEM全局估计&#xff08;lavaan&#xff09;在生态学领域高阶应用 第五章 SEM潜变量分析在生态学领域…