Spring Boot整合mybatis,多个数据源动态切换

导入依赖,配置

依赖

<!-- mybatis -->
		<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		<!-- 分页插件 -->
		<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter -->
		<dependency>
			<groupId>com.github.pagehelper</groupId>
			<artifactId>pagehelper-spring-boot-starter</artifactId>
			<version>1.2.12</version>
		</dependency>
		<!-- mybatis-plus注解依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> 
			<version>3.0.1</version> </dependency> -->
		<!-- sql server 驱动连接 -->
		<!-- https://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc -->
		<dependency>
			<groupId>com.microsoft.sqlserver</groupId>
			<artifactId>mssql-jdbc</artifactId>
			<version>7.0.0.jre8</version>
			<scope>test</scope>
		</dependency>

		<!-- mysql -->
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.15</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.microsoft.sqlserver/sqljdbc4 -->
		<dependency>
			<groupId>com.microsoft.sqlserver</groupId>
			<artifactId>sqljdbc4</artifactId>
			<version>4.0</version>
			<scope>system</scope>
			<systemPath>${project.basedir}/libs/sqljdbc4-4.0.jar</systemPath>
		</dependency>
		<!-- 根据实体类生成表 -->
		<dependency>
			<groupId>com.gitee.sunchenbin.mybatis.actable</groupId>
			<artifactId>mybatis-enhance-actable</artifactId>
			<version>1.0.8.1.RELEASE</version>
		</dependency>

配置

server:
 port: 8002
 tomcat:
         #格式
   uri-encoding: UTF-8
         #最大
   max-threads: 1000
        #最小
   min-spare-threads: 30
# 别称  
tcmtown-datasource:
   driver-class-name: com.mysql.cj.jdbc.Driver
   url: jdbc:mysql://localhost:3306/tcmtown?characterEncoding=UTF8&serverTimezone=UTC&serverTimezone=GMT%2b8&useSSL=false
   username: root
   password: root
     
#别称
health-datasource:
   driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
   url: jdbc:sqlserver://localhost:1433;DatabaseName=health_tcm360_DB;serverTimezone=GMT%2b8;useSSL=false
   username: root
   password: root
   
#别称
culture-datasource:
   url: jdbc:sqlserver://localhost:1433;DatabaseName=culture_tcm360_DB;serverTimezone=GMT%2b8;useSSL=false
   username: root
   password: root
   driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver

#别称
herbal-datasource:
   url: jdbc:sqlserver://localhost:1433;DatabaseName=herbal_tcm360_DB;serverTimezone=GMT%2b8;useSSL=false
   username: root
   password: root
   driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
   
#别称
lnyby-datasource:
   url: jdbc:sqlserver://localhost:1433;DatabaseName=lnyby_DB;serverTimezone=GMT%2b8;useSSL=false
   username: root
   password: root
   driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
       
#修改最大上传文件限制
spring:
 jackson:
   time-zone:  GMT+8
 servlet:
   multipart:
     max-file-size: 100MB
     max-request-size: 100MB
#解决中文乱码
 http:
   encoding:
     force: true
     charset: utf-8
     enabled: true
#行的sql打印出来,在开发或测试的时候可以用
mybatis:
 configuration:
   log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

编写工具类

列出所有的数据源key

package com.tcm.common.util;

/**
 * 列出所有的数据源key(常用数据库名称来命名) 
 * 注意: 
 * 这里的变量是  配置文件中的别称,只取datasource前的
 */
public enum DatabaseType {
	/**
	 * mysql
	 */
	tcmtown,
	/**
	 * sql server 数据库
	 */
	herbal,
	/**
	 * sql server 数据库
	 */
	culture,
	/**
	 * sql server 数据库
	 */
	health,
	lnyby
}

动态数据源

package com.tcm.common.util;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import com.tcm.common.util.DatabaseContextHolder;

/**
 * 动态数据源(需要继承AbstractRoutingDataSource)
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
//     System.out.println("\"数据源为{} "+DatabaseContextHolder.getDatabaseType());
        return DatabaseContextHolder.getDatabaseType();
    }
} 

保存一个线程安全的DatabaseType容器

package com.tcm.common.util;

/**
 * 作用:
 * 保存一个线程安全的DatabaseType容器
 */
public class DatabaseContextHolder {
    private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();

    public static DatabaseType getDatabaseType(){
        return contextHolder.get();
    }

    public static void setDatabaseType(DatabaseType type) {
        contextHolder.set(type);
    }
    public static void clearDataSource(){
        contextHolder.remove();
    }
}

数据源注入对应的方法实例

package com.tcm.common.config;

import com.tcm.common.service.IArticleCategorySqlService;
import com.tcm.common.service.imp.ArticleCategorySqlServiceImp;
import com.tcm.common.util.DatabaseContextHolder;
import com.tcm.common.util.DatabaseType;
import com.tcm.culture.service.CultureSqlService;
import com.tcm.culture.service.CultureTableService;
import com.tcm.culture.service.CultureViewService;
import com.tcm.culture.service.imp.CultureSqlServiceImp;
import com.tcm.culture.service.imp.CultureTableServiceImp;
import com.tcm.culture.service.imp.CultureViewServiceImp;
import com.tcm.health.service.HealthSqlService;
import com.tcm.health.service.HealthTableService;
import com.tcm.health.service.HealthViewService;
import com.tcm.health.service.imp.HealthSqlServiceImp;
import com.tcm.health.service.imp.HealthTableServiceImp;
import com.tcm.health.service.imp.HealthViewServiceImp;
import com.tcm.herbal.service.HerbalSqlService;
import com.tcm.herbal.service.HerbalTableService;
import com.tcm.herbal.service.imp.HerbalSqlServiceImp;
import com.tcm.herbal.service.imp.HerbalTableServiceImp;
import com.tcm.herbal.service.imp.HerbalViewServiceImp;
import com.tcm.lnyby.service.LnybySqlService;
import com.tcm.lnyby.service.LnybyTableService;
import com.tcm.lnyby.service.LnybyViewService;
import com.tcm.lnyby.service.imp.LnybySqlServiceImp;
import com.tcm.lnyby.service.imp.LnybyTableServiceImp;
import com.tcm.lnyby.service.imp.LnybyViewServiceImp;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 数据源注入对应的方法实例
 */
@Aspect
@Order(1)
@Component
public class DataSourceAspect {

	/**
	 * 使用空方法定义切点表达式
	 */
	@Pointcut("execution(* com.tcm.*.service.**.*(..))")
	public void declareJointPointExpression() {
	}

	@Before("declareJointPointExpression()")
	public void setDataSourceKey(JoinPoint point) {
	    // 不同的service类连接不同的数据源
		// 根据连接点所属的类实例,动态切换数据源
		/**
		 * @herbal 
		 * */
		if (point.getTarget() instanceof HerbalTableService || point.getTarget() instanceof HerbalViewServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.herbal);
		} 
		else if (point.getTarget() instanceof HerbalTableService || point.getTarget() instanceof HerbalTableServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.herbal);
		}
		else if (point.getTarget() instanceof HerbalSqlService || point.getTarget() instanceof HerbalSqlServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.herbal);
		}
		/**
		 * @lnyby 
		 * */
		else if (point.getTarget() instanceof IArticleCategorySqlService || point.getTarget() instanceof ArticleCategorySqlServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.herbal);
		}
		/**
		 * @health
		 * */
		if (point.getTarget() instanceof HealthViewService || point.getTarget() instanceof HealthViewServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.health);
		} 
		else if (point.getTarget() instanceof HealthTableService || point.getTarget() instanceof HealthTableServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.health);
		}
		else if (point.getTarget() instanceof HealthSqlService || point.getTarget() instanceof HealthSqlServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.health);
		}
		/**
		 * @culture 
		 * */ 
		if (point.getTarget() instanceof CultureViewService || point.getTarget() instanceof CultureViewServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.culture);
		} 
		else if (point.getTarget() instanceof CultureTableService || point.getTarget() instanceof CultureTableServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.culture);
		}
		else if (point.getTarget() instanceof CultureSqlService || point.getTarget() instanceof CultureSqlServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.culture);
		}
		/**
		 * @lnyby
		 * */ 
		if (point.getTarget() instanceof LnybyViewService || point.getTarget() instanceof LnybyViewServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.lnyby);
		} 
		else if (point.getTarget() instanceof LnybyTableService || point.getTarget() instanceof LnybyTableServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.lnyby);
		}else if (point.getTarget() instanceof LnybySqlService || point.getTarget() instanceof LnybySqlServiceImp) {
			DatabaseContextHolder.setDatabaseType(DatabaseType.lnyby);
		}
	}

	@After("declareJointPointExpression()")
	public void after(JoinPoint point) {
		DatabaseContextHolder.clearDataSource();
	}
}

springboot集成mybatis的基本入口

package com.tcm.common.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.tcm.common.util.DatabaseType;
import com.tcm.common.util.DynamicDataSource;

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.beans.factory.config.PropertiesFactoryBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
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.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)
 * 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置
 */
@Configuration // 该注解类似于spring配置文
@ComponentScan(basePackages = { "com.gitee.sunchenbin.mybatis.actable.manager.*" })
public class MyBatisConfig {
	/**
	 * 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)
	 */
	@Primary
	@ConfigurationProperties(prefix = "tcmtown-datasource")
	@Bean(name = "tcmtownDataSource")
	public DataSource tcmtownDataSource() throws Exception {
		return new DruidDataSource();
	}

	/**
	 * @health 
	 */
	@ConfigurationProperties(prefix = "health-datasource")
	@Bean(name = "healthDataSource")
	public DataSource healthDataSource() throws Exception {
		return new DruidDataSource();
	}

	/**
	 * @herbal 
	 */
	@ConfigurationProperties(prefix = "herbal-datasource")
	@Bean(name = "herbalDataSource")
	public DataSource herbalDataSource() throws Exception {
		return new DruidDataSource();
	}

	/**
	 * @culture 
	 */
	@ConfigurationProperties(prefix = "culture-datasource")
	@Bean(name = "cultureDataSource")
	public DataSource cultureDataSource() throws Exception {
		return new DruidDataSource();
	}
	
	/**
	 * @culture 
	 */
	@ConfigurationProperties(prefix = "lnyby-datasource")
	@Bean(name = "lnybyDataSource")
	public DataSource lnybyDataSource() throws Exception {
		return new DruidDataSource();
	}
	/**
	 * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
	 * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
	 */
	@Bean(name = "dynamicDataSource")
	public DynamicDataSource dataSource(@Qualifier("tcmtownDataSource") DataSource tcmtownDataSource,
			@Qualifier("healthDataSource") DataSource healthDataSource,
			@Qualifier("herbalDataSource") DataSource herbalDataSource,
			@Qualifier("cultureDataSource") DataSource cultureDataSource,
			@Qualifier("lnybyDataSource") DataSource lnybyDataSource) {
		Map<Object, Object> targetDataSources = new HashMap<>();
		targetDataSources.put(DatabaseType.tcmtown, tcmtownDataSource);
		targetDataSources.put(DatabaseType.health, healthDataSource);
		targetDataSources.put(DatabaseType.herbal, herbalDataSource);
		targetDataSources.put(DatabaseType.culture, cultureDataSource);
		targetDataSources.put(DatabaseType.lnyby, lnybyDataSource);
		DynamicDataSource dataSource = new DynamicDataSource();
		dataSource.setDefaultTargetDataSource(tcmtownDataSource);// 默认的datasource设置为tcmtownDataSource
		dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
		return dataSource;
	}

	/**
	 * @sqlSessionFactory 根据数据源创建SqlSessionFactory
	 */
	@Bean(name = "sqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
			throws Exception {
		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
		fb.setDataSource(dynamicDataSource);// 指定数据源(这个必须有,否则报错)
		// 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
		fb.setMapperLocations(resolver.getResources("classpath*:com/gitee/sunchenbin/mybatis/actable/mapping/*/*.xml"));
		fb.setTypeAliasesPackage("com.*.entity.*");
		fb.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
		return fb.getObject();
	}

	@Bean(name = "dataSourceTransactionManager")
	public DataSourceTransactionManager dataSourceTransactionManager(
			@Qualifier("tcmtownDataSource") DataSource tcmtownDataSource) {
		DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
		dataSourceTransactionManager.setDataSource(tcmtownDataSource);
		return dataSourceTransactionManager;
	}

	@Bean(name = "configProperties")
	public PropertiesFactoryBean configProperties() throws Exception {
		PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
		PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		propertiesFactoryBean.setLocations(resolver.getResources("classpath*:application.properties"));
		return propertiesFactoryBean;
	}
	
}

将mapper层代码,自动转换成mybatis对应的dao层代码(本人自己的理解)

package com.tcm.common.config;

import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import io.lettuce.core.dynamic.annotation.CommandNaming;

@Configuration
@AutoConfigureAfter(MyBatisConfig.class)
public class MyBatisMapperScannerConfig {

    @Bean(name = "mapperScannerConfigurer")
    public MapperScannerConfigurer mapperScannerConfigurer() throws Exception{
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.*.mapper.*;com.gitee.sunchenbin.mybatis.actable.dao.*");
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        return mapperScannerConfigurer;
    }

}

动态切换数据源到此结束,下面的是根据实体类创建在数据库中创建表

根据实体类自动创建数据库表,默认是在

配置

#create:系统启动后,会将所有的表删除掉,然后根据entity类中配置的结构重新建表,该操作会破坏原有数据。
#update:系统启动后,会根据entity类中配置的结构对表字段进行增删改操作
#------增:添加数据库表;根据实体向数据库表中添加字段
#------删:根据实体删除数据库表中的字段;不能实现删除项目实体类而删除数据库表
#------改:修改数据库字段的名字、属性
#none:系统不做任何处理
mybatis.table.auto=update
#用来配置要扫描的用于创建表的实体类的包名 多个就使用逗号分隔
mybatis.model.pack=com.tcm.health.entity,com.tcm.herbal.entity,com.tcm.culture.entity,com.tcm.lnyby.entity,com.tcm.common.entity
#错误的写法
#mybatis.model.pack=com.tcm.*.entity
#用来区别数据库,预计会支持这四种数据库mysql/oracle/sqlserver/postgresql,但目前仅支持mysql
mybatis.database.type=mysql

实体类

package   com.tcm.health.entity; 

import com.gitee.sunchenbin.mybatis.actable.annotation.Column; 
import com.gitee.sunchenbin.mybatis.actable.annotation.Table; 
import com.gitee.sunchenbin.mybatis.actable.command.BaseModel; 
import com.gitee.sunchenbin.mybatis.actable.constants.MySqlTypeConstant; 
import com.fasterxml.jackson.annotation.JsonProperty; 
import com.tcm.sys.entity.Pages;
import java.util.*;

@SuppressWarnings("serial")
//  数据库为MyBatisConfig类中 dataSource.setDefaultTargetDataSource(tcmtownDataSource); 设置的
// 表名
@Table(name="view_channel_mother_childer")
@Data
public class HealthChilder extends BaseModel { 

	@JsonProperty(value = "add_time") 
	//name 数据库中的字段名称 type 数据库类型
	@Column(name = "add_time",type = MySqlTypeConstant.VARCHAR) 
	private String add_time;

	@JsonProperty(value = "call_index") 
	@Column(name = "call_index",type = MySqlTypeConstant.VARCHAR) 
	private String call_index;

	@JsonProperty(value = "category_id") 
	@Column(name = "category_id",type = MySqlTypeConstant.VARCHAR) 
	private String category_id;

	@JsonProperty(value = "channel_id") 
	@Column(name = "channel_id",type = MySqlTypeConstant.VARCHAR) 
	private String channel_id;

	@JsonProperty(value = "click") 
	@Column(name = "click",type = MySqlTypeConstant.VARCHAR) 
	private String click;

	@JsonProperty(value = "content") 
	@Column(name = "content",type = MySqlTypeConstant.LONGTEXT) 
	private String content;

	@JsonProperty(value = "groupids_view") 
	@Column(name = "groupids_view",type = MySqlTypeConstant.VARCHAR) 
	private String groupids_view;

	@JsonProperty(value = "id") 
	@Column(name = "id",type = MySqlTypeConstant.VARCHAR) 
	private String id;

	@JsonProperty(value = "img_url") 
	@Column(name = "img_url",type = MySqlTypeConstant.VARCHAR) 
	private String img_url;

	@JsonProperty(value = "is_hot") 
	@Column(name = "is_hot",type = MySqlTypeConstant.VARCHAR) 
	private String is_hot;

	@JsonProperty(value = "is_msg") 
	@Column(name = "is_msg",type = MySqlTypeConstant.VARCHAR) 
	private String is_msg;

	@JsonProperty(value = "is_red") 
	@Column(name = "is_red",type = MySqlTypeConstant.VARCHAR) 
	private String is_red;

	@JsonProperty(value = "is_slide") 
	@Column(name = "is_slide",type = MySqlTypeConstant.VARCHAR) 
	private String is_slide;

	@JsonProperty(value = "is_sys") 
	@Column(name = "is_sys",type = MySqlTypeConstant.VARCHAR) 
	private String is_sys;

	@JsonProperty(value = "is_top") 
	@Column(name = "is_top",type = MySqlTypeConstant.VARCHAR) 
	private String is_top;

	@JsonProperty(value = "link_url") 
	@Column(name = "link_url",type = MySqlTypeConstant.VARCHAR) 
	private String link_url;

	@JsonProperty(value = "seo_description") 
	@Column(name = "seo_description",type = MySqlTypeConstant.VARCHAR) 
	private String seo_description;

	@JsonProperty(value = "seo_keywords") 
	@Column(name = "seo_keywords",type = MySqlTypeConstant.VARCHAR) 
	private String seo_keywords;

	@JsonProperty(value = "seo_title") 
	@Column(name = "seo_title",type = MySqlTypeConstant.VARCHAR) 
	private String seo_title;

	@JsonProperty(value = "sort_id") 
	@Column(name = "sort_id",type = MySqlTypeConstant.VARCHAR) 
	private String sort_id;

	@JsonProperty(value = "source") 
	@Column(name = "source",type = MySqlTypeConstant.VARCHAR) 
	private String source;

	@JsonProperty(value = "status") 
	@Column(name = "status",type = MySqlTypeConstant.VARCHAR) 
	private String status;

	@JsonProperty(value = "tags") 
	@Column(name = "tags",type = MySqlTypeConstant.VARCHAR) 
	private String tags;

	@JsonProperty(value = "title") 
	@Column(name = "title",type = MySqlTypeConstant.VARCHAR) 
	private String title;

	@JsonProperty(value = "update_time") 
	@Column(name = "update_time",type = MySqlTypeConstant.VARCHAR) 
	private String update_time;

	@JsonProperty(value = "user_name") 
	@Column(name = "user_name",type = MySqlTypeConstant.VARCHAR) 
	private String user_name;

	@JsonProperty(value = "vote_id") 
	@Column(name = "vote_id",type = MySqlTypeConstant.VARCHAR) 
	private String vote_id;

	@JsonProperty(value = "zhaiyao") 
	@Column(name = "zhaiyao",type = MySqlTypeConstant.VARCHAR) 
	private String zhaiyao;

	@JsonProperty(value = "pages")
		private Pages pages;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值