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

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

1 Spring Boot配置多数据源

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

spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedefault-datasource:username: rootpassword: rooturl: jdbc:mysql://localhost:3306/default?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.jdbc.Driverinspur-zs-datasource:username: rootpassword: rooturl: jdbc:mysql://localhost:3306/inspur-zs?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.jdbc.Driverdruid:initial-size: 5min-idle: 1max-active: 20profiles:active: devmybatis:mapper-locations: classpath:/mapper/*.xmltype-aliases-package: com.inspur.pojoconfiguration:mapUnderscoreToCamelCase: truelog-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<>();@Resourceprivate DataSource defaultDatasource;@Resourceprivate DataSource inspurZsDatasource;public DynamicDataSource1() {flag.set("defaultDatasource");}@Overridepublic Connection getConnection() throws SQLException {if (flag.get().equals("defaultDatasource")) {return defaultDatasource.getConnection();} else if (flag.get().equals("inspurZsDatasource")) {return inspurZsDatasource.getConnection();}return defaultDatasource.getConnection();}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}

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

使用:

    @Overridepublic 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<>();@Resourceprivate DataSource defaultDatasource;@Resourceprivate DataSource inspurZsDatasource;public DynamicDataSource2() {flag.set("defaultDatasource");}@Overrideprotected Object determineCurrentLookupKey() {return flag.get();}@Overridepublic 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来修改数据源。

使用:

    @Overridepublic 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<>();@Resourceprivate DataSource defaultDatasource;@Resourceprivate DataSource inspurZsDatasource;public DynamicDataSource3() {flag.set(DataSourceType.DEFAULT_DATASOURCE.name());}@Overrideprotected Object determineCurrentLookupKey() {return flag.get();}@Overridepublic 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 {@Resourceprivate BsFactoryCalendarMapper bsFactoryCalendarMapper;@Override@Transactionalpublic 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.DruidDataSourcedefault-datasource:username: rootpassword: rootjdbc-url: jdbc:mysql://localhost:3306/default?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.jdbc.Driverinspur-zs-datasource:username: rootpassword: rootjdbc-url: jdbc:mysql://localhost:3306/inspur-zs?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8driver-class-name: com.mysql.jdbc.Drivermain:allow-bean-definition-overriding : truedruid:initial-size: 5min-idle: 1max-active: 20profiles:active: devmybatis:mapper-locations: classpath:/mapper/*.xmltype-aliases-package: com.inspur.pojoconfiguration:mapUnderscoreToCamelCase: truelog-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")@Primarypublic SqlSessionTemplate sqlSessionTemplate(@Qualifier("DefaultSqlSessionFactory") SqlSessionFactory factory) {return new SqlSessionTemplate(factory);}@Beanpublic 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")@Primarypublic SqlSessionTemplate sqlSessionTemplate(@Qualifier("InspurZsSqlSessionFactory") SqlSessionFactory factory) {return new SqlSessionTemplate(factory);}@Beanpublic 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.rhkb.cn/news/79320.html

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

相关文章

考研算法38天:反序输出 【字符串的翻转】

题目 题目收获 很简单的一道题&#xff0c;但是还是有收获的&#xff0c;我发现我连scanf的字符串输入都忘记咋用了。。。。。我一开始写的 #include <iostream> #include <cstring> using namespace std;void deserve(string &str){int n str.size();int…

20230806将ASF格式的视频转换为MP4

20230806将ASF格式的视频转换为MP4 2023/8/6 18:47 缘起&#xff0c;自考中山大学的《计算机网络》&#xff0c;考试《数据库系统原理》的时候找到视频&#xff0c;由于个人的原因&#xff0c;使用字幕更加有学习效率&#xff01; 由于【重型】的PR2023占用资源较多&#xff0c…

通话降噪算法在手机和IOT设备上的应用和挑战

随着电子产品的升级换代&#xff0c;用户对通话质量的要求也越来越高。通话降噪算法对通话质量起到了关键核心的作用。计算资源的提升使得深度学习模型在便携式的低功耗芯片上面跑起来了&#xff0c;器件成本降低让IoT设备开始使用骨导传感器&#xff0c;&#xff0c;那怎么样才…

【秋招】算法岗的八股文之机器学习

目录 机器学习特征工程常见的计算模型总览线性回归模型与逻辑回归模型线性回归模型逻辑回归模型区别 朴素贝叶斯分类器模型 (Naive Bayes)决策树模型随机森林模型支持向量机模型 (Support Vector Machine)K近邻模型神经网络模型卷积神经网络&#xff08;CNN&#xff09;循环神经…

uni-app、H5实现瀑布流效果封装,列可以自定义

文章目录 前言一、效果二、使用代码三、核心代码总结 前言 最近做项目需要实现uni-app、H5实现瀑布流效果封装&#xff0c;网上搜索有很多的例子&#xff0c;但是代码都是不够完整的&#xff0c;下面来封装一个uni-app、H5都能用的代码。在小程序中&#xff0c;一个个item渲染…

网络安全(秋招)如何拿到offer?(含面试题)

以下为网络安全各个方向涉及的面试题&#xff0c;星数越多代表问题出现的几率越大&#xff0c;祝各位都能找到满意的工作。 注&#xff1a;本套面试题&#xff0c;已整理成pdf文档&#xff0c;但内容还在持续更新中&#xff0c;因为无论如何都不可能覆盖所有的面试问题&#xf…

今天开始学习如何正式调查

本节要讲解三个内容 样本容量 调查方式 调查问卷的回收 在正式调查之前需要确定样本容量 就说要准备调查多少人确定好样本容量之后又要考虑设计的调查问卷 是以什么样的方式发出去 问卷的回收又要注意什么问题 要讲的主要内容 先看样本容量 样本容量确定的基本原…

redis+token+分布式锁确保接口的幂等性

目录 1.幂等性是什么&#xff1f; 2.如何实现幂等性呢&#xff1f; 1.新增管理员&#xff0c;出弹窗的同时&#xff0c;请求后台。 2.后端根据雪花算法生成唯一标识key&#xff0c;以雪花数为key存到redis。并返回key给前端。 3.前端保存后端传过来的key。 4.前端输入完成…

Navicat远程连接Linux的MySQL

打开Linux终端&#xff0c;进入root权限&#xff0c;用vim打开MySQL的配置文件 vim /etc/mysql/mysql.conf.d/mysqld.cnf将bind-address的值改为0.0.0.0 进入MySQL mysql -u root -p 将root用户改为允许远程登录 update user set host % where user root; 创建用户 CRE…

【C++奇遇记】函数探幽(上)

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集 数据库专栏 初阶数据结构 &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如…

玩转SWAT模型——最全面SWAT模型教程【建模方法、实例应用、高级进阶】

目录 第一部分&#xff1a;SWAT模型实践部分 第二部分&#xff1a;SWAT模型【进阶部分】 更多推荐 【专家】&#xff1a;刘老师【副教授】&#xff0c;北京重点高校资深专家&#xff0c;和美国SWAT软件开发方长期合作&#xff0c;拥有丰富的科研及工程技术经验&#xff0c;长…

node.js安装

下载 https://nodejs.org/en 安装 D:\Program Files\nodejs 配置 D:\Program Files\nodejs 目录下新建 node_cache 和 node_global 在cmd管理员身份运行&#xff1a; npm config set prefix "D:\Program Files\nodejs\node_global" npm config set cache &qu…

Netty 粘包半包

什么是 TCP 粘包半包&#xff1f; 假设客户端分别发送了两个数据包 D1 和 D2 给服务端&#xff0c;由于服务端一次读取到的字节 数是不确定的&#xff0c;故可能存在以下 4 种情况。 &#xff08;1&#xff09;服务端分两次读取到了两个独立的数据包&#xff0c;分别…

手机变电脑2023之虚拟电脑droidvm

手机这么大的内存&#xff0c;装个app来模拟linux&#xff0c;还是没问题的。 app 装好后&#xff0c;手指点几下确定按钮&#xff0c;等几分钟就能把linux桌面环境安装好。 不需要敲指令&#xff0c; 不需要对手机刷机&#xff0c; 不需要特殊权限&#xff0c; 不需要找驱…

怎么快速搭建BI?奥威BI系统做出了表率

搭建BI系统有两大关键&#xff0c;分别是环境搭建和数仓建设。这两点不管是哪一个都相当地费时费力&#xff0c;那要怎么才能快速搭建BI平台&#xff0c;顺利实现全企业数字化运营决策&#xff1f;奥威BI系统方案&#xff0c;你值得拥有&#xff01; 奥威BI系统方案&#xff0…

剑指Offer06. 从尾到头打印链表

# 06. 从尾到头打印链表 目录 # 06. 从尾到头打印链表题目代码&#xff08;双指针法&#xff09;代码逻辑 递归法代码设计流程 题目 官方地址 代码&#xff08;双指针法&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* L…

【计算机视觉 | Kaggle】飞机凝结轨迹识别 Baseline 分享和解读(含源代码)

文章目录 一、导读二、比赛背景三、比赛任务四、比赛数据五、评价指标六、Baseline6.1 Training part6.2 Submission part 一、导读 比赛名称&#xff1a;Google Research - Identify Contrails to Reduce Global Warming https://www.kaggle.com/competitions/google-researc…

【计算机网络】UDP协议

文章目录 1. UDP简介2. TCP和UDP的区别3. UDP的报文格式4. UDP的应用场景 1. UDP简介 UDP和TCP都是传输层协议&#xff0c;用于在计算机网络中发送和接收数据。数据可以是视频,可以是图片,可以是文字等.与TCP相比&#xff0c;UDP不提供数据传输的可靠性和流控制功能&#xff0…

8月16日起!亚马逊新商品上架需更新产品类型的274个属性!

亚马逊美国站发布公告称为了帮助买家更轻松地搜索产品&#xff0c;改善买家的购买决策提高卖家的销量&#xff0c;8月16日起受影响的200种产品类型的274个属性在上架前需更新属性&#xff0c;以下是公告内容&#xff1a; 自2023年8月16日起&#xff0c;200种产品类型的274个属…

18 | 基于DDD的微服务设计实例

为了更好地理解 DDD 的设计流程&#xff0c;这篇文章会用一个项目来带你了解 DDD 的战略设计和战术设计&#xff0c;走一遍从领域建模到微服务设计的全过程&#xff0c;一起掌握 DDD 的主要设计流程和关键点。 项目基本信息 项目的目标是实现在线请假和考勤管理。功能描述如下…