Springboot中sharding-jdbc的API模式并使用自定义算法

news2025/4/8 18:10:02

Springboot中sharding-jdbc的API模式并使用自定义算法

可配合AbstractRoutingData使用切换数据源

程序用到了AbstractRoutingData来切换数据源(数据源是自定义的格式编写并没有用springboot的自动装配的格式写),但是又用到sharding-jdbc进行分库分页,如果直接引用sharding-jdbc-spring-boot-starter会自动装配它自己默认的数据源dataSource,导致我们自己写的数据源失效。所以我们需要用API的模式把sharding-jdbc的数据源dataSource放入我们自己的写的AbstractRoutingData里面来。


POM文件添加


<dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core</artifactId>
            <version>5.1.2</version>
 </dependency>

自定义的数据库信息格式

在这里插入图片描述


使用AbstractRoutingData切换数据源


public class DynamicDataSource extends AbstractRoutingDataSource {
	private static final Logger logger = Logger.getLogger(DynamicDataSource.class);
 
    private static DynamicDataSource dynamicDataSource;
  
    private final Map<Object, Object> targetDataSources = new HashMap();
    
    private static final ThreadLocal<String> dataSourceName = new ThreadLocal();

    public DynamicDataSource() {
    }

    public static DynamicDataSource getInstance() {
        if (dynamicDataSource == null) {
            synchronized(DynamicDataSource.class) {
                if (dynamicDataSource == null) {
                    dynamicDataSource = new DynamicDataSource();
                }
            }
        }
        return dynamicDataSource;
    }
	/**
	*determineCurrentLookupKey() 方法决定使用哪个数据源
	*/
    protected Object determineCurrentLookupKey() {
        return (String)dataSourceName.get();
    }


    public void setTargetDataSources(Map<String, DataSource> targetDataSources) {
    	//设置默认数据源
    	//super.setDefaultTargetDataSource(targetDataSources.get("default"));
        this.targetDataSources.putAll(targetDataSources);
        //设置数据源
        super.setTargetDataSources(this.targetDataSources);
        super.afterPropertiesSet();
    }

    public Map<Object, Object> getTargetDataSources() {
        return this.targetDataSources;
    }

    public void removeDataSource(String code) {
        if (this.targetDataSources.get(code) != null) {
            this.targetDataSources.remove(code);
        }
        //重新设置数据源
        super.setTargetDataSources(this.targetDataSources);
        super.afterPropertiesSet();
    }
    
    public static void setDataSource(String datasource) {
        logger.info("切换数据源为:"+datasource);
        dataSourceName.set(datasource);
    }
    
	public static void clear() {
        dataSourceName.remove();
    }

}
  • 数据源是自定义的,要禁用springboot的数据源自动装配配置,启动类上加上
 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

写入自己的自定义数据源


@Configuration
public class ShardingDataSourceConfig{
    Logger logger = Logger.getLogger(ShardingDataSourceConfig.class);

    @Primary
    @Bean
    public DataSource shardingdataSource() throws SQLException, IOException {
    	//获取AbstractRoutingData对象
        DynamicDataSource chooseDataSource = DynamicDataSource.getInstance();
        //获取自己配置文件上的普通数据源,该方法忽略展示,key为数据库的名字,value为数据源
        Map<String, DataSource> targetDataSources = this.getTargetDataSources();
        /*生成数据源的样式,使用DruidDataSource,POM文件记得加入,也可以使用其他数据源
   		DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(stringStringMap.get("driverClassName"));
        dataSource.setUrl(stringStringMap.get("url"));
        dataSource.setUsername(stringStringMap.get("username"));
        dataSource.setPassword(stringStringMap.get("password"));
        */
        //设置默认的数据源,必须保证Map里面有该值,可以放在DynamicDataSource里面再设置
        chooseDataSource.setDefaultTargetDataSource(targetDataSources.get("default"));
        //添加自己的sharding-jdbc数据源
        //分库分表数据源
        DataSource shardingDataSource = ShardingDataBaseConfiguration.getDataSource(shardingPrefixss);
    	targetDataSources.put("shardingDT",shardingDataSource);
        //只分表数据源
        DataSource dataSource = ShardingTableConfiguration.getDataSource(sourceDataBase);
            targetDataSources.put("shardingT",dataSource);
        }
        chooseDataSource.setTargetDataSources(targetDataSources);
        return chooseDataSource;
    }
}

获取配置文件辅助类,网上很多方法,这里使用的是继承PropertyPlaceholderConfigurer类


public class PropertyPlaceholder extends PropertyPlaceholderConfigurer {

    private static Map<String,String> propertyMap;

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
        super.processProperties(beanFactoryToProcess, props);
        propertyMap = new HashMap<String, String>();
        for (Object key : props.keySet()) {
            String keyStr = key.toString();
            String value = props.getProperty(keyStr);
            propertyMap.put(keyStr, value);
        }
    }

    //自定义一个方法,即根据key拿属性值,方便java代码中取属性值
    public static String getProperty(String name) {
        return propertyMap.get(name);
    }
}

定义自己的sharding分片规则,并返回sharding的数据源.

分库分表配置

public class ShardingDataBaseConfiguration {
    /**
     * 创建数据源
     */
    private static Map<String, DataSource> createDataSourceMap(List<String> datasourceNames){
        Map<String, DataSource> dataSourceMap=new HashMap<>();
        for (int i = 0; i < datasourceNames.size(); i++) {
            Map<String, String> stringStringMap = DataBaseInfoUtil.getDataBaseInformation().get(datasourceNames.get(i));
            if (ObjectUtil.isNull(stringStringMap)){
                return null;
            }
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(stringStringMap.get("driverClassName"));
            dataSource.setUrl(stringStringMap.get("url"));
            dataSource.setUsername(stringStringMap.get("username"));
            dataSource.setPassword(stringStringMap.get("password"));
            dataSourceMap.put("ds_"+datasourceNames.get(i), dataSource);
        }
        return dataSourceMap;
    }

    /**
     * 分库分表设置
     * create_time为分库的字段,按create_time字段的值来进行分库计算
     * HashModShardingAlgorithm.class.getName()是算法名字,可随便写,需要和分表配置的算法名字对应上就行
     * MY_HASH_MOD、MY_COMPLEX_INLINE、DATABASE_INLINE自定义算法的名字,最重要的地方,必须和自定义算法类中返回的名字一致,就是getType()返回的值,名字可以随意取
     * */
    private static ShardingRuleConfiguration createShardingRuleConfiguration() {
        ShardingRuleConfiguration configuration = new ShardingRuleConfiguration();
        configuration.getTables().add(getWlbTableRuleConfiguration());
        configuration.getTables().add(getWiorpTableRuleConfiguration());
        //设置分库的规则,按年份分库
        configuration.setDefaultDatabaseShardingStrategy(new StandardShardingStrategyConfiguration("create_time", PreciseDatabaseShardingAlgorithm.class.getName()));
        configuration.getShardingAlgorithms().put(HashModShardingAlgorithm.class.getName(),new ShardingSphereAlgorithmConfiguration("MY_HASH_MOD",new Properties()));
        configuration.getShardingAlgorithms().put(ComplexShardingAlgorithm.class.getName(),new ShardingSphereAlgorithmConfiguration("MY_COMPLEX_INLINE",new Properties()));
        configuration.getShardingAlgorithms().put(PreciseDatabaseShardingAlgorithm.class.getName(),new ShardingSphereAlgorithmConfiguration("DATABASE_INLINE",new Properties()));
        return configuration;
    }



    /**
     * 制定my_test表分片规则
     * my_test为逻辑表名,my_test_0,my_test_1....my_test_9为实际数据库的表名,就是把数据分到这0-9的表中
     * ds_${2020..2022} 为实际数据源的名字:ds_2020,ds_2021,ds_2022,写法${..},{}里面可以进行运算,例如ds_${id % 2}
     * sub_base为my_test表的分表字段,就是my_test表的分表规则按sub_base来区分
     * HashModShardingAlgorithm.class.getName(),这个是算法的名字可以随意起,对应configuration.getShardingAlgorithms().put()中key的值,写上自己自定义的类名好容易确认区分,sharding-jdbc也有自己默认定义好的分片算法
     * 如果使用ds_${id % 2}这种在{}进行运算的,可以不写setTableShardingStrategy
     * */
    private static ShardingTableRuleConfiguration getWlbTableRuleConfiguration(){
        ShardingTableRuleConfiguration tableRule=new ShardingTableRuleConfiguration("my_test","ds_${2020..2022}.my_test_${0..9}");
        tableRule.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("id","snowflake"));
        tableRule.setTableShardingStrategy(new StandardShardingStrategyConfiguration("sub_base",HashModShardingAlgorithm.class.getName()));
        return tableRule;
    }

    /**
     * 制定my_test2表分库分片规则
     * */
    private static ShardingTableRuleConfiguration getWiorpTableRuleConfiguration(){
        ShardingTableRuleConfiguration tableRule=new ShardingTableRuleConfiguration("my_test2","ds_${2020..2022}.my_test2_${0..9}");
        tableRule.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("id","snowflake"));
        tableRule.setTableShardingStrategy(new ComplexShardingStrategyConfiguration("code,name,sex,age", ComplexShardingAlgorithm.class.getName()));
        return tableRule;
    }


    public static DataSource getDataSource(List<String> datasourceNames) throws SQLException {
        // 其他配置
        Properties properties = new Properties();
        //控制台日志展示sharding-jdbc的sql
        properties.put("sql-show","true");
        return ShardingSphereDataSourceFactory.createDataSource(createDataSourceMap(datasourceNames),
                Collections.singleton(createShardingRuleConfiguration()),properties);
    }


}

仅分表配置

public class ShardingTableConfiguration {
    /**
     * 创建数据源
     */
    private static Map<String, DataSource> createDataSourceMap(List<String> datasourceNames){
        Map<String, DataSource> dataSourceMap=new HashMap<>();
        for (int i = 0; i < datasourceNames.size(); i++) {
            Map<String, String> stringStringMap = DataBaseInfoUtil.getDataBaseInformation().get(datasourceNames.get(i));
            if (ObjectUtil.isNull(stringStringMap)){
                return null;
            }
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(stringStringMap.get("driverClassName"));
            dataSource.setUrl(stringStringMap.get("url"));
            dataSource.setUsername(stringStringMap.get("username"));
            dataSource.setPassword(stringStringMap.get("password"));
            dataSourceMap.put("ds0", dataSource);
        }
        return dataSourceMap;
    }

    /**
     * 分表设置
     */
    private static ShardingRuleConfiguration createShardingRuleConfigurationOnlyTable() {
        ShardingRuleConfiguration configuration = new ShardingRuleConfiguration();
        configuration.getTables().add(getWlbTableRuleConfiguration());
        configuration.getTables().add(getWiorpTableRuleConfiguration());
        configuration.getShardingAlgorithms().put(HashModShardingAlgorithm.class.getName(),new ShardingSphereAlgorithmConfiguration("MY_HASH_MOD",new Properties()));
        configuration.getShardingAlgorithms().put(ComplexShardingAlgorithm.class.getName(),new ShardingSphereAlgorithmConfiguration("MY_COMPLEX_INLINE",new Properties()));
        return configuration;
    }


    /**
     * 制定my_test3表分片规则
     */
    private static ShardingTableRuleConfiguration getWlbTableRuleConfiguration(){
        ShardingTableRuleConfiguration tableRule=new ShardingTableRuleConfiguration("my_test3","ds0.my_test3_${0..9}");
        tableRule.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("id","snowflake"));
        tableRule.setTableShardingStrategy(new StandardShardingStrategyConfiguration("box_batch",HashModShardingAlgorithm.class.getName()));
        return tableRule;
    }

    /**
     * 制定my_test4表分库分片规则
     */
    private static ShardingTableRuleConfiguration getWiorpTableRuleConfiguration(){
        ShardingTableRuleConfiguration tableRule=new ShardingTableRuleConfiguration("my_test4","ds0.my_test4_${0..9}");
        tableRule.setKeyGenerateStrategy(new KeyGenerateStrategyConfiguration("id","snowflake"));
        tableRule.setTableShardingStrategy(new ComplexShardingStrategyConfiguration("code,name,sex,age", ComplexShardingAlgorithm.class.getName()));
        return tableRule;
    }


    public static DataSource getDataSource(String datasourceNames) throws SQLException {
        // 其他配置
        Properties properties = new Properties();
        properties.put("sql-show","true");
        return ShardingSphereDataSourceFactory.createDataSource(createDataSourceMap(new ArrayList<String>(){{add(datasourceNames);}}),
                Collections.singleton(createShardingRuleConfigurationOnlyTable()),properties);
    }


}

自定义分库分片算法

标准分片算法

public final class HashModShardingAlgorithm implements StandardShardingAlgorithm<String> {
    
   
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<String> shardingValue) {
        if(StringUtil.isEmpty(shardingValue.getValue())){
            throw new CommonException("precise sharding value is null");
        }
        String suffix = String.valueOf(Math.abs((long) shardingValue.hashCode())) % collection.size());
        for (String tableName : collection) {
            if (tableName.endsWith(suffix)) {
                return tableName;
            }
        }
        throw new UnsupportedOperationException();
    }


    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
        return collection;
    }

    @Override
    public Properties getProps() {
        return null;
    }

    @Override
    public void init(Properties properties) {

    }

	//返回的算法名字
    public String getType() {
        return "MY_HASH_MOD";
    }
}

复合字段算法

public class ComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm {

    @Override
    public Collection<String> doSharding(Collection collection, ComplexKeysShardingValue complexKeysShardingValue) {
        // 返回真实表名集合
        List<String> tableNameList = new ArrayList<>();

        // 逻辑表名
        String logicTableName = complexKeysShardingValue.getLogicTableName();


        // 获取分片键的值,算法自己定义
        Collection<String> factoryCodes = (Collection<String>) complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("code");
        Collection<String> workshopCodes = (Collection<String>) complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("name");
        Collection<String> storehouseCodes = (Collection<String>) complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("sex");
        Collection<String> materialNos = (Collection<String>) complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("age");

        if (ListUtil.isEmpty(factoryCodes)
                || ListUtil.isEmpty(workshopCodes)
                || ListUtil.isEmpty(storehouseCodes)
                || ListUtil.isEmpty(materialNos)) {//分片键缺任何一个字段均返回全部表
            for (String tableName : (Collection<String>) collection) {
                tableNameList.add(tableName);
            }
            return tableNameList;//返回全部
        }

        // 获取真实表名
        String realName = getTabel(factoryCodes) + getTabel(workshopCodes)+ getTabel(storehouseCodes);
        for (String materialNo : materialNos) {
            long abs = Math.abs((long) (realName + materialNo).hashCode());
            String tableSuffix = String.valueOf(abs % 10);
            for (String tableName : (Collection<String>) collection) {
                if (tableName.endsWith("_" + tableSuffix)) {
                    tableNameList.add(tableName);
                }
            }
        }
        return tableNameList;
    }

    /**
     * 获取表名
     *
     * @param codes
     * @return
     */
    private String getTabel(Collection<String> names) {
        Optional<String> name = names.stream().findFirst();
        if (name.isPresent()) {
            return name.get();
        }
        return "";
    }

    @Override
    public Properties getProps() {
        return null;
    }

    @Override
    public void init(Properties properties) {

    }
    
	//返回的算法名字
    public String getType() {
        return "MY_COMPLEX_INLINE";
    }
}

分库算法

public class PreciseDatabaseShardingAlgorithm implements StandardShardingAlgorithm<LocalDateTime> {
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<LocalDateTime> preciseShardingValue) {
        //对于库的分片collection存放的是所有的库的列表,这里代表ds_2020~dataSource_2022
        //配置的分片的sharding-column对应的值
        LocalDateTime year = preciseShardingValue.getValue();
        if(ObjectUtil.isNull(year)){
            throw new UnsupportedOperationException("preciseShardingValue is null");
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy");
        //按年路由
        for (String each : collection) {
            String value = formatter.format(year);//获取到年份
            if(each.endsWith(value)){
                // //这里返回回去的就是最终需要查询的库名
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }


    @Override
    public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<LocalDateTime> rangeShardingValue) {
        return collection;
    }

    @Override
    public Properties getProps() {
        return null;
    }

    @Override
    public void init(Properties properties) {

    }

	//返回算法的名字
    public String getType() {
        return "DATABASE_INLINE";
    }
}

自定义算法重点

SPI机制
需要在resources下面写上
META-INF.services.org.apache.shardingsphere.sharding.spi.ShardingAlgorithm
在这里插入图片描述
里面的内容写上算法的路径
在这里插入图片描述

使用
在需要切换数据源的地方设置数据源DynamicDataSource.setDataSource(自定义的数据源名字),使用完后记得remove,切换为默认数据源避免出问题
事务
必须在切换数据源后才开启事务,单事务,在事务中切换数据源是不生效的

PS:写出来仅仅为了自己后面能重新看到,如果有好的方法也可以告诉我

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

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

相关文章

pycharm添加虚拟环境以及虚拟环境安装pytorch

file、settings、interpreter、add interpreter、add local interpreter 记住不要勾选inherit&#xff0c;不然会把主环境的东西继承到虚拟环境。 创建前可以先点existing看看有没有已经建好的虚拟环境 有的时候pycharm有问题&#xff0c;创建了虚拟环境没有显示。找一个.py文…

交通网络分析性能再升级,SuperMap iServer新增开启SSC分析模型

导语 SSC分析模型&#xff0c;全名SuperMap Short Cut&#xff0c;底层采用Contraction Hierarchies&#xff08;简称CH&#xff09;算法&#xff0c;该算法旨在通过对图形进行预处理和优化来降低最佳路径分析的时间复杂度。SuperMap iServer 11i&#xff08;2023&#xff09;&…

scikit-learn中OneHotEncoder用法

One-Hot编码&#xff0c;又称为一位有效编码&#xff0c;是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值&#xff0c;然后&#xff0c;每个整数值被表示为二进制向量&#xff0c;将整数索引标记为1&#xff0c;其余都标为0。 OneHotEncoder()常用参数解释 …

【Winform学习笔记(十)】TextBox文本框控件 光标定位到文本末尾的方法

TextBox文本框控件 光标定位到文本末尾的方法 前言正文1、具体代码2、使用示例 前言 本文中主要介绍实现 TextBox 文本框控件的光标定位到文本末尾的方法. 正文 1、具体代码 /// <summary> /// TextBox文本框控件 光标定位到文本末尾的方法 /// </summary> ///…

next.js报错点

next.js报错点 1.类型“{ children: ReactNode; }”与类型“IntrinsicAttributes”不具有相同的属性。2. 不能将类型“void[]”分配给类型“ReactNode”&#xff1f;3.useRouter only works in Client Components. Add the "use client" directive at the top of the…

IDEA的maven想显示层级关系,而非平级

新版和旧版的IDEA的位置不一样&#xff0c;2023.2.1的版本在右上角的“” 这个位置 如图所示&#xff1a; 然后点击按模块分组&#xff1a;

[Stable Diffusion教程] 第一课 原理解析+配置需求+应用安装+基本步骤

第一课 原理解析配置需求应用安装基本步骤 本次内容记录来源于B站的一个视频 以下是自己安装过程中整理的问题及解决方法&#xff1a; 问题&#xff1a;stable-diffusion-webui启动No Python at ‘C:\xxx\xxx\python.exe‘ 解答&#xff1a;打开webui.bat 把 if not de…

从零开始的自动化测试框架——Web篇01

Selenium 谈到web自动化&#xff0c;逃不开的一定会是Selenium。这是最为主流&#xff0c;也是最广为人知的一项web自动化产物。但目前业内web自动化其实主要分为以下方向&#xff1a; Selenium&#xff0c;核心主流自动化技术&#xff0c;功能齐全&#xff0c;一般是搭配web…

【算法日志】动态规划刷题:01背包问题,多重背包问题(day37,day38)

代码随想录刷题60Day 目录 前言 目标和&#xff08;01背包&#xff09; 一和零&#xff08;01背包&#xff09; 零钱兑换&#xff08;多重背包&#xff09; 排列总和&#xff08;多重背包&#xff09; 前言 这两天都是背包问题&#xff0c;其中的01背包的一些应用问题需要…

迈步从头越——同为科技(TOWE)22周年庆典活动圆满举行

8月23日&#xff0c;同为科技&#xff08;TOWE&#xff09;迎来公司成立22周年纪念日&#xff0c;这是属于TOWE品牌的又一里程碑时刻。从2001到2023&#xff0c;从产品到品牌&#xff0c;从技术到服务&#xff0c;同为科技&#xff08;TOWE&#xff09;始终秉承着专注于终端设备…

电子合同9问9答,君子签为您答疑解惑

随着互联网的发展&#xff0c;人们交易行为的改变、电子合同的推广&#xff0c;买卖合同、租房合同、采购合同、劳动合同、招投标文件等都开始在线上签署。签署过程中&#xff0c;君子签针对大家的一些疑虑&#xff0c;整理了相关解答。 1、 签电子合同时&#xff0c;如何确…

MMEdu库的下载和安装(Python版)

为保证程序的顺利运行&#xff0c;这里提供MMEdu库的下载安装包。 OpenXLabEdu开源资源&#xff1a;OpenXLabEdu帮助文档 — OpenXLabEdu 文档 现MMEdu一键安装包已升级为XEdu一键安装包&#xff0c;下文提到的MMEdu文件&#xff0c;应理解为XEdu一键安装包。 第一步&#x…

融合算法综述

融合算法 前言一、概念二、原理三、融合的先决条件四、融合分类4.1、前融合和后融合4.2 、数据级融合、特征级融合和决策级融合 五、典型融合算法 多传感器信息融合&#xff08;Multi-sensor Information Fusion,MSIF&#xff09;&#xff1a;利用计算机技术将来自多传感器或多…

小红书旅游种草笔记:如何打造爆款内容,吸引万千粉丝?

随着社交媒体的飞速发展&#xff0c;小红书作为国内知名的分享平台&#xff0c;已经成为越来越多人展示生活、交流心得的阵地。在众多分类中&#xff0c;旅游种草笔记尤为热门&#xff0c;吸引了大量用户关注。那么&#xff0c;如何在这片竞争激烈的红海中&#xff0c;打造出爆…

Spring Boot:让你轻松掌握自动装配的奥秘

Spring Boot是基于Spring框架开发的一种应用框架&#xff0c;它通过自动装配机制&#xff0c;大大简化了Spring应用的开发和部署&#xff0c;使开发者可以更加专注于业务逻辑的实现&#xff0c;而无需过多关注Bean的实例化和装配过程。本文将从以下几个方面介绍Spring Boot的自…

计算机竞赛 基于RSSI的室内wifi定位系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; wifi室内定位系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;…

掌握这20条你将超过90%的测试员

1、不断学习 不管是“软技能”&#xff0c;比如公开演讲&#xff0c; 或者编程语言&#xff0c;亦或新的测试技术&#xff0c;成功的软件测试工程师总是会从繁忙中抽出时间来坚持学习。 2、管理你的时间 我们的时间很容易被大块的工作和不断的会议所占据&#xff0c;导致我们…

数据库mysql导入导出数据需要注意的问题,作者只有600~800个表的处理经验

文章目录 一、新建数据库的时候编码和排序规则尽量和原数据库完全一样二、表结构和表数据分开导入&#xff0c;不要同时导入Navicat导出表结构Navicat导入表结构Navicat导出表数据Navicat导入表数据 三、编码问题四、执行顺序的问题五、迁移数据大小和速度的问题六、数据库分区…

element-ui中的el-table的summary-method(合计)的使用

场景图片&#xff1a; 图片1&#xff1a; 图片2&#xff1a; 一&#xff1a;使用element中的方法 优点&#xff1a; 直接使用summary-method方法&#xff0c;直接&#xff0c;方便 缺点&#xff1a; 只是在表格下面添加了一行&#xff0c;如果想有多行就不行了 1&#xff1a;h…

成都瀚网科技:抖店如何经营?

作为热门的短视频分享平台&#xff0c;抖音不仅是一种娱乐工具&#xff0c;更是一个蕴藏着无限商机的电商平台。开店、抖音下单成为很多人的选择。那么&#xff0c;抖音如何开店、下单呢&#xff1f; 1、如何在抖音上开店和下单&#xff1f; 注册账号&#xff1a;首先&#xff…