使用Druid完成SpringBoot集成MySql、DM(达梦)数据库数据源操作

业务背景

一个方法里,对A数据源需要进行查询,B数据源进行插入(切面插入访问数据,日志)。

详细业务是查询业务数据,同时主数据库记录访问日志。

第一步依赖先行


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>

        <dependency>
            <groupId>com.dameng</groupId>
            <artifactId>Dm8JdbcDriver18</artifactId>
            <version>8.1.1.49</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.16</version>
        </dependency>

配置类

package com.zhuao.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.google.common.collect.Maps;
import com.zhuao.common.DynamicDataSource;
import com.zhuao.constant.DataSourceTypeConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
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 javax.sql.DataSource;
import java.util.Map;

/**
 * @Description: 数据源配置
 * @author: Be.insighted
 * @create: 2024/7/15 11:35
 * @since 1.0.0
 */
@Slf4j
@Configuration
public class DataSourceConfig {

    /**
     * 主数据源
     * @return
     */
    @Bean(initMethod = "init", destroyMethod = "close")
    @ConfigurationProperties("dm.datasource")
    public DruidDataSource masterDataSource() {
        log.info("masterDataSource druid data-source init...");
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 从数据源
     * @return
     */
    @Bean(initMethod = "init", destroyMethod = "close")
    @ConfigurationProperties("mysql.datasource")
    public DruidDataSource slaveDataSource() {
        log.info("slaveDataSource druid data-source init...");
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 动态数据源
     * @param masterDataSource masterDataSource
     * @param slaveDataSource  slaveDataSource
     * @return {@link DynamicDataSource}
     */
    @Bean
    @Primary
    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<String, DataSource> pool = Maps.newHashMapWithExpectedSize(16);
        pool.put(DataSourceTypeConstant.MASTER, masterDataSource);
        pool.put(DataSourceTypeConstant.FIRST_FOLLOWER, slaveDataSource);
        return new DynamicDataSource(pool, masterDataSource);
    }
}

枚举数据库类型

public interface DataSourceTypeConstant {
    /**
     * 主数据源
     */
    String MASTER = "MASTER";
    /**
     * 第1从数据源
     */
    String FIRST_FOLLOWER = "FIRST_FOLLOWER";
    /**
     * 第2从数据源
     */
    String SECOND_FOLLOWER = "SECOND_FOLLOWER";
}

切换数据源

package com.zhuao.common;

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

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

/**
 * @Description:
 * @author: lBe.insighted
 * @create: 2024/7/15 15:41
 * @since 1.0.0
 */

public class DynamicDataSource  extends AbstractRoutingDataSource {
    private static final ThreadLocal<Stack<String>> DATA_SOURCE_KEY = new InheritableThreadLocal<>();

    public static void setDataSourceKey(String dataSource) {
        Stack<String> stack = DATA_SOURCE_KEY.get();
        if (stack == null) {
            stack = new Stack<>();
            DATA_SOURCE_KEY.set(stack);
        }
        stack.push(dataSource);
    }

    public static void cleanDataSourceKey() {
        Stack<String> stack = DATA_SOURCE_KEY.get();
        if (stack != null) {
            stack.pop();
            if (stack.isEmpty()) {
                DATA_SOURCE_KEY.remove();
            }
        }
    }

    /**
     * 构造
     *
     * @param targetDataSources
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public DynamicDataSource(Map<String, DataSource> targetDataSources, DataSource defaultDataSource) {
        super.setTargetDataSources((Map) targetDataSources);
        super.setDefaultTargetDataSource(defaultDataSource);
    }

    /**
     * determineCurrentLookupKey
     *
     * @return
     * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
     */
    @Override
    protected Object determineCurrentLookupKey() {
        Stack<String> stack = DATA_SOURCE_KEY.get();
        if (stack != null) {
            return stack.peek();
        }
        return null;
    }
}

切面处理

注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DynamicDatasourceAnno {
    String value();
}

切面逻辑

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zhuao.annotation.DynamicDatasourceAnno;
import com.zhuao.common.DynamicDataSource;
import com.zhuao.constant.DataSourceTypeConstant;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;

/**
 * @Description:
 * @author: be.insighted
 * @create: 2024/7/15 15:42
 * @since 1.0.0
 */

@Component
@Aspect
@Slf4j
public class TypeSwitchDatasourceAspectJ {

    @Pointcut("@annotation(com.zhuao.annotation.DynamicDatasourceAnno)")
    public void pointcut() {
    }

    @Before(value="pointcut() && @annotation(dynamicDatasourceAnno)", argNames="dynamicDatasourceAnno")
    public void before(DynamicDatasourceAnno dynamicDatasourceAnno) {
        String dataSource = dynamicDatasourceAnno.value();
        if (StringUtils.isNotBlank(dataSource)) {
            log.info("从主数据源->切换到->从数据源({})", dataSource);
            DynamicDataSource.setDataSourceKey(dataSource);
        }
    }

    @After("pointcut()")
    public void after() {
        DynamicDataSource.cleanDataSourceKey();
        log.info("恢复主数据源");
        DynamicDataSource.setDataSourceKey(DataSourceTypeConstant.MASTER);
    }

}

使用

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zhuao.annotation.DynamicDatasourceAnno;
import com.zhuao.constant.DataSourceTypeConstant;
import com.zhuao.entity.CtiTSheetRecord;
import com.zhuao.req.CtiTeleRecordReq;


/**
 * @Description: 通话记录
 * @author: Be.insighted
 * @create: 2024/7/9 10:50
 * @since 1.0.0
 */

public interface CtiTSheetRecordService extends IService<CtiTSheetRecord> {

    @DynamicDatasourceAnno(DataSourceTypeConstant.FIRST_FOLLOWER)
    IPage<CtiTSheetRecord> query(Page<?> page, CtiTeleRecordReq req);
}

这样还不能完全实现数据源切换,服务无法启动

accountServiceImpl (field private com.******** ***Mapper)
      ↓
   accountDao
      ↓
   (inner bean)#12942633
      ↓
   org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration
┌─────┐
|  dataSource defined in class path resource [com/******************/DataSourcesConfig.class]
↑     ↓
|  databusinessDataSource defined in class path resource [com/*********/DataSourcesConfig.class]
↑     ↓
|  org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker
└─────┘

druid循环依赖

/**
	 * Specify the map of target DataSources, with the lookup key as key.
	 * The mapped value can either be a corresponding {@link javax.sql.DataSource}
	 * instance or a data source name String (to be resolved via a
	 * {@link #setDataSourceLookup DataSourceLookup}).
	 * <p>The key can be of arbitrary type; this class implements the
	 * generic lookup process only. The concrete key representation will
	 * be handled by {@link #resolveSpecifiedLookupKey(Object)} and
	 * {@link #determineCurrentLookupKey()}.
	 */
	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		this.targetDataSources = targetDataSources;
	}

	/**
	 * Specify the default target DataSource, if any.
	 * <p>The mapped value can either be a corresponding {@link javax.sql.DataSource}
	 * instance or a data source name String (to be resolved via a
	 * {@link #setDataSourceLookup DataSourceLookup}).
	 * <p>This DataSource will be used as target if none of the keyed
	 * {@link #setTargetDataSources targetDataSources} match the
	 * {@link #determineCurrentLookupKey()} current lookup key.
	 */
	public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
		this.defaultTargetDataSource = defaultTargetDataSource;
	}

解决druid循环依赖问题


1.启动时排除DataSourceAutoConfiguration

 2.用import以全路径的形式注入bean

/**
 * 
 *  解决druid循环依赖问题
 *  1.启动时排除DataSourceAutoConfiguration
 *  2.用import以全路径的形式注入bean
 * 
 */
@EnableAspectJAutoProxy
@Import(DataSourcesConfig.class)
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class BusinessApplication {

    public static void main(String[] args) {
        SpringApplication.run(BusinessApplication.class, args);
    }

}

使用Spring Boot在同一个应用中使用MySQL和MongoDB双数据源可以通过以下步骤实现: 1. 添加依赖 在pom.xml文件中添加MySQL和MongoDB的依赖: ```xml <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>3.12.7</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>2.2.6.RELEASE</version> </dependency> ``` 2. 配置数据源 在application.properties文件中配置MySQL和MongoDB的数据源: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/dbname?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.initial-size=5 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-active=20 spring.datasource.druid.test-on-borrow=true spring.datasource.druid.test-while-idle=true spring.datasource.druid.time-between-eviction-runs-millis=60000 spring.datasource.druid.validation-query=select 1 from dual spring.data.mongodb.uri=mongodb://localhost:27017/dbname ``` 3. 配置数据源连接池 在application.properties文件中配置Druid连接池: ```properties spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.filters=stat,wall spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 ``` 4. 配置JdbcTemplate和MongoTemplate 在Spring Boot应用程序中,可以使用JdbcTemplate和MongoTemplate来访问MySQL和MongoDB数据源。 ```java @Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return DruidDataSourceBuilder.create().build(); } @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter) { return new MongoTemplate(mongoDbFactory, mappingMongoConverter); } } ``` 5. 使用数据源 在需要使用MySQL和MongoDB数据源的类中,注入JdbcTemplate和MongoTemplate,并使用它们来访问数据库。 ```java @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private MongoTemplate mongoTemplate; public void addUser(User user) { // 使用JdbcTemplate访问MySQL数据源 jdbcTemplate.update("INSERT INTO user (id, username, password) VALUES (?, ?, ?)", user.getId(), user.getUsername(), user.getPassword()); // 使用MongoTemplate访问MongoDB数据源 mongoTemplate.save(user); } } ``` 以上就是使用Spring BootDruid实现MySQL和MongoDB双数据源的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值