Mybatis引出的一系列问题-spring多数据源配置

news2024/11/23 16:36:55

在日常开发中我们都是以单个数据库进行开发,在小型项目中是完全能够满足需求的。但是,当我们牵扯到像淘宝、京东这样的大型项目的时候,单个数据库就难以承受用户的CRUD操作。那么此时,我们就需要使用多个数据源进行读写分离的操作,这种方式也是目前一种流行的数据管理方式。

1 Spring Boot配置多数据源

在YAML文件中定义数据源所需的数据:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    default-datasource:
      username: root
      password: root
      url: jdbc:mysql://localhost:3306/default?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.jdbc.Driver
    inspur-zs-datasource:
      username: root
      password: root
      url: jdbc:mysql://localhost:3306/inspur-zs?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.jdbc.Driver

    druid:
      initial-size: 5
      min-idle: 1
      max-active: 20
  profiles:
    active: dev

mybatis:
  mapper-locations: classpath:/mapper/*.xml
  type-aliases-package: com.inspur.pojo
  configuration:
    mapUnderscoreToCamelCase: true
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

定义多个数据源:

package com.inspur.spring.config.datasource;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * 数据源配置
 * ConfigurationProperties注解用于将YAML中指定的数据创建成指定的对象,
 * 但是,YAML中的数据必须要与对象对象中的属性同名,不然无法由Spring Boot完成赋值。
 *
 * @author zhaoshuai-lc
 * @date 2023/07/11
 */
@Configuration
public class DataSourceConfig {

    @Bean(name = "defaultDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.default-datasource")
    public DataSource defaultDatasource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "inspurZsDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.inspur-zs-datasource")
    public DataSource inspurZsDatasource() {
        return DruidDataSourceBuilder.create().build();
    }
}

由于我们要定义多个数据源,所以在Spring Boot数据源自动配置类中就无法确定导入哪个数据源来完成初始化,所以我们就需要禁用掉Spring Boot的数据源自动配置类,然后使用我们自定义的数据源配置类来完成数据源的初始化与管理。

package com.inspur;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * WtMybatisStudyApplication
 * 由于我们要定义多个数据源,所以在Spring Boot数据源自动配置类中就无法确定导入哪个数据源来完成初始化,
 * 所以我们就需要禁用掉Spring Boot的数据源自动配置类,然后使用我们自定义的数据源配置类来完成数据源的初始化与管理。
 *
 * @author zhaoshuai-lc
 * @date 2023/07/11
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAspectJAutoProxy
public class WtMybatisStudyApplication {
    public static void main(String[] args) {
        SpringApplication.run(WtMybatisStudyApplication.class, args);
    }
}

1.1 指定数据源-实现DataSource接口

缺点:产生大量的代码冗余,在代码中存在硬编码。

package com.inspur.spring.config.datasource;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * 方式一
 * DynamicDataSource1
 * 实现DataSource接口我们本质上只使用了一个方法就是getConnection()这个无参的方法
 *
 * @author zhaoshuai-lc
 * @date 2023/07/11
 */
@Component
@Primary
public class DynamicDataSource1 implements DataSource {
    public static ThreadLocal<String> flag = new ThreadLocal<>();

    @Resource
    private DataSource defaultDatasource;

    @Resource
    private DataSource inspurZsDatasource;

    public DynamicDataSource1() {
        flag.set("defaultDatasource");
    }


    @Override
    public Connection getConnection() throws SQLException {
        if (flag.get().equals("defaultDatasource")) {
            return defaultDatasource.getConnection();
        } else if (flag.get().equals("inspurZsDatasource")) {
            return inspurZsDatasource.getConnection();
        }
        return defaultDatasource.getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

实现DataSource接口我们本质上只使用了一个方法,就是getConnection()这个无参的方法,但是DataSource接口中所有的方法我们也都需要实现,只是不用写方法体而已,也就是存在了很多的 “废方法” 。
@Primary注解 == @Order(1),用于设置此类的注入顺序。

使用:

    @Override
    public PageData<BsFactoryCalendar> selectByExample(BsFactoryCalendarExample example) {
        PageData<BsFactoryCalendar> pageData = new PageData<>();
		DynamicDataSource1.flag.set("default-datasource");
        List<BsFactoryCalendar> bsFactoryCalendars = bsFactoryCalendarMapper.selectByExample(example);
        PageInfo<BsFactoryCalendar> pageInfo = new PageInfo<>(bsFactoryCalendars);
        pageData.setRows(bsFactoryCalendars);
        pageData.setTotal(pageInfo.getTotal());
        return pageData;
    }

1.2 指定数据源-继承AbstrictRoutingDataSource类

减少了代码的冗余,但是还是会存在硬编码。

package com.inspur.spring.config.datasource;

import cn.hutool.core.map.MapUtil;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;

/**
 * DynamicDataSource2
 * AbstrictRoutingDataSource的本质就是利用一个Map将数据源存储起来,然后通过Key来得到Value来修改数据源。
 *
 * @author zhaoshuai-lc
 * @date 2023/07/11
 */

@Component
@Primary
public class DynamicDataSource2 extends AbstractRoutingDataSource {
    public static ThreadLocal<String> flag = new ThreadLocal<>();
    @Resource
    private DataSource defaultDatasource;

    @Resource
    private DataSource inspurZsDatasource;

    public DynamicDataSource2() {
        flag.set("defaultDatasource");
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return flag.get();
    }

    @Override
    public void afterPropertiesSet() {
        Map<Object, Object> targetDataSource = MapUtil.newConcurrentHashMap();
        // 将第一个数据源设置为默认的数据源
        super.setDefaultTargetDataSource(defaultDatasource);
        targetDataSource.put("defaultDatasource", defaultDatasource);
        targetDataSource.put("inspurZsDatasource", inspurZsDatasource);
        super.setTargetDataSources(targetDataSource);
        super.afterPropertiesSet();
    }
}

AbstrictRoutingDataSource的本质就是利用一个Map将数据源存储起来,然后通过Key来得到Value来修改数据源。

使用:

    @Override
    public PageData<BsFactoryCalendar> selectByExample(BsFactoryCalendarExample example) {
        PageData<BsFactoryCalendar> pageData = new PageData<>();
		DynamicDataSource2.flag.set("default-datasource");
        List<BsFactoryCalendar> bsFactoryCalendars = bsFactoryCalendarMapper.selectByExample(example);

        PageInfo<BsFactoryCalendar> pageInfo = new PageInfo<>(bsFactoryCalendars);
        pageData.setRows(bsFactoryCalendars);
        pageData.setTotal(pageInfo.getTotal());
        return pageData;
    }

1.3 指定数据源-使用Spring AOP + 自定义注解的形式

Spring AOP + 自定义注解的形式是一种推荐的写法,减少代码的冗余且不存在硬编码。此方法适合对指定功能操作指定数据库的模式。

导入依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

开启AOP支持:

package com.inspur;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableAspectJAutoProxy
public class WtMybatisStudyApplication {

    public static void main(String[] args) {
        SpringApplication.run(WtMybatisStudyApplication.class, args);
    }

}

定义枚举来表示数据源的标识:

package com.inspur.spring.config.datasource.enums;

public enum DataSourceType {
    DEFAULT_DATASOURCE,
    INSPURZS_DATASOURCE,
}

继承AbstractRoutingDataSource类:

package com.inspur.spring.config.datasource;

import cn.hutool.core.map.MapUtil;
import com.inspur.spring.config.datasource.enums.DataSourceType;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;

@Primary
@Component
public class DynamicDataSource3 extends AbstractRoutingDataSource {
    public static ThreadLocal<String> flag = new ThreadLocal<>();
    @Resource
    private DataSource defaultDatasource;

    @Resource
    private DataSource inspurZsDatasource;

    public DynamicDataSource3() {
        flag.set(DataSourceType.DEFAULT_DATASOURCE.name());
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return flag.get();
    }

    @Override
    public void afterPropertiesSet() {
        Map<Object, Object> targetDataSource = MapUtil.newConcurrentHashMap();
        // 将第一个数据源设置为默认的数据源
        super.setDefaultTargetDataSource(defaultDatasource);
        targetDataSource.put(DataSourceType.DEFAULT_DATASOURCE.name(), defaultDatasource);
        targetDataSource.put(DataSourceType.INSPURZS_DATASOURCE.name(), inspurZsDatasource);
        super.setTargetDataSources(targetDataSource);
        super.afterPropertiesSet();
    }
}

自定义注解:

package com.inspur.spring.config.datasource.annotation;

import com.inspur.spring.config.datasource.enums.DataSourceType;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    DataSourceType value() default DataSourceType.DEFAULT_DATASOURCE;
}

定义注解的实现类:

package com.inspur.spring.config.datasource.annotation;

import com.inspur.spring.config.datasource.DynamicDataSource3;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * TargetDataSourceAspect
 *
 * @author zhaoshuai-lc
 * @date 2023/07/11
 */
@Component
@Aspect
@Slf4j
public class TargetDataSourceAspect {
    @Before("@within(com.inspur.spring.config.datasource.annotation.TargetDataSource) || " +
            "@annotation(com.inspur.spring.config.datasource.annotation.TargetDataSource)")
    public void beforeNoticeUpdateDataSource(JoinPoint joinPoint) {
        TargetDataSource annotation = null;
        Class<? extends Object> target = joinPoint.getTarget().getClass();
        if (target.isAnnotationPresent(TargetDataSource.class)) {
            // 判断类上是否标注着注解
            annotation = target.getAnnotation(TargetDataSource.class);
            log.info("类: {}, 标注了注解@TargetDataSource", target);
        } else {
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            if (method.isAnnotationPresent(TargetDataSource.class)) {
                // 判断方法上是否标注着注解,如果类和方法上都没有标注,则报错
                annotation = method.getAnnotation(TargetDataSource.class);
                log.info("方法: {}, 标注了注解@TargetDataSource", method);
            } else {
                log.error("注解@TargetDataSource只能用于类或者方法上, error: {} {}", target, method);
                throw new RuntimeException("注解@TargetDataSource使用错误");
            }
        }
        // 切换数据源
        DynamicDataSource3.flag.set(annotation.value().name());
    }
}

使用:

package com.inspur.spring.service;

import com.github.pagehelper.PageInfo;
import com.inspur.spring.common.interfaceResult.PageData;
import com.inspur.spring.config.datasource.annotation.TargetDataSource;
import com.inspur.spring.config.datasource.enums.DataSourceType;
import com.inspur.spring.dao.BsFactoryCalendarMapper;
import com.inspur.spring.pojo.BsFactoryCalendar;
import com.inspur.spring.pojo.BsFactoryCalendarExample;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

@Service
@TargetDataSource(value = DataSourceType.DEFAULT_DATASOURCE) // 方式三 多数据源设置
public class BsFactoryCalendarServiceImpl implements BsFactoryCalendarService {

    @Resource
    private BsFactoryCalendarMapper bsFactoryCalendarMapper;

    @Override
    @Transactional
    public PageData<BsFactoryCalendar> selectByExample(BsFactoryCalendarExample example) {
        PageData<BsFactoryCalendar> pageData = new PageData<>();
        List<BsFactoryCalendar> bsFactoryCalendars = bsFactoryCalendarMapper.selectByExample(example);

        PageInfo<BsFactoryCalendar> pageInfo = new PageInfo<>(bsFactoryCalendars);
        pageData.setRows(bsFactoryCalendars);
        pageData.setTotal(pageInfo.getTotal());
        return pageData;
    }
}

1.4 通过SqlSessionFactory指定的数据源来操作指定目录的XML文件

使用此方法则不会与上面所述的类有任何关系,本方法会重新定义类。本方法也是一种推荐的方法,适用于对指定数据库的操作,也就是适合读写分离。不会存在代码冗余和存在硬编码。

在这里插入图片描述

配置YAML文件:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    default-datasource:
      username: root
      password: root
      jdbc-url: jdbc:mysql://localhost:3306/default?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.jdbc.Driver
    inspur-zs-datasource:
      username: root
      password: root
      jdbc-url: jdbc:mysql://localhost:3306/inspur-zs?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.jdbc.Driver
  main:
    allow-bean-definition-overriding : true
    
    druid:
      initial-size: 5
      min-idle: 1
      max-active: 20
  profiles:
    active: dev

mybatis:
  mapper-locations: classpath:/mapper/*.xml
  type-aliases-package: com.inspur.pojo
  configuration:
    mapUnderscoreToCamelCase: true
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

针对Mapper层通过SqlSessionFactory指定数据源来操作:

package com.inspur.spring.config.datasource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.inspur.spring.dao.defaultzs", sqlSessionFactoryRef = "DefaultSqlSessionFactory")
public class DefaultDatasourceConfig {

    @Primary
    @Bean(name = "DefaultDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.default-datasource")
    public DataSource getDateSource1() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "DefaultSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("DefaultDatasource") DataSource datasource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        // 设置mybatis的xml所在位置
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/defaultzs/*.xml"));
        return bean.getObject();
    }


    @Bean("DefaultSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("DefaultSqlSessionFactory") SqlSessionFactory factory) {
        return new SqlSessionTemplate(factory);
    }

    @Bean
    public PlatformTransactionManager transactionManager(@Qualifier("DefaultDatasource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

package com.inspur.spring.config.datasource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.inspur.spring.dao.inspurzs", sqlSessionFactoryRef = "InspurZsSqlSessionFactory")
public class InspurZsDatasourceConfig {

    @Primary
    @Bean(value = "InspurZsDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.inspur-zs-datasource")
    public DataSource getDateSource1() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(value = "InspurZsSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("InspurZsDatasource") DataSource datasource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        // 设置mybatis的xml所在位置
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/inspurzs/*.xml"));
        return bean.getObject();
    }


    @Bean(value = "InspurZsSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("InspurZsSqlSessionFactory") SqlSessionFactory factory) {
        return new SqlSessionTemplate(factory);
    }

    @Bean
    public PlatformTransactionManager transactionManager(@Qualifier("InspurZsDatasource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

@MapperScan注解中的basePackages指向的是指定的Dao层。
@MapperScan注解中sqlSessionFactoryRef 用来指定使用某个SqlSessionFactory来操作数据源。

 bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/inspurzs/*.xml"));

使用此种方法不会存在任何代码的冗余以及硬编码的存在,但是需要分层明确。唯一的不足就是添加一个数据源就需要重新写一个类,而这个类中的代码大部分又是相同的。

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

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

相关文章

在Linux服务器上搭建Git

环境 服务器&#xff1a;Ubuntu 客户端&#xff1a;Win11 1、在服务器上安装Git&#xff08;服务器中处理&#xff09; 在服务器上执行git --version 如果出现&#xff1a; 则&#xff0c;已经安装Git&#xff0c;跳过此步骤。 如果没有&#xff0c;则&#xff1a; 执行…

前端个人年度工作述职报告(二十篇)

前端个人年度工作述职报告篇1 尊敬的各位领导、各位同仁&#xff1a; 大家好!按照20__年度我公司就职人员工作评估的安排和要求&#xff0c;我认真剖析、总结了自己的工作情况&#xff0c;现将本人工作开展情况向各位领导、同仁做以汇报&#xff0c;有不妥之处&#xff0c;希…

ElasticSearch详细操作

ElasticSearch搜索引擎详细操作以及概念 文章目录 ElasticSearch搜索引擎详细操作以及概念 1、_cat节点操作1.1、GET/_cat/nodes&#xff1a;查看所有节点1.2、GET/_cat/health&#xff1a;查看es健康状况1.3_、_GET/_cat/master&#xff1a;查看主节点1.4、GET/_cat/indices&a…

内存快照:宕机后,Redis如何实现快速恢复?RDB

AOF的回顾 回顾Redis 的AOF的持久化机制。 Redis 避免数据丢失的 AOF 方法。这个方法的好处&#xff0c;是每次执行只需要记录操作命令&#xff0c;需要持久化的数据量不大。一般而言&#xff0c;只要你采用的不是 always 的持久化策略&#xff0c;就不会对性能造成太大影响。 …

OpenAI因担心隐私问题而阻止GPT-4图像功能的发展

据《纽约时报》报道&#xff0c;GPT-4的图像能力可以识别某些个人。 OpenAI一直在测试其支持图像识别的多模态GPT-4版本&#xff0c;以便计划中的广泛发布。然而&#xff0c;据周二《纽约时报》报道&#xff0c;出于对其可能识别特定个体的担忧&#xff0c;公众访问被限制了。…

(2023国赛必看)零基础挑战一周拿下数学建模国奖

1、 数学建模国赛介绍 1.1 数学建模国赛是什么&#xff1f;如何评奖 全国大学生数学建模竞赛是全国高校规模最大的课外科技活动之一。该竞赛每年9月&#xff08;一般在上旬某个周末的星期五至下周星期一共3天&#xff0c;72小时&#xff09;举行&#xff0c;竞赛面向全国大专院…

使用vscode远程登录以及本地使用的配置(插件推荐)

1、远程登陆ssh 1.1打开vscode插件商店&#xff0c;安装remote-ssh插件 远程ssh添加第三方插件&#xff1a;vscode下链接远程服务器安装插件失败、速度慢等解决方法_vscode远程安装不上扩展_Emphatic的博客-CSDN博客 转到定义&#xff0c;选中代码->鼠标右键->转到定义…

Linux:在使用UEFI固件的计算机上内核是如何被启动的

前言 启动计算机通常不是一件难事&#xff1a;按下电源键&#xff0c;稍等片刻&#xff0c;你就能看到一个登录界面&#xff0c;再输入正确的密码&#xff0c;就可以开启一天的网上冲浪之旅了。 但偶尔这件事没那么顺利&#xff0c;有时候迎接你的不是熟悉的登录界面&#xf…

SSM(Vue3+ElementPlus+Axios+SSM前后端分离)--功能实现[五]

文章目录 SSM--功能实现实现功能09-带条件查询分页显示列表需求分析/图解思路分析代码实现测试分页条件查询带条件分页查询显示效果 实现功能10-添加家居表单前端校验需求分析/图解思路分析代码实现完成测试测试页面效果 实现功能11-添加家居表单后端校验需求分析/图解思路分析…

【HTML】<input>

分类 text password number button reset submit hidden radio checkbox file image color range tel email&#xff08;火狐有校验&#xff0c;360浏览器无校验。&#xff09; url datetime&#xff08;火狐、360浏览器不支持&#xff09; search date、month、week、time、da…

计算机网络-三种交换方式

计算机网络-三种交换方式 电路交换(Circuit Switching) 电话交换机接通电话线的方式称为电路交换从通信资源分配的角度来看&#xff0c;交换(Switching)就是按照某种方式动态的分配传输线路的资源 电话交换机 为了解决电话之间通信两两之间连线过多&#xff0c;所以产生了电话…

【Docker】docker镜像+nginx部署vue项目:

文章目录 一、文档&#xff1a;二、打包vue项目&#xff1a;三、配置nginx&#xff1a;四、配置Dockerfile&#xff1a;五、构建镜像&#xff1a;六、运行容器&#xff1a;七、最终效果&#xff1a; 一、文档&#xff1a; 【1】菜鸟教程&#xff1a;https://www.runoob.com/do…

windows下以指定用户访问SMB服务器进行读写

一 概述 最近遇到一个问题&#xff0c;linux 的 smb服务器开启匿名访问&#xff0c;windows访问linux文件夹不需要用户名密码就可以进去使用&#xff0c;但是存在一个问题&#xff0c;ssh连接到linux 后修改的文件&#xff0c;在windows已smb方式下打开某个文件修改 是没有权限…

HTML5 Canvas和Svg:哪个简单且好用?

HTML5 Canvas 和 SVG 都是基于标准的 HTML5 技术&#xff0c;可用于创建令人惊叹的图形和视觉体验。 首先&#xff0c;让我们花几句话介绍HTML5 Canvas和SVG。 什么是Canvas? Canvas&#xff08;通过 标签使用&#xff09;是一个 HTML 元素&#xff0c;用于在用户计算机屏幕…

Vue3+SpringBoot快速开发模板

起因&#xff1a;个人开发过程经常会使用到Vue3SpringBoot技术栈来开发项目&#xff0c;每次在项目初始化时都需要涉及一些重复的整理工作&#xff0c;于是结合一些个人觉得不错的前后端模板进行整合&#xff0c;打通一些大多数项目都需要的实现的基础功能&#xff0c;以便于快…

探讨|使用或不使用机器学习

动动发财的小手&#xff0c;点个赞吧&#xff01; 机器学习擅长解决某些复杂问题&#xff0c;通常涉及特征和结果之间的困难关系&#xff0c;这些关系不能轻易地硬编码为启发式或 if-else 语句。然而&#xff0c;在决定 ML 是否是当前给定问题的良好解决方案时&#xff0c;有一…

opencv基础-38 形态学操作-闭运算(先膨胀,后腐蚀)cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

闭运算是先膨胀、后腐蚀的运算&#xff0c;它有助于关闭前景物体内部的小孔&#xff0c;或去除物体上的小黑点&#xff0c;还可以将不同的前景图像进行连接。 例如&#xff0c;在图 8-17 中&#xff0c;通过先膨胀后腐蚀的闭运算去除了原始图像内部的小孔&#xff08;内部闭合的…

PCIe枚举源码分析

枚举的过程也就是RC的系统软件通过配置空间访问来确定以及扫描整个总线拓扑的过程。 PCIe的拓扑结构如下&#xff1a; • Root Complex是树的根&#xff0c;它一般实现了一个主桥设备(host bridge), 一条内部PCIe总线(BUS 0)&#xff0c;以及通过若干个PCI bridge扩展出一些r…

性能测试监控指标及分析调优指南

目录 一、哪些因素会成为系统的瓶颈 二、哪些指标做为衡量系统的性能 三、性能测试注意的问题 四、定位性能问题的时候&#xff0c;可以使用自下而上的策略分析排查 五、优化性能问题的时候&#xff0c;可以使用自上而下的策略进行优化 一、哪些因素会成为系统的瓶颈 CPU&…

Vercel 部署的项目发现APIkeys过期了怎么办

好不容易部署的Vercel&#xff0c;发现APIkeys过期了显示&#xff0c;查了查资料发现只要更新下新的apikeys&#xff0c;然后再重新部署下就好了。 重新设置APIkeys 1.1. 进去 Vercel 项目内部控制台&#xff0c;点击顶部的 Settings 按钮&#xff1b; 1.2 点击环境变量Enviorn…