如何在 Spring Boot 项目中配置多数据源,基于自定义注解 `@TargetDataSource` 实现数据库动态切换,适用于分库分表、读写分离等场景

望舒的头像
望舒
标签:
springboot动态数据源多数据源数据源切换java

本文详细介绍如何在 Spring Boot 项目中配置多数据源,基于自定义注解 @TargetDataSource 实现数据库动态切换,适用于分库分表、读写分离等场景。

一、基础配置:多数据源 YAML 配置示例

application.yml 中定义多个数据源:

复制
spring:
  datasource:
    dynamic:
      defaultName: default
      dataSource:
        - name: default
          driver: com.mysql.cj.jdbc.Driver
          type: com.alibaba.druid.pool.DruidDataSource
          url: jdbc:mysql://localhost:3306/database
          userName: xxx
          password: xxx
        - name: two
          driver: com.mysql.cj.jdbc.Driver
          type: com.alibaba.druid.pool.DruidDataSource
          url: jdbc:mysql://localhost:3306/database-2
          userName: xxx
          password: xxx

二、数据源模型实体类 DataSourceModel

用于创建具体的数据源实例:

复制
@Data
public class DataSourceModel {
    private String name;
    private String driver;
    private String url;
    private String userName;
    private String password;
    private Class<? extends DataSource> type = com.alibaba.druid.pool.DruidDataSource.class;

    public DataSource createDataSource() {
        return DataSourceBuilder.create()
            .driverClassName(this.driver)
            .url(this.url)
            .username(this.userName)
            .password(this.password)
            .type(this.type)
            .build();
    }
}

三、继承 AbstractRoutingDataSource 实现动态数据源切换

核心类:DynamicDataSource

复制
public class DynamicDataSource extends AbstractRoutingDataSource {
    private String defaultKey = null;

    @Override
    protected Object determineCurrentLookupKey() {
        if (StringUtil.isEmpty(DataSourceHolder.getDataSourceName())) {
            return defaultKey;
        }
        return DataSourceHolder.getDataSourceName();
    }

    public DynamicDataSource(String defaultKey, DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        this.defaultKey = defaultKey;
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
}

四、使用 ThreadLocal 储存当前数据源名称

DataSourceHolder 工具类:

复制
public class DataSourceHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void setDataSourceName(String name) {
        CONTEXT_HOLDER.set(name);
    }

    public static String getDataSourceName() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSourceName() {
        CONTEXT_HOLDER.remove();
    }
}

五、加载配置的所有数据源并设置默认源

DataSourceProperties 配置类:

复制
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
public class DataSourceProperties {

    private List<DataSourceModel> dataSource = new ArrayList<>();
    private String defaultDataSourceName = "default";

    public DataSourceModel getDefaultDataSourceModel() {
        return this.dataSource.stream()
            .filter(item -> StringUtil.isEqual(item.getName(), this.defaultDataSourceName))
            .findFirst()
            .orElse(null);
    }

    public DataSource getDefaultDataSource() {
        DataSourceModel model = getDefaultDataSourceModel();
        return model != null ? model.createDataSource() : null;
    }

    public Map<Object, Object> getTargetDataSource() {
        Map<Object, Object> map = new HashMap<>();
        dataSource.forEach(ds -> map.put(ds.getName(), ds.createDataSource()));
        return map;
    }
}

六、自定义数据源注解 @TargetDataSource

支持方法级或类级切换数据源:

复制
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
    @AliasFor("name") String value() default "default";
    @AliasFor("value") String name() default "default";
}

七、注解切面 AOP:自动切换数据源

核心切面 TargetDataSourceAop 实现自动切换逻辑:

复制
@Aspect
@Component
@Slf4j
public class TargetDataSourceAop {

    @Pointcut("@annotation(com.xxx.config.datasource.TargetDataSource)")
    public void pointCut() {}

    @Before("pointCut()")
    public void before(JoinPoint joinPoint) {
        TargetDataSource targetDataSource = null;
        Class<?> targetClass = joinPoint.getTarget().getClass();
        if (targetClass != null) {
            targetDataSource = targetClass.getAnnotation(TargetDataSource.class);
        }
        if (targetDataSource == null) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            targetDataSource = signature.getMethod().getAnnotation(TargetDataSource.class);
        }

        DataSourceHolder.setDataSourceName(targetDataSource.value());
        log.info("切换数据源: {}", targetDataSource.value());
    }

    @After("pointCut()")
    public void after(JoinPoint joinPoint) {
        DataSourceHolder.clearDataSourceName();
        log.info("清空数据源");
    }
}

八、总结

通过以上配置与注解切面机制,你可以轻松实现:

  • Spring Boot 多数据源支持
  • 动态切换数据源逻辑
  • 注解式或类级别数据源指定
  • 可扩展至读写分离、分库分表、主从配置场景

作者:https://blog.xn--rpv331d.com/望舒

链接:https://blog.xn--rpv331d.com/望舒/blog/19

转载注意保留文章出处...

‌‌‌‌‌‌​‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌​​‌​‌​‌‌​​​​​​‌‌‌‌​‌​‌‌‌‌​​‌‌‌‌​‌​​​‌​​‌​‌‌​​​‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌​​‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌​‌‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌‌​‌​‌​‌‌​‌‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌​‌​‌​​‌‌‌‌​‌​‌‌‌​​‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌‌​‌‌‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌​​‌​‌‌‌‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌​‌‌‌​​‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌‌​‌​‌​‌‌​‌‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌​‌​‌​​‌‌‌‌​‌​‌‌‌​​‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌​​‌​​‌​‌​‌‌​‌​‌​‌‌​‌‌​‌​​‌​​​‌​​​‌​​​‌​​‌​‌‌​​​‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌​​‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌​‌‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌‌​‌​‌​‌‌​‌‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌​‌​‌​​‌‌‌‌​‌​‌‌‌​​‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌‌‌‌‌‌‌‌​‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌‌​‌‌‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌​​‌​‌‌‌‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌​‌‌‌​​‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌‌​‌​‌​‌‌​‌‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌​‌‌‌‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌​‌​‌​​‌‌‌‌​‌​‌‌‌​​‌‌‌‌‌‌‌‌‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌‌​‌‌‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌‌‌‌‌‌‌‌‌‌​‌​‌​‌‌‌‌‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌​‌‌​‌​‌‌​‌‌‌​‌‌‌​‌‌​‌‌‌‌‌‌‌‌​‌‌‌​​‌​‌​​‌​‌‌‌​‌‌‌​‌‌​‌​‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌​‌‌‌‌‌‌​‌‌‌‌‌‌‌‌‌‌‌‌‌​‌​‌​​‌‌‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌​‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌​‌‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌​​‌‌‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌​‌‌​‌‌‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌‌​​​​​​‌​​​​​​‌‌‌​​​​‌​‌​​​​​‌‌‌‌​‌‌​​​​​​​‌‌​​​‌‌‌‌‌​​​‌‌‌​​‌​‌‌​​‌​‌​‌‌​​​​‌​​​​‌‌​‌​​‌‌​‌​‌​‌‌‌‌‌​​‌​‌‌‌​​‌​​​​​‌‌​‌​​​​‌‌​​​​​‌‌‌‌​‌​‌‌‌​​​​​‌‌‌‌​‌‌‌​​​​​​‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌​‌‌‌‌‌‌‌‌​‌​‌​​‌​‌‌‌‌‌‌‌​​‌​‌‌​‌‌‌‌‌​‌‌‌‌‌‌​‌​‌‌​‌‌‌‌‌‌‌‌​‌​‌‌​‌​‌‌
2
1
89
No data