【微服務】后臺管理項目多數據源管理方案實戰

目錄

前言

1、使用Spring提供的AbstractRoutingDataSource

2、使用MyBatis注冊多個SqlSessionFactory

3、使用dynamic-datasource框架


前言

????????Java后臺使用MyBatis-plus 快速訪問多個數 據源,這里分享三種常用的多數據源管理方案

1、使用Spring提供的AbstractRoutingDataSource

這種方式的核心是使用 Spring 提供的 AbstractRoutingDataSource 抽象類,注入多個數據源。核心代碼如下:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;@Component
@Primary   // 將該Bean設置為主要注入Bean
public class DynamicDataSource extends AbstractRoutingDataSource {// 當前使用的數據源標識public static ThreadLocal<String> name=new ThreadLocal<>();// 寫@AutowiredDataSource dataSource1;// 讀@AutowiredDataSource dataSource2;// 返回當前數據源標識@Overrideprotected Object determineCurrentLookupKey() {return name.get();}@Overridepublic void afterPropertiesSet() {// 為targetDataSources初始化所有數據源Map<Object, Object> targetDataSources=new HashMap<>();targetDataSources.put("W",dataSource1);targetDataSources.put("R",dataSource2);super.setTargetDataSources(targetDataSources);// 為defaultTargetDataSource 設置默認的數據源super.setDefaultTargetDataSource(dataSource1);super.afterPropertiesSet();}
}
將自己實現的 DynamicDataSource 注冊成為默認的 DataSource 實例后,只需要在每次使用 DataSource 時,提前

改變一下其中的name標識,就可以快速切換數據源。這里使用到AOP功能,關鍵代碼如下:

注解類,用于標識讀庫或者寫庫

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/**** @Author 黎劍* @Slogan 無意與眾不同,只因品位出眾,致敬未來的你*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface WR {String value() default "W";
}

切面類

import com.lijian.dynamic.datasource.DynamicDataSource;
import com.lijian.dynamic.datasource.annotation.WR;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/**** @author 黎劍* @slogan 無意與眾不同,只因品位出眾,致敬未來的你*/
@Component
@Aspect
public class DynamicDataSourceAspect implements Ordered {// 前置@Before("within(com.lijian.dynamic.datasource.service.impl.*) && @annotation(wr)")public void before(JoinPoint point, WR wr){String name = wr.value();DynamicDataSource.name.set(name);System.out.println(name);}@Overridepublic int getOrder() {return 0;}//TODO 環繞通知
}

數據庫配置類


import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.lijian.dynamic.datasource.DynamicDataSource;
import com.lijian.dynamic.datasource.plugin.DynamicDataSourcePlugin;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
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 javax.sql.DataSource;/**** @author 黎劍* @slogan 無意與眾不同,只因品位出眾,致敬未來的你*/
@Configuration
public class DataSourceConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource1")public DataSource dataSource1() {// 底層會自動拿到spring.datasource中的配置, 創建一個DruidDataSourcereturn DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource2")public DataSource dataSource2() {// 底層會自動拿到spring.datasource中的配置, 創建一個DruidDataSourcereturn DruidDataSourceBuilder.create().build();}@Beanpublic DataSourceTransactionManager transactionManager1(DynamicDataSource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);return dataSourceTransactionManager;}@Beanpublic DataSourceTransactionManager transactionManager2(DynamicDataSource dataSource){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource);return dataSourceTransactionManager;}
}

application.yml

spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedatasource1:url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driverdatasource2:url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driver

服務層實現類

import com.lijian.dynamic.datasource.annotation.WR;
import com.lijian.dynamic.datasource.mapper.FrendMapper;
import com.lijian.dynamic.datasource.entity.Frend;
import com.lijian.dynamic.datasource.service.FrendService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
public class FrendImplService implements FrendService {@AutowiredFrendMapper frendMapper;@Override@WR("R")        // 庫2public List<Frend> list() {return frendMapper.list();}@Override@WR("W")        // 庫1public void save(Frend frend) {frendMapper.save(frend);}
}

啟動類

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication
@MapperScan("com.lijian.dynamic.datasource.mapper")
@EnableAspectJAutoProxy(exposeProxy=true) // 啟動AOP
public class DynamicDatasourceApplication {public static void main(String[] args) {SpringApplication.run(DynamicDatasourceApplication.class, args);}}

2、使用MyBatis注冊多個SqlSessionFactory

如果使用MyBatis框架,要注冊多個數據源的話,就需要將MyBatis底層的DataSource、SqlSessionFactory、 DataSourceTransactionManager這些核心對象一并進行手動注冊。例如:
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;import javax.sql.DataSource;@Configuration
// 繼承mybatis:
// 1. 指定掃描的mapper接口包(主庫)
// 2. 指定使用sqlSessionFactory是哪個(主庫)
@MapperScan(basePackages = "com.lijian.datasource.dynamic.mybatis.mapper.r",sqlSessionFactoryRef="rSqlSessionFactory")
public class RMyBatisConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource2")public DataSource dataSource2() {// 底層會自動拿到spring.datasource中的配置, 創建一個DruidDataSourcereturn DruidDataSourceBuilder.create().build();}@Bean@Primarypublic SqlSessionFactory rSqlSessionFactory()throws Exception {final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();// 指定主庫sessionFactory.setDataSource(dataSource2());// 指定主庫對應的mapper.xml文件/*sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/r/*.xml"));*/return sessionFactory.getObject();}@Beanpublic DataSourceTransactionManager rTransactionManager(){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource2());return dataSourceTransactionManager;}@Beanpublic TransactionTemplate rTransactionTemplate(){return new TransactionTemplate(rTransactionManager());}
}
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.apache.ibatis.logging.stdout.StdOutImpl;
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.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.springframework.transaction.support.TransactionOperations;
import org.springframework.transaction.support.TransactionTemplate;import javax.sql.DataSource;/***** 寫數據源配置*/
@Configuration
// 繼承mybatis:
// 1. 指定掃描的mapper接口包(主庫)
// 2. 指定使用sqlSessionFactory是哪個(主庫)
@MapperScan(basePackages = "com.lijian.datasource.dynamic.mybatis.mapper.w",sqlSessionFactoryRef="wSqlSessionFactory")
public class WMyBatisConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.datasource1")public DataSource dataSource1() {// 底層會自動拿到spring.datasource中的配置, 創建一個DruidDataSourcereturn DruidDataSourceBuilder.create().build();}@Bean@Primarypublic SqlSessionFactory wSqlSessionFactory()throws Exception {final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();// 指定主庫sessionFactory.setDataSource(dataSource1());// 指定主庫對應的mapper.xml文件/*sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/order/*.xml"));*/return sessionFactory.getObject();}@Bean@Primarypublic DataSourceTransactionManager wTransactionManager(){DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(dataSource1());return dataSourceTransactionManager;}@Beanpublic TransactionTemplate wTransactionTemplate(){return new TransactionTemplate(wTransactionManager());}
}

?注解類

import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MultiTransactional {String[] value() default {};
}

?AOP

import com.lijian.datasource.dynamic.mybatis.service.transaction.ComboTransaction;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Aspect
@Component
public class MultiTransactionAop {private final ComboTransaction comboTransaction;@Autowiredpublic MultiTransactionAop(ComboTransaction comboTransaction) {this.comboTransaction = comboTransaction;}@Pointcut("within(com.lijian.datasource.dynamic.mybatis.service.impl.*)")public void pointCut() {}@Around("pointCut() && @annotation(multiTransactional)")public Object inMultiTransactions(ProceedingJoinPoint pjp, MultiTransactional multiTransactional) {return comboTransaction.inCombinedTx(() -> {try {return pjp.proceed();       //執行目標方法} catch (Throwable throwable) {if (throwable instanceof RuntimeException) {throw (RuntimeException) throwable;}throw new RuntimeException(throwable);}}, multiTransactional.value());}
}
import com.alibaba.druid.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.concurrent.Callable;
import java.util.stream.Stream;@Component
public class ComboTransaction {@Autowiredprivate Db1TxBroker db1TxBroker;@Autowiredprivate Db2TxBroker db2TxBroker;public <V> V inCombinedTx(Callable<V> callable, String[] transactions) {if (callable == null) {return null;}// 相當于循環 [wTransactionManager,wTransactionManager]Callable<V> combined = Stream.of(transactions).filter(ele -> !StringUtils.isEmpty(ele)).distinct().reduce(callable, (r, tx) -> {switch (tx) {case DbTxConstants.DB1_TX:return () -> db1TxBroker.inTransaction(r);case DbTxConstants.DB2_TX:return () -> db2TxBroker.inTransaction(r);default:return null;}}, (r1, r2) -> r2);try {return combined.call();} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException(e);}}
}
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import java.util.concurrent.Callable;@Component
public class Db1TxBroker {@Transactional(DbTxConstants.DB1_TX)public <V> V inTransaction(Callable<V> callable) {try {return callable.call();} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException(e);}}
}
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import java.util.concurrent.Callable;@Component
public class Db2TxBroker {@Transactional(DbTxConstants.DB2_TX)public <V> V inTransaction(Callable<V> callable) {try {return callable.call();} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException(e);}}
}
public class DbTxConstants {public static final String DB1_TX = "wTransactionManager";public static final String DB2_TX = "rTransactionManager";
}

服務層類

import com.lijian.datasource.dynamic.mybatis.entity.Frend;
import com.lijian.datasource.dynamic.mybatis.mapper.r.RFrendMapper;
import com.lijian.datasource.dynamic.mybatis.mapper.w.WFrendMapper;
import com.lijian.datasource.dynamic.mybatis.service.FrendService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;import java.util.List;@Service
public class FrendImplService implements FrendService {@Autowiredprivate RFrendMapper rFrendMapper;@Autowiredprivate WFrendMapper wFrendMapper;@AutowiredTransactionTemplate wTransactionTemplate;@AutowiredTransactionTemplate rTransactionTemplate;// 讀-- 讀庫@Overridepublic List<Frend> list() {return rFrendMapper.list();}// 保存-- 寫庫@Overridepublic void save(Frend frend) {wFrendMapper.save(frend);}// 保存-- 寫庫@Overridepublic void saveW(Frend frend) {frend.setName("無羨W");wFrendMapper.save(frend);}// 保存-- 讀庫@Overridepublic void saveR(Frend frend) {frend.setName("無羨R");rFrendMapper.save(frend);}@Transactional(transactionManager = "wTransactionManager")public void saveAll(Frend frend) throws Exception {FrendService frendService = (FrendService) AopContext.currentProxy();frendService.saveAllR(frend);}@Transactional(transactionManager = "rTransactionManager")public void saveAllR(Frend frend) {saveW(frend);saveR(frend);int a = 1 / 0;}
}
這樣就完成了讀庫的注冊。而讀庫與寫庫之間,就可以通過指定不同的 Mapper XML 文件的地址來進行區分。

3、使用dynamic-datasource框架

????????dynamic-datasource是 MyBaits-plus 作者設計的一個多數據源開源方案。使用這個框架需要引入對應的 pom 依賴
        <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency>
這樣就可以在 SpringBoot 的配置文件中直接配置多個數據源,application.yaml
spring:datasource:dynamic:#設置默認的數據源或者數據源組,默認值即為masterprimary: master#嚴格匹配數據源,默認false. true未匹配到指定數據源時拋異常,false使用默認數據源strict: falsedatasource:master:url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driverslave_1:url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456initial-size: 1min-idle: 1max-active: 20test-on-borrow: truedriver-class-name: com.mysql.cj.jdbc.Driver
這樣就配置完成了 master slave_1 兩個數據庫。
接下來在使用時,只要在對應的方法或者類上添加 @DS注解即可。服務層實現類, 例如
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.lijian.dynamic.datasource.mapper.FrendMapper;
import com.lijian.dynamic.datasource.entity.Frend;
import com.lijian.dynamic.datasource.service.FrendService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
public class FrendImplService implements FrendService {@AutowiredFrendMapper frendMapper;@Override@DS("slave")  // 從庫, 如果按照下劃線命名方式配置多個  , 可以指定前綴即可(組名)public List<Frend> list() {return frendMapper.list();}@Override@DS("#session.userID")@DSTransactional #事務控制public void save(Frend frend) {frendMapper.save(frend);}@DS("master")@DSTransactionalpublic void saveAll(){// 執行多數據源的操作}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/37286.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/37286.shtml
英文地址,請注明出處:http://en.pswp.cn/web/37286.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C++深度探索】繼承機制詳解(一)

hello hello~ &#xff0c;這里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;歡迎大家點贊&#x1f973;&#x1f973;關注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;個人主頁&#xff1a;大耳朵土土垚的博客 &#x1…

代碼托管服務:GitHub、GitLab、Gitee

目錄 引言GitHub&#xff1a;全球最大的代碼托管平臺概述功能特點適用場景 GitLab&#xff1a;一體化的開發平臺概述功能特點適用場景 Gitee&#xff08;碼云&#xff09;&#xff1a;中國本土化的代碼托管服務概述功能特點適用場景 功能對比結論 引言 在現代軟件開發中&#…

numpy - array(3)

arr1 np.array([[(1000, 1001, 1002, 1003), (1010, 1011, 1012, 1013), (1020, 1021, 1022, 1023)],[(1100, 1101, 1102, 1103), (1110, 1111, 1112, 1113), (1120, 1121, 1122, 1123)]], dtypeint) (1) 根據坐標訪問元素或內容,更改訪問的內容&#xff0c;array也會更改。“…

C++操作系列(一):MinGW環境安裝與配置(無報錯版)

本文選擇MinGW作為安裝對象。 1. 下載MinGW 進入官網&#xff1a;MinGW - Minimalist GNU for Windows download | SourceForge.net 點擊File&#xff1a; 劃到最下面&#xff1a; &#xfeff; Windows 64位系統下載seh結尾的安裝包&#xff1a; 2. 安裝MinGW 解壓MinGW&am…

力扣第218題“天際線問題”

在本篇文章中&#xff0c;我們將詳細解讀力扣第218題“天際線問題”。通過學習本篇文章&#xff0c;讀者將掌握如何使用掃描線算法和堆來解決這一問題&#xff0c;并了解相關的復雜度分析和模擬面試問答。每種方法都將配以詳細的解釋&#xff0c;以便于理解。 問題描述 力扣第…

【CSS】深入理解CSS 的steps()函數

在CSS動畫中&#xff0c;steps()函數是一個時間函數&#xff0c;它主要用于animation-timing-function屬性&#xff0c;以定義動畫的步進方式。當你想要動畫的某個屬性&#xff08;如background-position或transform: translateX()&#xff09;在特定的步驟之間變化時&#xff…

探索 ES6:現代 JavaScript 的新特性

隨著 JavaScript 的不斷演進&#xff0c;ECMAScript 2015&#xff08;簡稱 ES6&#xff09;作為 JavaScript 的一次重大更新&#xff0c;帶來了許多新特性和語法改進&#xff0c;極大地提升了開發體驗和代碼質量。本文將詳細介紹 ES6 的主要新特性&#xff0c;并展示如何在實際…

NLTK:原理與使用詳解

文章目錄 NLTK簡介NLTK的核心功能1. 文本處理2. 詞匯處理3. 語法分析4. 語義分析5. 情感分析 NLTK的使用1. 安裝NLTK2. 導入NLTK庫3. 下載NLTK數據集4. 文本處理示例5. 情感分析示例 總結 NLTK簡介 NLTK是一個開源的Python庫&#xff0c;用于處理和分析人類語言數據。它提供了…

扛鼎中國AI搜索,天工憑什么?

人類的創作不會沒有瓶頸&#xff0c;但AI的熱度可不會消停。 大模型之戰依舊精彩&#xff0c;OpenAI選擇在Google前一天舉行發布會&#xff0c;兩家AI企業之間的拉扯賺足了熱度。 反觀國內&#xff0c;百模大戰激發了大家對于科技變革的熱切期盼&#xff0c;而如今行業已逐漸…

【操作系統期末速成】 EP01 | 學習筆記(基于五道口一只鴨)

文章目錄 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;??????1.1 考點一&#xff1a;操作系統的概率及特征 三、總結&#xff1a;&#x1f353;&#x1f353;&#x1f353; 一、前言&#x1f680;&#x1f680;&#x1f680; ?? 回報不在行動…

文章浮現之單細胞VDJ的柱狀圖

應各位老師的需求復現一篇文章的中的某個圖 具體復現圖5的整個思路圖&#xff0c;這里沒有原始數據&#xff0c;所以我使用虛擬生產的metadata進行畫圖 不廢話直接上代碼&#xff0c;先上python的代碼的結果圖 import matplotlib.pyplot as plt import numpy as np# 數據&#…

架構師篇-8、運用事件風暴進行業務領域建

如何成為優秀架構師&#xff1f; 需要有一定的技術積累&#xff0c;但是核心是懂業務。 具備一定的方法&#xff0c;并且有很強的業務理解能力。 技術架構師&#xff1a;形成技術方案&#xff0c;做的更多的是底層的平臺&#xff0c;提供工具。 業務架構師&#xff1a;解決方…

兩數之和你會,三數之和你也會嗎?o_O

前言 多少人夢想開始的地方&#xff0c;兩數之和。 但是今天要聊的不是入門第一題&#xff0c;也沒有面試官會考這一題吧…不會真有吧&#xff1f; 咳咳不管有沒有&#xff0c;今天的豬腳是它的兄弟&#xff0c;三數之和&#xff0c;作為雙指針經典題目之一&#xff0c;也是常…

Vue3中Element Plus組件庫el-eialog彈框中的input無法獲取表單焦點的解決辦法

以下是vue.js官網給出的示例 <script setup> import { ref, onMounted } from vue// 聲明一個 ref 來存放該元素的引用 // 必須和模板里的 ref 同名 const input ref(null)onMounted(() > {input.value.focus() }) </script><template><input ref&qu…

如何在Vue3項目中使用Pinia進行狀態管理

**第一步&#xff1a;安裝Pinia依賴** 要在Vue3項目中使用Pinia進行狀態管理&#xff0c;首先需要安裝Pinia依賴。可以使用以下npm命令進行安裝&#xff1a; bash npm install pinia 或者如果你使用的是yarn&#xff0c;可以使用以下命令&#xff1a; bash yarn add pinia *…

Tomcat的安裝和虛擬主機和context配置

一、 安裝Tomcat 注意&#xff1a;安裝 tomcat 前必須先部署JDK 1. 安裝JDK 方法1&#xff1a;Oracle JDK 的二進制文件安裝 [rootnode5 ~]# mkdir /data [rootnode5 ~]# cd /data/ [rootnode5 data]# rz[rootnode5 data]# ls jdk-8u291-linux-x64.tar.gz [rootnode5 data]…

C++:std::function的libc++實現

std::function是個有點神奇的模板&#xff0c;無論是普通函數、函數對象、lambda表達式還是std::bind的返回值&#xff08;以上統稱為可調用對象&#xff08;Callable&#xff09;&#xff09;&#xff0c;無論可調用對象的實際類型是什么&#xff0c;無論是有狀態的還是無狀態…

【C++】string基本用法(常用接口介紹)

文章目錄 一、string介紹二、string類對象的創建&#xff08;常見構造&#xff09;三、string類對象的容量操作1.size()和length()2.capacity()3.empty()4.clear()5.reserve()6.resize() 四、string類對象的遍歷與訪問1.operator[ ]2.正向迭代器begin()和end()3.反向迭代器rbeg…

QTableView與QSqlQueryModel的簡單使用

測試&#xff1a; 這里有一個sqlite數據庫 存儲了10萬多條數據&#xff0c;col1是1,col2是2. 使用QSqlQueryModel和QTableView來顯示這些數據&#xff0c;也非常非常流暢。 QString aFile QString::fromLocal8Bit("E:/桌面/3.db");if (aFile.isEmpty())return;//打…

關于考摩托車駕照

剛通過了摩托車駕照考試&#xff0c;說兩句。 1、在哪兒考試就要搞清楚當地的規定&#xff0c;不要以為全國要求都一樣。 2、首先是報駕校。雖然至少有些地方允許自學后&#xff08;不報駕校&#xff09;考試&#xff0c;但報駕校聽聽教練說的&#xff0c;還是能提高通過率&a…