Mybatis集成篇(一)

news2024/11/29 23:25:23

Spring 框架集成Mybatis

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

简单集成案例:

Config配置:

@Configuration
@MapperScan(basePackages = {"com.czy.mapper"})
public class MybatisConfig {

    @Bean
    public 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 核心代码

@Override
  public 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 resolver
          rawTypeFilters.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-native
    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }

MapperScannerConfigurer

核心方法

@Override
  public 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;
    }

@Override
  public 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 BeanDefinition
      processBeanDefinitions(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 MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      try {
        Class<?> beanClass = Resources.classForName(beanClassName);
        // Attribute for MockitoPostProcessor
        // https://github.com/mybatis/spring-boot-starter/issues/475
        definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClass);
        // for spring-native
        definition.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

核心方法

  @Override
  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    var configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        // 在这里是我们属性的代码,Mybatis增加了mapper
        configuration.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.coloradmin.cn/o/2250066.html

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

相关文章

C51相关实验

C51相关实验 LED (P2 / 0~7)蜂鸣器 (P2^5)数码管 (P0 0~7 段 &#xff0c;P2 2~4 位)独立按键 &#xff08;P3^1 P3^0 P3^2 P3^3&#xff09;直流电机 (J47 5v 01~04)综合实验矩阵按键 (P1组 0~7)LED点阵 LED (P2 / 0~7) //功能&#xff1a;1.让开发板的LED全亮&#xff0c;2,…

C++语法·叭

阁下何不乘风起&#xff0c;扶摇直上九万里。 qi fei 目录 内存管理 分区介绍 1.栈区&#xff1a; 2.内存映射段&#xff1a; 3.堆&#xff1a; 4.数据段&#xff1a; 5.代码段&#xff1a; 补充&#xff1a; C内存管理&#xff08;简略回忆&#xff09; C内存…

数据库期末复习题库

1. Mysql日志功能有哪些? 记录日常操作和错误信息&#xff0c;以便了解Mysql数据库的运行情况&#xff0c;日常操作&#xff0c;错误信息和进行相关的优化。 2. 数据库有哪些备份方法 完全备份&#xff1a;全部都备份一遍表备份&#xff1a;只提取数据库中的数据&#xff0…

矩阵重新排列——rot90函数

通过 r o t 90 rot90 rot90函数可以将矩阵进行旋转 用法&#xff1a; r o t 90 ( a , k ) rot90(a,k) rot90(a,k)将矩阵 a a a按逆时针方向旋转 k 9 0 ∘ k\times90^\circ k90∘

挑战用React封装100个组件【001】

项目地址 https://github.com/hismeyy/react-component-100 组件描述 组件适用于需要展示图文信息的场景&#xff0c;比如产品介绍、用户卡片或任何带有标题、描述和可选图片的内容展示 样式展示 代码展示 InfoCard.tsx import ./InfoCard.cssinterface InfoCardProps {ti…

联通云服务器部署老项目tomcat记录

1.先在服务器上安装mysql和tomcat 2.tomcat修改端口 3.在联通云运控平台配置tomcat访问端口&#xff08;相当于向外部提供可访问端口&#xff09; 4.将tomcat项目放在服务器tomcat的webapps里面 5.在mysql里创建项目数据库&#xff0c;运行sql创建表和导入数据 6.在配置文…

Python 删除Word中的表格

在处理Word文档时&#xff0c;我们经常会遇到需要删除表格的情况。无论是为了简化文档结构&#xff0c;还是为了更新内容&#xff0c;删除表格都是一个常见的操作。但是通过手动删除不仅耗时&#xff0c;而且容易出错&#xff0c;本文将介绍如何使用Python通过编程删除Word中的…

讯飞语音转写WebApi 【JS语言】

讯飞语音转写 API 文档 文档地址&#xff1a;https://www.xfyun.cn/doc/asr/ifasr_new/API.html 看到没有 js 版本的 demo&#xff08;音频流模式&#xff09;&#xff0c;所以就搞了一个分享出来 在 React Native 运行环境下测试有效。 1、生成 signa import axios from a…

c++基础开发环境vscode+mingw-w64

c开发需要的基础有两个&#xff1a;编译环境&#xff0c;开发环境。 最简单的编译环境可以用gcc&#xff0c;cl&#xff0c;clongllvm; 开发环境最简单直接用文本编辑器就可以。 但是实际开发都会用ide来做&#xff0c;现代的ide即有开发环境可以写代码&#xff0c;自动补全&am…

DBA面试题-1

面临失业&#xff0c;整理一下面试题&#xff0c;找下家继续搬砖 主要参考&#xff1a;https://www.csdn.net/?spm1001.2101.3001.4476 略有修改 一、mysql有哪些数据类型 1&#xff0c; 整形 tinyint,smallint,medumint,int,bigint&#xff1b;分别占用1字节、2字节、3字节…

LSTM卫星轨道预测(一)

一.多文件预测 代码详细解析 1. 文件读取与数据处理 功能 从 .sp3 文件中读取卫星轨迹数据。提取包括 Satellite_ID, X, Y, Z 等字段的信息。计算派生特征&#xff08;如速度和加速度&#xff09;&#xff0c;便于后续建模使用。 主要函数&#xff1a;extract_sp3_data(fil…

如何通过智能生成PPT,让演示文稿更高效、更精彩?

在快节奏的工作和生活中&#xff0c;我们总是追求更高效、更精准的解决方案。而在准备演示文稿时&#xff0c;PPT的制作往往成为许多人头疼的问题。如何让这项工作变得轻松且富有创意&#xff1f;答案或许就在于“AI生成PPT”这一智能工具的广泛应用。我们就来聊聊如何通过这些…

格网法计算平面点云面积(matlab版本)

1、原理介绍 格网法计算平面点云面积&#xff0c;其思想类似高中油膜法计算面积。其将点云投影到水平面&#xff0c;再将点云划分成尺寸相同的格网。最后&#xff0c;统计格网内包含点的数量number&#xff0c;那么可利用如下公式计算得到点云的面积&#xff1a; Aeranumber*L…

无代码实现可视化GIS+模型+三维

现在的工具是越来越方便了&#xff0c;本来不是做前端的。可以节省很多的人力和时间&#xff0c;更快的搭建自己想要的可视化大屏&#xff0c;看例子 主要由三维的gis地图和模型加上二维的数据表格分析来实现这个可视化界面。 gis地图的设置 每一个gis都要设置世界远点&#x…

Jmeter中的监听器

3&#xff09;监听器 1--查看结果树 用途 调试测试计划&#xff1a;查看每个请求的详细信息&#xff0c;帮助调试和修正测试计划。分析响应数据&#xff1a;查看服务器返回的响应数据&#xff0c;验证请求是否成功。检查错误&#xff1a;识别和分析请求失败的原因。 配置步骤…

kafka进阶_3.消费消息

文章目录 一、消费消息概览1.1、消费示例代码1.2、消费过程 二、消费者组2.1、push & pull2.2、消费者组 三、调度器Coordinator四、消费者分配策略4.1、引言4.2、分配基本流程4.3、分配策略4.3.1、轮询分配策略4.3.2、轮询分配策略 五、消费偏移量5.1、起始偏移量5.2、指定…

用VC2019+MFC 创建一个DLL封装MD工业相机库然后用EXE调用这个相机库采图并且显示

主要描述&#xff1a; 用VC2019MFC 创建一个DLL封装MD工业相机库&#xff0c;再建一个EXE调用这个相机库采图并且显示。 先创建库工程&#xff1a; 新建一个库工程&#xff0c; 创建完成&#xff0c;添加一个DllFunction.h头文件&#xff0c;一个DllFunction.cpp 源文件 拷贝…

ODB 框架

目录 概述 基本工作原理 映射C对象到数据库表 从数据库中加载对象 持久化C对象到数据库 ODB常用接口 表创建预处理 #pragma db Object table 数据表属性 id auto column&#xff08;“xxx”&#xff09; type("xxx") unique index null default&…

ModuleNotFoundError: No module named ‘_ssl‘ centos中的Python报错

1、检查系统有没有openssl&#xff0c;有的话&#xff0c;就是python安装时没有指定openssl openssl version&#xff0c;有输出版本号就有&#xff0c;没有的话&#xff0c;需要手动安装 下载地址 参见https://www.openssl.org/&#xff0c;包括以下版本&#xff1a; https:/…

小程序-基于java+SpringBoot+Vue的微信小程序养老院系统设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…