当前位置: 首页 > news >正文

互联网行业信息网站投稿平台

互联网行业信息网站,投稿平台,网站备案主体是什么意思,wordpress分页项目支持多数据库连接是个很常见的需求&#xff0c;这不仅是要在编译前连已经知道的多个数据库&#xff0c;有时还要在程序运行时连后期增加的多个数据源来获得数据。 一、编译前注册数据库连接 1.引入依赖包 <!-- springboot 3.x --><dependency><groupId&g…

项目支持多数据库连接是个很常见的需求,这不仅是要在编译前连已经知道的多个数据库,有时还要在程序运行时连后期增加的多个数据源来获得数据。

一、编译前注册数据库连接

1.引入依赖包

<!-- springboot 3.x --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId>
</dependency>
<!-- springboot 2.x --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId>
</dependency>

2.配置文件

一般本地应用可能有多个数据源,比如,需要支持读写分离的主从数据库配置,可能的application-druid.yml配置文件代码如下:

# 数据源配置
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverdynamic:primary: MASTERdatasource:# 主库数据源MASTER:url: jdbc:mysql://127.0.0.1/ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: 123456# 从库数据源SLAVE:url: jdbc:mysql://127.0.0.1/ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: 123456druid:# 初始连接数initialSize: 5# 最小连接池数量minIdle: 10# 最大连接池数量maxActive: 20# 配置获取连接等待超时的时间maxWait: 60000# 配置连接超时时间connectTimeout: 30000# 配置网络超时时间socketTimeout: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒timeBetweenEvictionRunsMillis: 60000# 配置一个连接在池中最小生存的时间,单位是毫秒minEvictableIdleTimeMillis: 300000# 配置一个连接在池中最大生存的时间,单位是毫秒maxEvictableIdleTimeMillis: 900000# 配置检测连接是否有效validationQuery: SELECT 1 FROM DUALtestWhileIdle: truetestOnBorrow: falsetestOnReturn: falsewebStatFilter:enabled: truestatViewServlet:enabled: true# 设置白名单,不填则允许所有访问allow:url-pattern: /druid/*# 控制台管理用户名和密码login-username: ruoyilogin-password: 123456filter:stat:enabled: true# 慢SQL记录log-slow-sql: trueslow-sql-millis: 1000merge-sql: truewall:config:multi-statement-allow: true

3.配置DruidConfig

/*** druid 配置多数据源* */
@Configuration
public class DruidConfig
{/** 主数据库配置 */@Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}/** 从数据库配置 */@Bean@ConfigurationProperties("spring.datasource.druid.slave")@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")public DataSource slaveDataSource(DruidProperties druidProperties){DruidDataSource dataSource = DruidDataSourceBuilder.create().build();return druidProperties.dataSource(dataSource);}/** 动态数据源 */@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource(DataSource masterDataSource){Map<Object, Object> targetDataSources = new HashMap<>();// 加入主数据库配置targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);// 加入从数据库配置setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");return new DynamicDataSource(masterDataSource, targetDataSources);}/*** 设置数据源* * @param targetDataSources 备选数据源集合* @param sourceName 数据源名称* @param beanName bean名称*/public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName){try{DataSource dataSource = SpringUtils.getBean(beanName);targetDataSources.put(sourceName, dataSource);}catch (Exception e){}}
}

4.配置DynamicDataSource

新建DynamicDataSource 类来继承AbstractRoutingDataSource 类。

继承自 Spring 提供的 AbstractRoutingDataSource,通过重写 determineCurrentLookupKey 方法,根据上下文信息动态选择当前使用的数据源。

/*** 动态数据源类,用于实现多数据源切换。*/
public class DynamicDataSource extends AbstractRoutingDataSource {/*** 构造函数,初始化默认数据源和目标数据源映射。** @param defaultTargetDataSource 默认数据源,在未指定其他数据源时使用* @param targetDataSourceMap 目标数据源映射,包含多个可选的数据源*/public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSourceMap) {// 设置默认数据源super.setDefaultTargetDataSource(defaultTargetDataSource);// 设置目标数据源映射super.setTargetDataSources(targetDataSourceMap);// 初始化父类属性super.afterPropertiesSet();}/*** 确定当前使用的数据源键。* 该方法会根据上下文中的数据源类型来决定使用哪个数据源。** @return 当前使用的数据源键*/@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}}

5.配置DynamicDataSourceContextHolder

例如在web应用这种多线程的环境中,每个线程需要执行访问的数据库都不一样。因此需要给每个线程设置独立变量。

新建DynamicDataSourceContextHolder文件,代码如下:

/*** 数据源切换处理* */
public class DynamicDataSourceContextHolder
{public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);/*** 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。*/private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();/*** 设置数据源的变量*/public static void setDataSourceType(String dsType){log.info("切换到{}数据源", dsType);CONTEXT_HOLDER.set(dsType);}/*** 获得数据源的变量*/public static String getDataSourceType(){return CONTEXT_HOLDER.get();}/*** 清空数据源变量*/public static void clearDataSourceType(){CONTEXT_HOLDER.remove();}
}

6.配置AOP切面类

通过 AOP 拦截带有 @DataSource 注解的方法或类,在方法执行前设置数据源,在方法执行后清除数据源,确保每个请求都能正确使用指定的数据源。

代码如下:

/*** 多数据源处理切面类。*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect {protected Logger logger = LoggerFactory.getLogger(getClass());/*** 定义切入点,匹配带有 @DataSource 注解的方法或类。*/@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"+ "|| @within(com.ruoyi.common.annotation.DataSource)")public void dsPointCut() {}/*** 环绕通知,拦截 dsPointCut 切入点匹配的方法。* 在方法执行前设置数据源类型,在方法执行后清除数据源类型。** @param point 当前连接点对象* @return 方法返回值* @throws Throwable 如果方法执行过程中抛出异常*/@Around("dsPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {DataSource dataSource = getDataSource(point);if (StringUtils.isNotNull(dataSource)) {// 根据注解配置设置数据源类型if ("".equals(dataSource.name())) {DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());} else {DynamicDataSourceContextHolder.setDataSourceType(dataSource.name());}}try {// 执行目标方法return point.proceed();} finally {// 方法执行完毕后清除数据源类型DynamicDataSourceContextHolder.clearDataSourceType();}}/*** 获取当前方法或类上的 @DataSource 注解配置。** @param point 当前连接点对象* @return 数据源配置信息*/public DataSource getDataSource(ProceedingJoinPoint point) {MethodSignature signature = (MethodSignature) point.getSignature();// 从方法上查找 @DataSource 注解DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);if (Objects.nonNull(dataSource)) {return dataSource;}// 如果方法上没有找到,则从类上查找 @DataSource 注解return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);}
}

7.使用方式

现在可以通过注解选择要使用哪个数据库。

在需要使用多数据源方法或类上添加@DataSource注解,其中value用来表示数据源。

@DataSource(value = DataSourceType.SLAVE)
public List<SysUser> selectUserList(SysUser user)
{return userMapper.selectUserList(user);
}
@Service
@DataSource(value = DataSourceType.SLAVE)
public class SysUserServiceImpl{}

或者手动切换数据源

public List<SysUser> selectUserList(SysUser user)
{DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());List<SysUser> userList = userMapper.selectUserList(user);DynamicDataSourceContextHolder.clearDataSourceType();return userList;
}

在这里插入图片描述

二、运行时访问数据库

前面访问多数据源的方式必须提前在yaml这样的配置文件中提前填写好数据库的连接属性。

但是类似BI展示这样的软件项目可能得访问十几个业务的数据源,并且别的数据源的访问信息还时刻在变。这样的话,就不方便把数据库的连接属性提前写在配置文件中了。

1.配置数据管理工具


@Configuration
public class DataSourceManagement implements InitializingBean {protected final Logger logger = LoggerFactory.getLogger(DataSourceManagement.class);/*** 额外数据源*/private Map<String, DataSource> targetDataSources = new HashMap<>();/*** 配置文件的数据源*/@Autowiredprivate DynamicDataSourceProperties dataSourceProperties;@Autowiredprivate CreateDataSource c;@Autowired(required = false)private AfterCreateDataSource afterCreateDataSource;private DynamicDataSource dynamicDataSource;/*** 创建并返回动态数据源实例。** @return 动态数据源实例*/@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource dataSource() {Map<Object, Object> targetDataSourceMap = new HashMap<>(targetDataSources);this.dynamicDataSource = new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), targetDataSourceMap);return this.dynamicDataSource;}/*** 验证数据源是否可用。** @param dataSource 要验证的数据源*/public void validateDataSource(DataSource dataSource) {try (Connection conn = dataSource.getConnection()) {String validationQuery = "SELECT 1";try (Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(validationQuery)) {if (!(rs.next() && rs.getInt(1) == 1)) {throw new RuntimeException("数据源连接验证失败:查询结果不正确");}}} catch (SQLException e) {throw new RuntimeException("数据源连接验证失败", e);}}/*** 在所有依赖项设置完成后执行此方法。* 初始化所有配置的数据源,并进行连接验证。*/@Overridepublic void afterPropertiesSet() throws Exception {dataSourceProperties.getDatasource().forEach((name, props) -> {Properties properties = dataSourceProperties.build(props);CommonDataSource commonDataSource = c.createDataSource(name, properties);if (afterCreateDataSource != null) {afterCreateDataSource.afterCreateDataSource(name, properties, commonDataSource);}DataSource dataSource = (DataSource) commonDataSource;logger.info("数据源:{} 校验中.......", name);// 计时long start = System.currentTimeMillis();validateDataSource(dataSource);logger.info("数据源:{} 链接成功,耗时:{}ms", name, System.currentTimeMillis() - start);this.putDataSource(name, dataSource);});}/*** 添加或更新一个数据源,并创建相应的SqlSessionFactory。** @param name       数据源名称* @param dataSource 数据源实例*/public void putDataSource(String name, DataSource dataSource) {targetDataSources.put(name, dataSource);}/*** 添加一个新的数据源。** @param name       数据源名称* @param properties 数据源属性*/public void addDataSourceWithCheck(String name, Properties properties) {if (targetDataSources.containsKey(name)) {logger.info("数据源" + name + "之前已经创建,准备测试数据源是否正常...");//throw new RuntimeException("数据源已存在");} else {CommonDataSource commonDataSource = c.createDataSource(name, properties);if (afterCreateDataSource != null) {afterCreateDataSource.afterCreateDataSource(name, properties, commonDataSource);}DataSource dataSource = (DataSource) commonDataSource;logger.info("数据源:{} 校验中.......", name);// 计时long start = System.currentTimeMillis();validateDataSource(dataSource);logger.info("数据源:{} 链接成功,耗时:{}ms", name, System.currentTimeMillis() - start);this.putDataSource(name, dataSource);HashMap<Object, Object> objectHashMap = new HashMap<>(this.targetDataSources);// 将map赋值给父类的TargetDataSourcesthis.dynamicDataSource.setTargetDataSources(objectHashMap);// 重要:将TargetDataSources中的连接信息放入resolvedDataSources管理,否则无法切换数据源this.dynamicDataSource.afterPropertiesSet();logger.debug("添加数据源成功,名称:{}", name);}}}

2.创建数据源

通过 DruidXADataSource 创建数据源,并使用 DruidConfig 和 DynamicDataSourceProperties 配置数据源属性。

/*** 数据源创建类,实现 CreateDataSource 接口。* */
@Component
public class DataSourceCreate implements CreateDataSource {@Autowiredprivate DynamicDataSourceProperties properties;@Autowiredprivate DruidConfig druidConfig;/*** 创建数据源的方法。** @param name 数据源名称* @param prop 数据源配置属性* @return 创建的数据源对象*/@Overridepublic DataSource createDataSource(String name, Properties prop) {// 创建 DruidXADataSource 实例DruidXADataSource dataSource = new DruidXADataSource();// 将创建的数据源添加到 DruidConfig 的数据源列表中druidConfig.getDruidDataSources().add(dataSource);// 设置数据源的连接属性dataSource.setConnectProperties(prop);// 使用 DynamicDataSourceProperties 配置数据源的其他属性properties.setProperties(dataSource, prop);// 返回创建的数据源对象return dataSource;}
}

3.准备实体

准备个存储数据库连接属性的实体,用于接收信息。

@Data
public class DataSource {private String id;private String connIp;private String connPort;private String connDbName;private String connUserName;private String connPassWord;private String connName;private String connType;private String connProperty;private String connDriverClass;
}

4.调用方式

写一个服务类,将从某个存储数据库连接信息的表里查出所有数据源。然后根据连接属性创建动态数据源。

@Service
public class DBChangeServiceImpl implements IDBChangeService {private final Logger logger = LoggerFactory.getLogger(DBChangeServiceImpl.class);@AutowiredDataSourceMapper dataSourceMapper;@Autowiredprivate DataSourceManagement dataSourceManagement;/*** 获取数据源* @return*/@Overridepublic List<DataSource> get() {return dataSourceMapper.get();}/*** 切换数据源* @param datasourceId* @return*/@Overridepublic boolean changeDb(String datasourceId) {//清除本线程的数据源DynamicDataSourceContextHolder.clearDataSourceType();logger.debug("清除本线程的数据源");//获取所有数据源List<DataSource> dataSourcesList = dataSourceMapper.get();logger.debug("获取到的数据源列表: {}", JSON.toJSONString(dataSourcesList));//遍历所有数据源,切换到指定数据源for (DataSource dataSource : dataSourcesList) {if (dataSource.getId().equals(datasourceId)) {logger.info(JSON.toJSONString(dataSource));logger.info("需要使用的的数据源已经找到,datasourceId是:" + dataSource.getId());Properties properties = buildDataSourceProperties(dataSource);//创建数据源连接&检查 若存在则不需重新创建try {// 使用DataSourceManagement来创建数据源并检查dataSourceManagement.addDataSourceWithCheck(dataSource.getId(), properties);// 切换数据源DynamicDataSourceContextHolder.setDataSourceType(dataSource.getId());logger.info("标记切换到数据源:"+datasourceId);return true;} catch (Exception e) {logger.error("数据源切换失败", e);e.printStackTrace();}}}logger.debug("未找到目标数据源");return false;}private Properties buildDataSourceProperties(DataSource dataSource) {Properties properties = new Properties();properties.setProperty("url", "jdbc:mysql://" + dataSource.getConnIp() + ":" + dataSource.getConnPort() + "/" + dataSource.getConnDbName());properties.setProperty("username", dataSource.getConnUserName());properties.setProperty("password", dataSource.getConnPassWord());properties.setProperty("driverClassName", dataSource.getConnDriverClass());// 添加其他必要的属性return properties;}}

切换数据源执行sql语句后,记得把数据源再切回去。

public AjaxResult runWithSql(String datasourceid, String sql){//切换到数据库dbChangeService.changeDb(datasourceid);System.out.println("Current DataSource Type: " + DynamicDataSourceContextHolder.getDataSourceType());System.out.println("RunWithSql: "+sql);List<Map<String, Object>> maps = execSqlService.runWithSql(sql);System.out.println("=======>"+JSON.toJSONString(maps));//切回主数据源DynamicDataSourceContextHolder.clearDataSourceType();return AjaxResult.success(maps);}

可以自行测试,发现连接成功了。
在这里插入图片描述


文章转载自:
http://dinncocalchas.tqpr.cn
http://dinncobiogenesis.tqpr.cn
http://dinncospandril.tqpr.cn
http://dinncopsilomelane.tqpr.cn
http://dinncosolaceful.tqpr.cn
http://dinncotwirp.tqpr.cn
http://dinncocanopied.tqpr.cn
http://dinncoclothesman.tqpr.cn
http://dinncopretersensual.tqpr.cn
http://dinncogoldwaterism.tqpr.cn
http://dinncocursed.tqpr.cn
http://dinncosouthernization.tqpr.cn
http://dinncoaponeurotic.tqpr.cn
http://dinncofid.tqpr.cn
http://dinncovoluntarily.tqpr.cn
http://dinncocalomel.tqpr.cn
http://dinncosympathize.tqpr.cn
http://dinncoreversionary.tqpr.cn
http://dinncorerun.tqpr.cn
http://dinncoamazonian.tqpr.cn
http://dinncocarotinoid.tqpr.cn
http://dinncoequilibrium.tqpr.cn
http://dinnconanosecond.tqpr.cn
http://dinncocaseinate.tqpr.cn
http://dinncodisembark.tqpr.cn
http://dinncoelusive.tqpr.cn
http://dinncodiscourtesy.tqpr.cn
http://dinncobecomingly.tqpr.cn
http://dinncocurviform.tqpr.cn
http://dinncounpriceable.tqpr.cn
http://dinncoinversely.tqpr.cn
http://dinncoflexibility.tqpr.cn
http://dinncoreis.tqpr.cn
http://dinncoprizeless.tqpr.cn
http://dinncopluckless.tqpr.cn
http://dinncotepefaction.tqpr.cn
http://dinncotroublesomely.tqpr.cn
http://dinncooffline.tqpr.cn
http://dinncomicrobic.tqpr.cn
http://dinncolentisk.tqpr.cn
http://dinncobanaban.tqpr.cn
http://dinncocesspool.tqpr.cn
http://dinncoconfederate.tqpr.cn
http://dinncoemi.tqpr.cn
http://dinncokempis.tqpr.cn
http://dinnconodus.tqpr.cn
http://dinncometapage.tqpr.cn
http://dinncocontroversy.tqpr.cn
http://dinncofantasyland.tqpr.cn
http://dinncobackstage.tqpr.cn
http://dinncotindal.tqpr.cn
http://dinncoinseparable.tqpr.cn
http://dinncogeromorphism.tqpr.cn
http://dinncoviscose.tqpr.cn
http://dinncohalidome.tqpr.cn
http://dinncoquotient.tqpr.cn
http://dinncolama.tqpr.cn
http://dinncomarried.tqpr.cn
http://dinncoclimatize.tqpr.cn
http://dinncocommiseration.tqpr.cn
http://dinncoborer.tqpr.cn
http://dinncobarret.tqpr.cn
http://dinncoritard.tqpr.cn
http://dinncopeenie.tqpr.cn
http://dinncomalacostracous.tqpr.cn
http://dinncodigger.tqpr.cn
http://dinncounprepare.tqpr.cn
http://dinncomidriff.tqpr.cn
http://dinncorepay.tqpr.cn
http://dinncogangdom.tqpr.cn
http://dinncoemasculation.tqpr.cn
http://dinncojoskin.tqpr.cn
http://dinncorille.tqpr.cn
http://dinncodiorthosis.tqpr.cn
http://dinncotaphonomy.tqpr.cn
http://dinncopetiolar.tqpr.cn
http://dinncorockfish.tqpr.cn
http://dinncobenzenoid.tqpr.cn
http://dinncolaconical.tqpr.cn
http://dinncocartulary.tqpr.cn
http://dinncoibex.tqpr.cn
http://dinncozapotecan.tqpr.cn
http://dinncoelvan.tqpr.cn
http://dinncodissymmetrical.tqpr.cn
http://dinncounderfund.tqpr.cn
http://dinncolucency.tqpr.cn
http://dinncodlitt.tqpr.cn
http://dinncocurvature.tqpr.cn
http://dinncogasholder.tqpr.cn
http://dinncoindiscerptible.tqpr.cn
http://dinncotimesaving.tqpr.cn
http://dinncooutlive.tqpr.cn
http://dinncocarbonatation.tqpr.cn
http://dinncosuperincumbent.tqpr.cn
http://dinncodecarbonize.tqpr.cn
http://dinncoacrawl.tqpr.cn
http://dinncobrushfire.tqpr.cn
http://dinncoplatynite.tqpr.cn
http://dinncoroentgen.tqpr.cn
http://dinncopluto.tqpr.cn
http://www.dinnco.com/news/135586.html

相关文章:

  • 网站建设公司网址南京百度关键字优化价格
  • 东莞响应式网站建设兰州网络推广与营销
  • 网站建设的安全可行性免费推广方法
  • 外行学网页制作与网站建设从入门到精通网上国网app推广
  • 赤峰企业网站建设沈阳seo顾问
  • 网站建设型网站横幅(banner)图片新手怎样做网络推广
  • 网站页面下沉的特效代码seo公司网站推广
  • 网站更换备案号百度推广费用多少钱
  • 网站版面的图文是怎么做的最新的网络营销的案例
  • 政府门户网站建设总结如何推广一个项目
  • 网站如何做微信支付宝支付宝支付宝app推广平台有哪些
  • 东莞三合一网站制作搜索排名广告营销怎么做
  • 深圳有什么互联网公司北京seo优化外包
  • 网站策划机构一句话让客户主动找你
  • 做网站的技术体系长沙关键词排名首页
  • wordpress 响应式模块宁波seo推广推荐公司
  • 怎样给网站做百度推广网站排名靠前
  • 南京网络营销上海seo有哪些公司
  • 交流建设网站今日头条热搜榜前十名
  • iis7网站建设快速的网站设计制作
  • 建筑网站免费seo方案
  • 上海自适应网站开发seo必备软件
  • 天津网站建设代理商软文模板app
  • 六安网站优化短视频平台推广方案
  • 高端网站设计图片seo文章推广
  • 网站页面设计流程巢湖seo推广
  • 嘉兴市做外贸网站南京网络推广外包
  • 织梦CMS做的网站能过等保三级吗网站内容检测
  • 购物网站的商品展示模块天津百度推广排名优化
  • 打电话推销做网站的是真的吗百度seo查询