MyBatis多数据源以及动态切换实现(基于SpringBoot 2.7.x)

MyBatis多数据源以及动态切换实现可以实现不同功能模块可以对应到不同的数据库,现在就让我们来讲解一下。

目录

  • 一、引入Maven
  • 二、配置文件
  • 三、实现多数据源
  • 四、动态切换数据源

一、引入Maven

注意:博主这边使用的springboot版本是2.7.14的

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
    <scope>provided</scope>
</dependency>

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

<!-- MyBatis依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

<!-- 数据库连接池依赖(这里以HikariCP为例) -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>

<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.12</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc -->
<!-- SQL Server驱动 -->
<dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>mssql-jdbc</artifactId>
    <version>8.2.0.jre8</version>
</dependency>

<!-- Spring Boot AOP依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二、配置文件

以application.properties讲解

server.port=8099

# MySQL1
spring.datasource.mysqldb1.url=jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.mysqldb1.username=root
spring.datasource.mysqldb1.password=123456
# MySQL2
spring.datasource.mysqldb2.url=jdbc:mysql://localhost:3306/ke_prd?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.mysqldb2.username=root
spring.datasource.mysqldb2.password=123456
# sqlserver
spring.datasource.sqlserver.url=jdbc:sqlserver://localhost:1433;database=TEST
spring.datasource.sqlserver.username=sa
spring.datasource.sqlserver.password=123

#mybatis
#pojo对应路径
mybatis.type-aliases-package=com.zhangximing.springbootmybatis_datasource.pojo
# 默认扫描xml
mybatis.mapper-locations=classpath:mybatis/mapper/**/*.xml
# sqlserver数据库对应扫描xml
custom.mapper.xml.sqlserver=classpath:mybatis/mapper/sqlserver/*.xml
# mysql数据库对应扫描xml
custom.mapper.xml.mysql=classpath:mybatis/mapper/mysql/*.xml
# 动态切换数据库对应扫描xml
custom.mapper.xml.dynamic=classpath:mybatis/mapper/dynamic/*.xml

# 编码utf-8
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

三、实现多数据源

先实现普通的mybatis写法,写三个(两个mysql,一个sqlserver)(冗余的代码不贴出)

接口入口写法都一样,就是名称换了而已,LogMapperMySql 换 LogMapperMySqlT和LogMapperSqlServer

import com.zhangximing.springbootmybatis_datasource.pojo.LogInfo;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @Author: zhangximing
 * @Email: 530659058@qq.com
 * @Date: 2023/6/14 13:54
 * @Description: 日志操作
 */
/*加了这个注解 就表示了 这是一个Mybatis的mapper类
就相当于之前使用的spring整合mybatis接口 也可以使用@MapperScan("com.kuang.mapper")*/
@Mapper
/**@Component 也可以用这个 万能的*/
@Repository
public interface LogMapperMySql {

    List<LogInfo> queryLogList();

    LogInfo queryLogById(int id);

    int addLog(LogInfo logInfo);

    int updateLog(LogInfo logInfo);

    int deleteLog(LogInfo logInfo);
}

xml文件方面,除了namespace需要修改外,当是sqlserver时NOW()函数要改为GETDATE()

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhangximing.springbootmybatis_datasource.mapper.mysql.LogMapperMySql">
    <select id="queryLogList" resultType="LogInfo">
        select * from log_center
    </select>

    <select id="queryLogById" resultType="LogInfo">
        select * from log_center
        where id=#{id};
    </select>

    <insert id="addLog" parameterType="LogInfo">
        insert into log_center(method,requestJson,responseJson,createDate)
        values(#{method},#{requestJson},#{responseJson},NOW());

    </insert>

    <update id="updateLog" parameterType="LogInfo">
        update mybatis.user
        set responseJson=#{responseJson},requestJson=#{requestJson}
        where id=#{id};
    </update>

    <delete id="deleteLog" parameterType="int">
        delete
        from log_center
        where id=#{id};
    </delete>

</mapper>

pojo实体 LogInfo

//import io.swagger.annotations.ApiModel;
//import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * @Author: zhangximing
 * @Email: 530659058@qq.com
 * @Date: 2023/5/31 13:07
 * @Description: 日志实体类
 */
@Data
//@ApiModel(value = "日志类")
public class LogInfo implements Serializable {

//    @ApiModelProperty(value = "",required = false)
    private String id;

//    @ApiModelProperty(value = "方法名",required = false)
    private String method;

//    @ApiModelProperty(value = "请求参数",required = false)
    private String requestJson;

//    @ApiModelProperty(value = "返回参数",required = false)
    private String responseJson;

//    @ApiModelProperty(value = "创建日期",required = false)
    private String createDate;
}

开始重点部分啦,如何实现多数据源啦
先写多数据源配置类

package com.zhangximing.springbootmybatis_datasource.config.datasource;

import org.springframework.beans.factory.annotation.Autowired;
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.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 多数据源配置类
 */
@Configuration
public class DataSourceConfig {

    @Autowired
    private Environment env;

    // 多数据源一:默认mysql
    // Primary注解是在没有指明使用哪个数据源的时候指定默认使用的主数据源
    // Primary在无动态切换的情况下可以用
    @Primary
    @Bean(name = "mysqlDataSource")
    //注意:该注解可能失效导致无法直接注入,所以手动获取装配
//    @ConfigurationProperties(prefix = "spring.datasource.mysqldb1")
    public DataSource mysqlDataSource() {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.url(env.getProperty("spring.datasource.mysqldb1.url"));
        dataSourceBuilder.username(env.getProperty("spring.datasource.mysqldb1.username"));
        dataSourceBuilder.password(env.getProperty("spring.datasource.mysqldb1.password"));
        return dataSourceBuilder.build();
//        return DataSourceBuilder.create().build();
    }

    // 多数据源二:mysql库2
    @Bean(name = "mysqlDataSourceT")
//    @ConfigurationProperties(prefix = "spring.datasource.mysqldb2")
    public DataSource mysqlDataSourceT() {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.url(env.getProperty("spring.datasource.mysqldb2.url"));
        dataSourceBuilder.username(env.getProperty("spring.datasource.mysqldb2.username"));
        dataSourceBuilder.password(env.getProperty("spring.datasource.mysqldb2.password"));
        return dataSourceBuilder.build();
    }

    // 多数据源三:sqlserver
    @Bean(name = "sqlserverDataSource")
//    @ConfigurationProperties(prefix = "spring.datasource.sqlserver")
    public DataSource sqlserverDataSource() {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.url(env.getProperty("spring.datasource.sqlserver.url"));
        dataSourceBuilder.username(env.getProperty("spring.datasource.sqlserver.username"));
        dataSourceBuilder.password(env.getProperty("spring.datasource.sqlserver.password"));
        return dataSourceBuilder.build();
    }

针对数据源配置(mysql)

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.beans.factory.annotation.Value;
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 javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
@MapperScan(
		// 指定该数据源扫描指定包下面的Mapper接口文件
        basePackages = "com.zhangximing.springbootmybatis_datasource.mapper.mysql",
        sqlSessionFactoryRef = "sqlSessionFactoryPrimary",
        sqlSessionTemplateRef = "sqlSessionTemplatePrimary")
public class DataSourceMySqlConfig {

    // mapper扫描xml文件的路径
    @Value("${custom.mapper.xml.mysql}")
    private String MAPPER_LOCATION;
	
    private DataSource primaryDataSource;

    // 通过构造方法进行注入
    public DataSourceMySqlConfig(@Qualifier("mysqlDataSource") DataSource primaryDataSource) {
        this.primaryDataSource = primaryDataSource;
    }

    /**
     * SqlSession工厂方法
     * 用于创建SqlSession对象。
     * 用于管理SqlSession对象的的生命周期。
     * 可以根据需要创建不同的SqlSession子类,以支持不同的数据库操作需求。
     * 可以将SqlSession对象存储在持久化缓存中,以提高性能。
     * @return
     * @throws Exception
     */
    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactoryPrimary() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(primaryDataSource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
        sqlSessionFactoryBean.setTypeAliasesPackage("com.zhangximing.springbootmybatis_datasource.pojo");
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    @Primary
    public SqlSessionTemplate sqlSessionTemplatePrimary() throws Exception {
        return new SqlSessionTemplate(sqlSessionFactoryPrimary());
    }
}

针对数据源配置(mysql库2)(sqlserver的参考这个写即可,扫描路径以及配置文件获取修改)

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * sqlserver数据源配置
 */
@Configuration
@MapperScan(
        basePackages = "com.zhangximing.springbootmybatis_datasource.mapper.mysql",
        sqlSessionFactoryRef = "sqlSessionFactorySecondaryMySql")
public class DataSourceMySqlTConfig {

    // mapper扫描xml文件的路径
    @Value("${custom.mapper.xml.mysql}")
    private String MAPPER_LOCATION;

    private DataSource secondaryDataSource;

	// 通过构造方法进行注入
    public DataSourceMySqlTConfig(@Qualifier("mysqlDataSourceT") DataSource secondaryDataSource) {
        this.secondaryDataSource = secondaryDataSource;
    }

    @Bean("sqlSessionFactorySecondaryMySql")
    public SqlSessionFactory sqlSessionFactorySecondary() throws Exception {

        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();

        // 指定数据源
        sqlSessionFactoryBean.setDataSource(secondaryDataSource);
        /*
			获取xml文件资源对象
			当Mapper接口所对应的.xml文件与Mapper接口文件分离,存储在 resources
			文件夹下的时候,需要手动指定.xml文件所在的路径
		*/
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
        //指定实体目录
        sqlSessionFactoryBean.setTypeAliasesPackage("com.zhangximing.springbootmybatis_datasource.pojo");
        return sqlSessionFactoryBean.getObject();
    }

    @Bean("SecondaryDataSourceManagerMySql")
    public DataSourceTransactionManager SecondaryDataSourceManager() {
        return new DataSourceTransactionManager(secondaryDataSource);
    }
}

到这里基本上就可以了,写测试类测试

import com.alibaba.fastjson.JSONObject;
//import io.swagger.annotations.Api;
//import io.swagger.annotations.ApiOperation;
import com.zhangximing.springbootmybatis_datasource.annotation.DataBase;
import com.zhangximing.springbootmybatis_datasource.mapper.mysql.LogMapperMySql;
import com.zhangximing.springbootmybatis_datasource.mapper.mysql.LogMapperMySqlT;
import com.zhangximing.springbootmybatis_datasource.mapper.sqlserver.LogMapperSqlServer;
import com.zhangximing.springbootmybatis_datasource.pojo.LogInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @Author: zhangximing
 * @Email: 530659058@qq.com
 * @Date: 2023/5/31 13:00
 * @Description: 测试接口
 */
//@Api(value="日志模块",tags = "日志模块管理")
@RestController
@RequestMapping("/log")
public class LogController {

    @Autowired
    private LogMapperMySql logMapper_mysql;

    @Autowired
    private LogMapperMySqlT logMapper_mysqlT;

    @Autowired
    private LogMapperSqlServer logMapper_sqlserver;

//    @ApiOperation(value="插入日志")
    @PostMapping(value = "/addLog", produces = {"text/plain;charset=UTF-8"})
    public String addLog(@RequestBody LogInfo logInfo){

        int cnt = logMapper_sqlserver.addLog(logInfo);

        JSONObject result = new JSONObject();
        result.put("count",cnt);

        return result.toJSONString();
    }

    @RequestMapping("/getLogById/{id}")
    public String getLogById(@PathVariable("id") int logId){

        System.out.println("logId:"+logId);
        LogInfo logInfo = logMapper_sqlserver.queryLogById(logId);

        JSONObject result = new JSONObject();
        result.put("data",JSONObject.toJSONString(logInfo));

        return result.toJSONString();
    }
}

测试结果就不展示了,就手动修改需要调用的数据源进行请求测试

四、动态切换数据源

在第三点的基础上,我们在 多数据源配置类 增加 如下代码

/**
 *  动态数据源配置
 */
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource(){
    DynamicDataSource dynamicDataSource = new DynamicDataSource();
    //默认数据源
    dynamicDataSource.setDefaultTargetDataSource(mysqlDataSource());

    //配置多数据源
    Map<Object, Object> dsMap = new HashMap<Object, Object>(5);
    dsMap.put("mysqldb1", mysqlDataSource());
    dsMap.put("mysqldb2", mysqlDataSourceT());
    dsMap.put("sqlserver", sqlserverDataSource());
    dynamicDataSource.setTargetDataSources(dsMap);
    return dynamicDataSource;
}

/**
 * 配置@Transactional注解事物(管理事务)
 *  @return
 */
@Bean
public PlatformTransactionManager transactionManager(){
    return new DataSourceTransactionManager(dynamicDataSource());
}

DynamicDataSource(动态数据源配置类)

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源配置
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    // 确定当前的数据源键
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDB();
    }
}

DataSourceContextHolder(切换数据库类)

import lombok.extern.slf4j.Slf4j;

/**
 * 上下文切换(切换数据库)
 */
@Slf4j
public class DataSourceContextHolder {
    // 默认数据源
    public static final String DEFAULT_DS = "mysqldb1";

    // 本地线程共享变量
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDB(String dbType) {
        log.info("切换至" + dbType + "数据源");
        contextHolder.set(dbType);
    }

    public static String getDB() {
        return (contextHolder.get());
    }

    public static void clearDB() {
        contextHolder.remove();
    }
}

增加与mysql配置mybatis一致的xml和接口(由于我们只用一个表测试,所以就以同类型数据库切换进行演示),此处省略代码…

自定义注解(这边打算用注解标记哪些接口需要进行切换数据库的)

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

/**
 * 自定义注解:参数为数据库标识
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataBase {
    String value() default "mysqldb1";
}

切面部分,这里测试是以请求头传数据库标识来动态实现,也可以通过数据库或配置文件存储多种方式

import com.zhangximing.springbootmybatis_datasource.annotation.DataBase;
import com.zhangximing.springbootmybatis_datasource.config.datasource.DataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 *  切面拦截切换数据库
 */
@Aspect
@Component
public class DataBaseAspect {

    // 拦截用过注释的
    @Pointcut("@annotation(com.zhangximing.springbootmybatis_datasource.annotation.DataBase)")
    public void dbPointCut(){

    }

    // 拦截切换数据源
    @Before("dbPointCut()")
    public void beforeSwitchDS(JoinPoint point){
        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();
            //获得访问的方法名
            String methodName = point.getSignature().getName();
            //得到方法的参数的类型
            Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();

            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //获取请求头数据库(模拟动态切换数据库)
            String myDataSource = request.getHeader("dataSource");

            // 获取默认数据源
            String dataSource = DataSourceContextHolder.DEFAULT_DS;
            try {
                // 得到访问的方法对象
                Method method = className.getMethod(methodName, argClass);

                // 判断是否存在@DateBase注解
                if (method.isAnnotationPresent(DataBase.class)) {
                    DataBase annotation = method.getAnnotation(DataBase.class);
                    // 取出注解中的数据源名
                    dataSource = annotation.value();
                }

                if (null != myDataSource && myDataSource.length() > 0){
                    dataSource = myDataSource;
                }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 切换数据源
        DataSourceContextHolder.setDB(dataSource);
    }

    // 结束后清除线程共享变量
    @After("dbPointCut()")
    public void afterSwitchDS(JoinPoint point){
        DataSourceContextHolder.clearDB();
    }
}

测试类增加如下代码

//置空根据请求头dataSource动态切库
 @DataBase()
 @PostMapping(value = "/addLogDy", produces = {"text/plain;charset=UTF-8"})
 public String addLogDy(@RequestBody LogInfo logInfo){

     int cnt = logMapperDynamic.addLog(logInfo);

     JSONObject result = new JSONObject();
     result.put("count",cnt);

     return result.toJSONString();
 }

 //可注解指定数据库
 @DataBase("mysqldb2")
 @RequestMapping("/getLogByIdDy/{id}")
 public String getLogByIdDy(@PathVariable("id") int logId){

     System.out.println("logId:"+logId);
     LogInfo logInfo = logMapperDynamic.queryLogById(logId);

     JSONObject result = new JSONObject();
     result.put("data",JSONObject.toJSONString(logInfo));

     return result.toJSONString();
 }
--附上表结构创建(mysql)
CREATE TABLE IF NOT EXISTS `log_center`(
   `id` INT UNSIGNED AUTO_INCREMENT,
   `method` VARCHAR(200),
   `requestJson` TEXT,
	 `responseJson` TEXT,
   `createDate` DATE,
   PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

整体代码结构
在这里插入图片描述

功能测试正常,大家可以自行试下,代码完整包已放到博主资源(SpringBoot-MyBatis-DataSource(多数据源以及动态切换))。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

thinkphp6入门(19)-- 中间件向控制器传参

可以通过给请求对象赋值的方式传参给控制器&#xff08;或者其它地方&#xff09;&#xff0c;例如 <?phpnamespace app\middleware;class Hello {public function handle($request, \Closure $next){$request->hello ThinkPHP;return $next($request);} } 然后在控制…

类图(Class diagram)

类图主要是用来展现软件系统中的类、接口以及它们之间的静态结构 。 一、元素 1、类 ​ 从上到下分为三部分&#xff0c;分别是类名、属性和操作。 类名是必须有的。​类如果有属性&#xff0c;则每一个属性必须有一个名字&#xff0c;另外还可以有其他的描述信息&#xff…

论文阅读-通过云特征增强的深度学习预测云工作负载转折点

论文名称&#xff1a;Cloud Workload Turning Points Prediction via Cloud Feature-Enhanced Deep Learning 摘要 云工作负载转折点要么是代表工作负载压力的局部峰值点&#xff0c;要么是代表资源浪费的局部谷值点。预测这些关键点对于向系统管理者发出警告、采取预防措施以…

选择大语言模型:2024 年开源 LLM 入门指南

作者&#xff1a;来自 Elastic Aditya Tripathi 如果说人工智能在 2023 年起飞&#xff0c;这绝对是轻描淡写的说法。数千种新的人工智能工具被推出&#xff0c;人工智能功能被添加到现有的应用程序中&#xff0c;好莱坞因对这项技术的担忧而戛然而止。 甚至还有一个人工智能工…

【网站项目】038汽车养护管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

sql实现将某一列下移一行

问题 实现如下图所示的 max_salary 下移一行 方法&#xff1a;使用开窗函数 select max_salary, max(max_salary) over(order by max_salary asc rows between 1 PRECEDING and 1 PRECEDING) max_salary_plus from jobs

电商小程序02数据源设计

上一篇我们讲解了电商小程序的需求分析&#xff0c;分析了需要具备的功能并且绘制了系统原型。有了原型之后下一步的事情就是根据原型来设计数据源。 数据源就像盖房子打地基一样&#xff0c;地基打不好&#xff0c;楼可能就盖不高&#xff0c;盖起来要再想调整就比较困难。 …

使用Qt创建项目 Qt中输出内容到控制台 设置窗口大小和窗口标题 Qt查看说明文档

按windows键&#xff0c;找到Qt Creator &#xff0c;打开 一.创建带模板的项目 新建项目 设置项目路径QMainWindow是带工具栏的窗口。 QWidget是无工具栏的窗口。 QDuakig是对话框窗口。创建好的项目如下&#xff1a; #include "widget.h"// 构造函数&#xff…

Windows系统安装Flink及实现MySQL之间数据同步

Apache Flink是一个框架和分布式处理引擎&#xff0c;用于对无界和有界数据流进行有状态计算。Flink的设计目标是在所有常见的集群环境中运行&#xff0c;并以内存执行速度和任意规模来执行计算。它支持高吞吐、低延迟、高性能的流处理&#xff0c;并且是一个面向流处理和批处理…

03 动力云客项目之登录功能后端实现

创建项目 使用Spring initializr初始化项目 老师讲的是3.2.0, 但小版本之间问题应该不大.

14.0 Zookeeper环球锁实现原理

全局锁是控制全局系统之间同步访问共享资源的一种方式。 下面介绍zookeeper如何实现全民锁&#xff0c;讲解他锁和共享锁两类全民锁。 排他锁 排他锁&#xff08;Exclusive Locks&#xff09;&#xff0c;又被称为写锁或独占锁&#xff0c;如果事务T1对数据对象O1加上排他锁…

2023年09月CCF-GESP编程能力等级认证C++编程一级真题解析

一、单选题(共15题,共30分) 第1题 我们通常说的“内存”属于计算机中的( )。 A:输出设备 B:输入设备 C:存储设备 D:打印设备 答案:C 第2题 以下C++不可以作为变量的名称的是( )。 A:redStar B:RedStar C:red_star D:red star 答案:D 第3题 C++表达式…

Linux C/C++ 原始套接字:打造链路层ping实现

在C/C中&#xff0c;我们可以使用socket函数来创建套接字。我们需要指定地址族为AF_PACKET&#xff0c;协议为htons(ETH_P_ALL)来捕获所有传入和传出的数据包。 可以使用sendto和recvfrom函数来发送和接收数据包。我们需要构建一个合法的链路层数据包&#xff0c;在数据包的头…

C++ //练习 4.23 因为运算符的优先级问题,下面这条表达式无法通过编译。根据4.12节中的表(第147页)指出它的问题在哪里?应该如何修改?

C Primer&#xff08;第5版&#xff09; 练习 4.23 练习 4.23 因为运算符的优先级问题&#xff0c;下面这条表达式无法通过编译。根据4.12节中的表&#xff08;第147页&#xff09;指出它的问题在哪里&#xff1f;应该如何修改&#xff1f; string s "word"; stri…

树莓派4b连接WQ9201外置无线网卡命令行配置详解

树莓派4B连接WQ9201无线网卡 接线方式 蓝色的线来连接树莓派和WQ9201demo板&#xff0c;USB接树莓派的USB接口&#xff0c;microUSB一端接demo板靠近天线部分的microUSB口。 驱动和固件准备 驱动直接放在树莓派系统的任意目录&#xff0c;目前配置则是将驱动放在树莓派的主目…

【漏洞复现】电信网关配置管理系统SQL注入漏洞

Nx01 产品简介 电信网关配置管理系统是一个用于管理和配置电信网络中网关设备的软件系统。它可以帮助网络管理员实现对网关设备的远程监控、配置、升级和故障排除等功能&#xff0c;从而确保网络的正常运行和高效性能。 Nx02 漏洞描述 电信网关配置管理系统存在SQL注入漏洞,攻…

【华为云】云上两地三中心实践实操

写在前面 应用上云之后&#xff0c;如何进行数据可靠性以及业务连续性的保障是非常关键的&#xff0c;通过华为云云上两地三中心方案了解相关方案认证地址&#xff1a;https://connect.huaweicloud.com/courses/learn/course-v1:HuaweiXCBUCNXI057Self-paced/about当前内容为华…

《动手学深度学习(PyTorch版)》笔记7.4

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

苹果macbook电脑删除数据恢复该怎么做?Mac电脑误删文件的恢复方法

苹果电脑删除数据恢复该怎么做&#xff1f;Mac电脑误删文件的恢复方法 如何在Mac上恢复误删除的文件&#xff1f;在日常使用Mac电脑时&#xff0c;无论是工作还是娱乐&#xff0c;我们都会创建和处理大量的文件。然而&#xff0c;有时候可能会不小心删除一些重要的文件&#x…

Stata学习(1)

一、五大窗口 Command窗口&#xff1a;实现人机交互 来导入一个自带数据&#xff1a; sysuse是导入系统自带的数据&#xff0c;auto导入该数据的名称&#xff0c;后面的clear是清除之前的数据 结果窗口&#xff1a;展示计算结果、查找功能 在Edit的find可以实现查找功能&#…
最新文章