jpa 自定义sql if_SpringBoot整合JPA实现多数据源及读写分离

SpringBoot整合JPA实现多数据源及读写分离

项目地址:https://github.com/baojingyu/spring-boot-jpa-dynamic-datasource

本项目使用 SpringBoot 和 SpringData JPA 实现多数据源,动态数据源的切换。

前段时间写 MySQL 主从复制的技术分享,我就在想,既然主从复制都搭建好了,不写个 Demo 玩玩读写分离,好像差点意思哼~ 于是就有了这么个 Demo Project。

一、什么是读写分离?

读写分离其实就是将数据库分为主从库,简单点说是主库处理事务性增、改、删操作,多个从库处理查询操作。主库的事务性操作导致的变更通过BinLog 日志同步到集群中的从库。

二、为什么要读写分离?

其实最主要是减轻数据库的压力。数据库的写操作比较耗时,如果没有进行读写分离,写操作将会影响到读操作的效率。

三、什么时候要读写分离?

如果程序读多写少,那么可以考虑读写分离,利用数据库主从同步,能减轻数据库压力,提高性能。

四、读写分离方案

数据库读远大于写,查询多的情况,那就得考虑主库负责写操作,多个从库负责读操作,另外结合 Redis 等缓存来配合分担数据库的读操作。

五、代码层面进行读写分离

代码环境是SpringBoot + SpringData JPA + Druib连接池。想要读写分离就需要配置多个数据源,在进行写操作时选择写的数据源,读操作时选择读的数据源。其中有两个关键点:

  1. 如何切换数据源
  2. 如何根据不同的方法选择正确的数据源

1)、如何切换数据源

通常用 SpringBoot 时都是使用它的默认配置,只需要在配置文件中定义好连接属性就行了,但是现在我们需要自己来配置了,Spring 是支持多数据源的,多个datasource放在一个HashMapTargetDataSource中,通过dertermineCurrentLookupKey获取key来决定要使用哪个数据源。因此我们的目标就很明确了,建立多个datasource放到TargetDataSource中,同时重写dertermineCurrentLookupKey方法来决定使用哪个key。

用户自定义设置数据库路由

SpringBoot 提供了 AbstractRoutingDataSource 根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库。AbstractRoutingDataSource 就是DataSource 的抽象,基于 lookupKey 的方式在多个数据库中进行切换。重点关注setTargetDataSources,setDefaultTargetDataSource,determineCurrentLookupKey三个方法。那么AbstractRoutingDataSource就是Spring读写分离的关键了。

实现可动态路由的数据源,在每次数据库查询操作前执行。

2)、如何选择数据源

事务一般是注解在Service层的,因此在开始这个service方法调用时要确定数据源,有什么通用方法能够在开始执行一个方法前做操作呢?相信你已经想到了那就是切面 。怎么切有两种办法:

注解式,定义一个只读注解,被该数据标注的方法使用读库 方法名,根据方法名写切点,比如getXXX用读库,setXXX用写库

六、部分代码

DataSourcesConfig

/** * @author jingyu.bao * @version 1.0 * @className DataSourceConfig * @description * @date 7/5/2020 20:09 **/@EnableTransactionManagement@Configurationpublic class DataSourceConfig {    @Value("${spring.datasource.druid.master.name}")    private String masterName;    @Value("${spring.datasource.druid.master.url}")    private String masterUrl;    @Value("${spring.datasource.druid.master.username}")    private String masterUsername;    @Value("${spring.datasource.druid.master.password}")    private String masterPassword;    @Value("${spring.datasource.druid.master.driver-class-name}")    private String masterDriverClassName;    @Value("${spring.datasource.druid.slave.name}")    private String slaveName;    @Value("${spring.datasource.druid.slave.url}")    private String slaveUrl;    @Value("${spring.datasource.druid.slave.username}")    private String slaveUsername;    @Value("${spring.datasource.druid.slave.password}")    private String slavePassword;    @Value("${spring.datasource.druid.slave.driver-class-name}")    private String slaveDriverClassName;    @Value("${spring.datasource.druid.initial-size}")    private String initialSize;    @Value("${spring.datasource.druid.min-idle}")    private String minIdle;    @Value("${spring.datasource.druid.max-active}")    private String maxActive;    @Value("${spring.datasource.druid.max-wait}")    private String maxWait;    @Value("${spring.datasource.druid.time-between-eviction-runs-millis}")    private String timeBetweenEvictionRunsMillis;    @Value("${spring.datasource.druid.min-evictable-idle-time-millis}")    private String minEvictableIdleTimeMillis;    @Value("${spring.datasource.druid.validation-query}")    private String validationQuery;    @Value("${spring.datasource.druid.test-while-idle}")    private String testWhileIdle;    @Value("${spring.datasource.druid.test-on-borrow}")    private String testOnBorrow;    @Value("${spring.datasource.druid.test-on-return}")    private String testOnReturn;    @Value("${spring.datasource.druid.filters}")    private String filters;    @Value("{spring.datasource.druid.filter.stat.log-slow-sql}")    private String logSlowSql;    @Value("{spring.datasource.druid.filter.stat.slow-sql-millis}")    private String slowSqlMillis;    @Value("${spring.datasource.druid.type}")    private String type;    @Value("${spring.datasource.druid.stat-view-servlet.login-username}")    private String loginUserName;    @Value("${spring.datasource.druid.stat-view-servlet.login-password}")    private String password;    @Bean(name = "masterDataSource")    public DataSource masterDataSource() {        DruidDataSource datasource = new DruidDataSource();        datasource.setUrl(masterUrl);        datasource.setUsername(masterUsername);        datasource.setPassword(masterPassword);        datasource.setDriverClassName(masterDriverClassName);        //configuration        if (!StringUtils.isEmpty(initialSize)) {            datasource.setInitialSize(Integer.parseInt(initialSize));        }        if (!StringUtils.isEmpty(minIdle)) {            datasource.setMinIdle(Integer.parseInt(minIdle));        }        if (!StringUtils.isEmpty(maxActive)) {            datasource.setMaxActive(Integer.parseInt(maxActive));        }        if (!StringUtils.isEmpty(maxWait)) {            datasource.setMaxWait(Integer.parseInt(maxWait));        }        if (!StringUtils.isEmpty(timeBetweenEvictionRunsMillis)) {            datasource.setTimeBetweenEvictionRunsMillis(Integer.parseInt(timeBetweenEvictionRunsMillis));        }        if (!StringUtils.isEmpty(minEvictableIdleTimeMillis)) {            datasource.setMinEvictableIdleTimeMillis(Integer.parseInt(minEvictableIdleTimeMillis));        }        if (!StringUtils.isEmpty(validationQuery)) {            datasource.setValidationQuery(validationQuery);        }        if (!StringUtils.isEmpty(testWhileIdle)) {            datasource.setTestWhileIdle(Boolean.parseBoolean(testWhileIdle));        }        if (!StringUtils.isEmpty(testOnBorrow)) {            datasource.setTestOnBorrow(Boolean.parseBoolean(testOnBorrow));        }        if (!StringUtils.isEmpty(testOnReturn)) {            datasource.setTestOnReturn(Boolean.parseBoolean(testOnReturn));        }        try {            datasource.setFilters(filters);        } catch (SQLException e) {            e.printStackTrace();        }        return datasource;    }    @Bean(name = "slaveDataSource")    public DataSource slaveDataSource() {        DruidDataSource datasource = new DruidDataSource();        datasource.setUrl(masterUrl);        datasource.setUsername(masterUsername);        datasource.setPassword(masterPassword);        datasource.setDriverClassName(masterDriverClassName);        //configuration        if (!StringUtils.isEmpty(initialSize)) {            datasource.setInitialSize(Integer.parseInt(initialSize));        }        if (!StringUtils.isEmpty(minIdle)) {            datasource.setMinIdle(Integer.parseInt(minIdle));        }        if (!StringUtils.isEmpty(maxActive)) {            datasource.setMaxActive(Integer.parseInt(maxActive));        }        if (!StringUtils.isEmpty(maxWait)) {            datasource.setMaxWait(Integer.parseInt(maxWait));        }        if (!StringUtils.isEmpty(timeBetweenEvictionRunsMillis)) {            datasource.setTimeBetweenEvictionRunsMillis(Integer.parseInt(timeBetweenEvictionRunsMillis));        }        if (!StringUtils.isEmpty(minEvictableIdleTimeMillis)) {            datasource.setMinEvictableIdleTimeMillis(Integer.parseInt(minEvictableIdleTimeMillis));        }        if (!StringUtils.isEmpty(validationQuery)) {            datasource.setValidationQuery(validationQuery);        }        if (!StringUtils.isEmpty(testWhileIdle)) {            datasource.setTestWhileIdle(Boolean.parseBoolean(testWhileIdle));        }        if (!StringUtils.isEmpty(testOnBorrow)) {            datasource.setTestOnBorrow(Boolean.parseBoolean(testOnBorrow));        }        if (!StringUtils.isEmpty(testOnReturn)) {            datasource.setTestOnReturn(Boolean.parseBoolean(testOnReturn));        }        try {            datasource.setFilters(filters);        } catch (SQLException e) {            e.printStackTrace();        }        return datasource;    }    @Primary    @Bean    public DynamicRoutingDataSource dynamicDataSource(@Qualifier(value = "masterDataSource") DataSource masterDataSource, @Qualifier(value = "slaveDataSource") DataSource slaveDataSource) {        Map targetDataSources = new HashMap<>(2);        targetDataSources.put(DynamicRoutingDataSourceContext.MASTER, masterDataSource);        targetDataSources.put(DynamicRoutingDataSourceContext.SLAVE, slaveDataSource);        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();        //设置数据源        dynamicRoutingDataSource.setTargetDataSources(targetDataSources);        //设置默认选择的数据源        dynamicRoutingDataSource.setDefaultTargetDataSource(masterDataSource);        dynamicRoutingDataSource.afterPropertiesSet();        return dynamicRoutingDataSource;    }    @Bean    public ServletRegistrationBean statViewServlet() {        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");        //设置ip白名单        servletRegistrationBean.addInitParameter("allow", "");        //设置ip黑名单,优先级高于白名单        servletRegistrationBean.addInitParameter("deny", "");        //设置控制台管理用户        servletRegistrationBean.addInitParameter("loginUsername", loginUserName);        servletRegistrationBean.addInitParameter("loginPassword", password);        //是否可以重置数据        servletRegistrationBean.addInitParameter("resetEnable", "false");        return servletRegistrationBean;    }    @Bean    public FilterRegistrationBean statFilter() {        //创建过滤器        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());        //设置过滤器过滤路径        filterRegistrationBean.addUrlPatterns("/*");        //忽略过滤的形式        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");        return filterRegistrationBean;    }

DynamicRoutingDataSource

/** * @author jingyu.bao * @version 1.0 * @className DynamicRoutingDataSource * @description * @date 7/5/2020 21:22 **/public class DynamicRoutingDataSource extends AbstractRoutingDataSource {    @Override    protected Object determineCurrentLookupKey() {        Object lookupKey = DynamicRoutingDataSourceContext.getRoutingDataSource();        System.err.println(Thread.currentThread().getName() + " determineCurrentLookupKey : " + lookupKey);        return lookupKey;    }}

DynamicRoutingDataSourceContext

/** * @author jingyu.bao * @version 1.0 * @className DynamicRoutingDataSourceContext * @description * @date 7/5/2020 20:16 **/public class DynamicRoutingDataSourceContext {    public static final String MASTER = "master";    public static final String SLAVE = "slave";    private static final ThreadLocal threadLocalDataSource = new ThreadLocal<>();    public static void setRoutingDataSource(Object dataSource) {        if (dataSource == null) {            throw new NullPointerException();        }        threadLocalDataSource.set(dataSource);        // System.err.println(Thread.currentThread().getName()+" set RoutingDataSource : " + dataSource);    }    public static Object getRoutingDataSource() {        Object dataSourceType = threadLocalDataSource.get();        if (dataSourceType == null) {            threadLocalDataSource.set(DynamicRoutingDataSourceContext.MASTER);            return getRoutingDataSource();        }        // System.err.println(Thread.currentThread().getName()+" get RoutingDataSource : " + dataSourceType);        return dataSourceType;    }    public static void removeRoutingDataSource() {        threadLocalDataSource.remove();        // System.err.println(Thread.currentThread().getName()+" remove RoutingDataSource");    }}

RoutingAopAspect

/** * @author jingyu.bao * @version 1.0 * @className RoutingAopAspect * @description * @date 7/5/2020 20:21 **/@Order(0)@Aspect@Componentpublic class RoutingAopAspect {    @Around("@annotation(targetDataSource)")    public Object routingWithDataSource(ProceedingJoinPoint joinPoint, TargetDataSource targetDataSource) throws Throwable {        try {            DynamicRoutingDataSourceContext.setRoutingDataSource(targetDataSource.value());            return joinPoint.proceed();        } finally {            DynamicRoutingDataSourceContext.removeRoutingDataSource();        }    }}

TargetDataSource

/** * @author jingyu.bao * @version 1.0 * @className TargetDataSource * @description * @date 7/5/2020 20:40 **/@Target({ElementType.METHOD, ElementType.TYPE})@Retention(value = RetentionPolicy.RUNTIME)@Documentedpublic @interface TargetDataSource {    String value();}

UserInfoServiceImpl

/** * @author jingyu.bao * @version 1.0 * @className UserServiceImpl * @description * @date 7/5/2020 21:39 **/@Servicepublic class UserInfoServiceImpl implements IUserInfoService {    @Autowired    private IUserInfoRepository userInfoRepository;    @TargetDataSource(value = "slave")    @Override    public List findAll() {        return userInfoRepository.findAll();    }    @Transactional    @Override    public UserInfoEntity save(UserInfoEntity userInfoEntity) {        return userInfoRepository.save(userInfoEntity);    }    @TargetDataSource(value = "slave")    @Override    public UserInfoEntity findById(Long id) {        Optional userInfoEntity = userInfoRepository.findById(id);        return userInfoEntity.isPresent() ? userInfoEntity.get() : null;    }    @Override    public List findAllMaster() {        return userInfoRepository.findAll();    }    @Transactional    @Override    public void saveAll(ArrayList list) {        userInfoRepository.saveAll(list);    }}

application.properties

server.port=8080server.tomcat.max-threads=3000server.tomcat.max-connections=20000server.tomcat.uri-encoding=UTF-8server.tomcat.accept-count=800# 自定义线程池参数fxea.threadPool.coreThreadNum=5fxea.threadPool.maxThreadNum=25# 这个参数是在建表的时候,将默认的存储引擎切换为 InnoDB 用的spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialectspring.jpa.database=mysql# 配置在日志中打印出执行的 SQL 语句信息。spring.jpa.show-sql=true# 第一次建表create,后面用update,要不然每次重启都会新建表spring.jpa.hibernate.ddl-auto=create# Druidspring.datasource.druid.type=com.alibaba.druid.pool.DruidDataSource#初始化连接大小spring.datasource.druid.initial-size=10#最小连接池数量spring.datasource.druid.min-idle=10#最大连接池数量spring.datasource.druid.max-active=100#配置获取连接等待超时的时间spring.datasource.druid.max-wait=60000#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒spring.datasource.druid.time-between-eviction-runs-millis=60000#配置一个连接在池中最小生存的时间,单位是毫秒spring.datasource.druid.min-evictable-idle-time-millis=300000#测试连接spring.datasource.druid.validation-query=SELECT 'x'#申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性spring.datasource.druid.test-while-idle=true#获取连接时执行检测,建议关闭,影响性能spring.datasource.druid.test-on-borrow=false#归还连接时执行检测,建议关闭,影响性能spring.datasource.druid.test-on-return=false#druid 用户spring.datasource.druid.stat-view-servlet.login-username=admin#druid 密码spring.datasource.druid.stat-view-servlet.login-password=admin#配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙spring.datasource.druid.filters=stat,wallspring.datasource.druid.filter.stat.log-slow-sql=truespring.datasource.druid.filter.stat.slow-sql-millis=1# Db Masterspring.datasource.druid.master.name=masterspring.datasource.druid.master.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.druid.master.url=jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=truespring.datasource.druid.master.username=rootspring.datasource.druid.master.password=123456# Db Slavesspring.datasource.druid.slave.name=slavespring.datasource.druid.slave.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.druid.slave.url=jdbc:mysql://localhost:3307/test?useSSL=false&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=truespring.datasource.druid.slave.username=rootspring.datasource.druid.slave.password=123456

点击左下角阅读原文可跳转至项目下载地址:https://github.com/baojingyu/spring-boot-jpa-dynamic-datasource

点个关注,和我一起共同进步吧!

a2b123ff7c6e8f285816de59d5f0e87e.png
微信公众号:鲸鱼手记

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

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

相关文章

时间序列分析简介

目录 1.引言 2.时间序列的定义 3.时间序列分析方法 &#xff08;1&#xff09;描述性时间序列分析 &#xff08;2&#xff09;统计时序分析 时序分析方法 时域分析方法 1.引言 最早的时间序列分析可以追溯到7000年前&#xff0c;古埃及把尼罗河涨落的情况逐天记录下来&a…

时间序列的预处理

目录 一、特征统计量 1.概率分布 2.特征统计量 二、平稳时间序列的定义 严平稳与宽平稳的关系 三、平稳时间序列的统计性 四、平稳性的重大意义 五、平稳性检验 时序图检验 自相关图检验 R绘图 一、特征统计量 平稳性是某些时间序列具有的一种统计特征要描述清楚这个…

R中‘ts‘ object must have one or more observations

错误如下&#xff1a; 为什么会出现&#xff0c;如下错误是因为我读取数据是&#xff0c;没有对数据进行分割&#xff0c;而是直接读取&#xff0c;然后使用的 最初读取方式为&#xff1a; 改变后的读取方式&#xff1a; 此时&#xff0c;再调用上面提示错误的那行代码试试&am…

Java字符编码介绍

在计算机中&#xff0c;任何的文字都是以指定的编码方式存在的&#xff0c;在 Java 程序的开发中最常见的是 ISO8859-1、GBK/GB2312、Unicode、 UTF 编码。 Java 中常见编码说明如下&#xff1a; ISO8859-1&#xff1a;属于单字节编码&#xff0c;最多只能表示 0~255 的字符范…

灰色预测法 —— python

目录 1.简介 2.算法详解 2.1 生成累加数据 2.2 累加后的数据表达式 2.3 求解2.2的未知参数 3.实例分析 3.1 导入数据 3.2 进行累加数据 3.3 求解系数 3.4 预测数据及对比 完整代码 1.简介 灰色系统理论认为对既含有已知信息又含有未知或非确定信息的系统进行预测&am…

python爬虫怎么爬小说_python爬虫爬取笔趣网小说网站过程图解

首先&#xff1a;文章用到的解析库介绍 BeautifulSoup&#xff1a; Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。 它是一个工具箱&#xff0c;通过解析文档为用户提供需要抓取的数据&#xff0c;因为简单&#xff0c;所以不需要多少代…

时间序列的预处理之纯随机性检验

目录 1.纯随机序列的定义 2.性质 3.纯随机性检验 1.纯随机序列的定义 纯随机序列也称为白噪声序列&#xff0c;满足如下性质&#xff1a;2.性质 纯随机性&#xff08;无记忆性&#xff09;方差齐性举例&#xff0c;随机生成1000个白噪声序列 用正态分布序列 rnorm(数量&am…

ARMA模型的性质之方法性工具

目录 一、差分 Xt 二、延迟算子 延迟算子的性质 p阶差分 k步差分 三、线性差分方程 齐次线性差分方程的解 非齐次线性差分方程的解 时序分析与线性差分方程的关系 一、差分 Xt 二、延迟算子 延迟算子类似于一个时间指针&#xff0c;当前序列值乘以一个延迟算子&…

python如何下载库_python中如何下载库

python下载安装库的方法&#xff1a; 1、在线安装 在cmd窗口直接运行&#xff1a;pip install 包名&#xff0c;如 pip install requests 注意&#xff1a;这种方式安装时会自动下载第三方库&#xff0c;安装完成后并不会删除&#xff0c;如需删除请到它的默认下载路径下手动删…

ARMA模型的性质 1

目录 1.wold分解定理&#xff08;1938&#xff09; 2.AR模型 2.1定义&#xff1a; AR(p) 有三个限制条件&#xff1a; 中心化 AR(p) 模型 2.2 AR模型的平稳性判别 序列拟合函数 R 举例 1.wold分解定理&#xff08;1938&#xff09; 对于任何一个离散平稳序列 {xt} 他都…

python 二维码_Python提取支付宝和微信支付二维码

本文首发于我的个人博客&#xff0c;更多 Python 和 django 开发教程&#xff0c;请访问 追梦人物的博客。支付宝或者微信支付导出的收款二维码&#xff0c;除了二维码部分&#xff0c;还有很大一块背景图案&#xff0c;例如下面就是微信支付的收款二维码&#xff1a;有时候我们…

ARMA模型的平稳性判别(续)

目录 1.特征根判别法 AR(p)模型对应齐次方程特征根与回归系数多项式根的关系&#xff1a; 2.平稳域判别 &#xff08;1&#xff09;AR(1)(一阶)模型平稳域 &#xff08;2&#xff09;AR(2)(二阶)模型平稳域 3.举例 4.函数展开成幂级数——麦克劳林级数 小结 1.特征根判…

form表单中根据值判断是否disabled_Java 0基础入门 (Html表单、表单元素)

上一篇&#xff1a;Java 0基础入门 (Html标签的使用)表单在网页中主要负责数据采集功能。一.表单实际应用场景百度搜索5173注册如上两张图&#xff0c;图中的黑色线条是我画上去的&#xff0c;如果按照黑线&#xff0c;在Excle中画出这两张表单&#xff0c;相信大家都可以也不是…

ARMA模型性质之平稳AR模型得统计性质

目录 1.均值 Green函数定义 Green函数递推公式 2.方差 举例&#xff1a; 方法1&#xff1a; 方法2&#xff1a; 3.协方差函数 举例1&#xff1a; 举例2&#xff1a; 4.自相关系数 常用的ARA模型自相关系数递推公式&#xff1a; AR模型自相关系数的性质 举例 5.偏自…

LDA(线性判别分析(普通法))详解 —— python

在这里和大家道个歉&#xff0c;因为我有一篇matlab的LDA和这篇内容大致相同&#xff0c;我就犯懒了&#xff0c;直接复制&#xff0c;没想到公式复制过来全变成了图片&#xff0c;然后造成了&#xff0c;排版有问题&#xff0c;非常难看&#xff0c;真的很抱歉&#xff01;&am…

wordpress 通过域名无法访问_VPS主机和宝塔面板搭建WordPress网站教程

这是一篇Wordpress建站教程&#xff0c;记录了我在VPS主机上&#xff0c;通过使用宝塔面板&#xff0c;搭建Wordpress网站或个人博客的详细步骤&#xff0c;外贸新人或小白在建立网站的时候可以作为参考。WordPress是全球最流行的建站程序&#xff0c;而且是免费的。用Wordpres…

猜数字小游戏

java代码 猜数字小游戏 程序分析 根据提示输入内容 获取输入的内容 使用for循环进行遍历使用if循坏进行数值的判断 输出结果 完整代码 import java.util.Scanner; import java.util.Random; public class mulTip{public static void main(String[] args){System.out.println…

LDA(线性判别分析(普通法))详解 —— matlab

目录 前言 正题 1.LDA的思想 2. 瑞利商&#xff08;Rayleigh quotient&#xff09;与广义瑞利商&#xff08;genralized Rayleigh quotient&#xff09; 3. 二类LDA原理 4.多类LDA原理 5.LDA分类 6.LDA算法流程 二类LDA matlab举例&#xff1a; 1.读取数据集 2.分离…

java 异步得到函数返回值_使用JavaScript进行异步编程

毫无疑问&#xff0c;虽然JavaScript的历史比较悠久&#xff0c;但这并不妨碍它成为当今最受欢迎的编程语言之一。对刚接触该语言的人来说&#xff0c;JavaScript的异步特性可能会有一些挑战。在本文中&#xff0c;我们将了解和使用Promise和async/await来编写小型异步程序。通…

ARMA模型的性质之MA模型

目录 一、MA模型的定义 二、MA模型的统计性质 1.常数均值 2.常数方差 3.自协方差函数q阶结尾 4.自相关系数q阶截尾 举例&#xff1a; 三、MA模型的可逆 1.可逆的定义和条件 2.MA与AR模型的对比 3.逆函数的递推公式 举例&#xff1a; 四、MA模型的偏自相关系数拖尾…