spring+druid多数据源配置

博客引用处(以下内容在原有博客基础上进行补充或更改,谢谢这些大牛的博客指导):
spring+druid多数据源配置

druid多数据源配置

一、druid简介
Druid首先是一个数据库连接池,但它不仅仅是一个数据库连接池,它还包含一个ProxyDriver,一系列内置的JDBC组件库,一个SQL Parser。

Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。

Druid 是目前比较流行的高性能的,分布式列存储的OLAP框架(具体来说是MOLAP)。它有如下几个特点:

一. 亚秒级查询

druid提供了快速的聚合能力以及亚秒级的OLAP查询能力,多租户的设计,是面向用户分析应用的理想方式。

二.实时数据注入

druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性

三.可扩展的PB级存储

druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入。即便在加大数据规模的情况下,也能保证时其效性

四.多环境部署

druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括hadoop,spark,kafka,storm和samza等

五.丰富的社区

二、druid多数据源使用
1.直接配置

配置两个 dataSource,两个sqlSessionFactory,两个transactionManager,以及关键的地方在于MapperScannerConfigurer 的配置——使用sqlSessionFactoryBeanName属性,注入不同的sqlSessionFactory的名称,这样就为不同的数据库对应的 mapper 接口注入了对应的 sql于master-slave类型的多数据源配置而言不太适应,不支持分布式事务

2.基于AbstractRoutingDataSource和AOP的多数据源配置

我们自己定义一个DataSource类ThreadLocalRountingDataSource,来继承AbstractRoutingDataSource,然后在配置文件中向ThreadLocalRountingDataSource注入 master 和 slave 的数据源,然后通过 AOP 来灵活配置。

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd>
    
    <!-- 启动spring注解 -->
    <context:annotation-config/>
    <!-- 扫描注解所在的包 -->
    <context:component-scan base-package="com.example"/>
    <!-- 启动aop注解 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/> 
       
       <!-- 引入属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
  <!--   <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
    </bean> 
     -->
     <!-- 配置数据源master -->
     <bean id="dataSourceMaster" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">   
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}" />  
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />  
       <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />  
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="1" />  
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="20" />  
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />  
    </bean>  
    <!-- 配置数据源master -->
     <bean id="dataSourceSlave" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">   
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url_slave}" />  
        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />  
       <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />  
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0" />  
        <!-- 连接池最大空闲 -->
        <property name="maxIdle" value="20" />  
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />  
    </bean>  
     <bean id="dataSource" class="com.example.util.ThreadLocalRountingDataSource">
         <property name="targetDataSources">
             <map key-type="com.example.enums.DataSources">
                 <entry key="MASTER" value-ref="dataSourceMaster" />
                 <entry key="SLAVE" value-ref="dataSourceSlave"/>
             </map>
         </property>
         <property name="defaultTargetDataSource" ref="dataSourceMaster"></property>
        
    </bean> 
    
    <!-- 配置SQLSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>  
        <property name="configLocation" value="classpath:mybatis-config.xml"></property> 
        <!-- 加载映射文件 -->
        <property name="mapperLocations" value="classpath*:/com/example/dao/*Mapper.xml"></property>  
    </bean>
    
    <!-- 接口方式 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.example.dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean> 
  
</beans>

1.定义一个DataSource枚举类

//定义一个enum来表示不同的数据源
public enum DataSources {
    MASTER,SLAVE
}

2.ThreadLocalRountingDataSource,继承AbstractRoutingDataSource

public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource{

    @Override
    protected Object determineCurrentLookupKey() {
        // TODO Auto-generated method stub
        return DataSourceTypeManager.get();
    }

}

3.DataSourceTypeManager

//通过 TheadLocal 来保存每个线程选择哪个数据源的标志(key)

public class DataSourceTypeManager {
    
    private static final ThreadLocal<DataSources> dataSourceTypes=new ThreadLocal<>();
    
    public static DataSources get(){
        return dataSourceTypes.get();
    }
    
    public static void set(DataSources dataSourceType){
        dataSourceTypes.set(dataSourceType);
    }
    
    public static void reset(){
        dataSourceTypes.set(DataSources.MASTER);
    }
}

4.aop管理

@Aspect
@Component
public class DataSourceInterceptor {

    @Pointcut("execution(public * com.example.service..*.selectByPrimaryKey(..))")
    public void dataSourceMaster(){
        
    };
    
    @Before("dataSourceMaster()")
    public void before(JoinPoint jp){
        DataSourceTypeManager.set(DataSources.MASTER);
    }
    //...
    /*这里我们定义了一个 Aspect 类,我们使用 @Before 来在符合
     *  @Pointcut("execution(public * net.aazj.service..*.selectByPrimaryKey(..))") 中的方法被调用之前,
     *  调用 DataSourceTypeManager.set(DataSources.SLAVE) 设置了 key 的类型为 DataSources.MASTER,
     *  所以 dataSource 会根据key=DataSources.MASTER 选择 dataSourceSlave 这个dataSource。
     *  所以该方法对于的sql语句会在slave数据库上执行.
     *  我们可以不断的扩充 DataSourceInterceptor这个 Aspect,在中进行各种各样的定义,
     *  来为某个service的某个方法指定合适的数据源对应的dataSource。
     *  这样我们就可以使用 Spring AOP 的强大功能来,十分灵活进行配置了。
     */
}

当调用selectByPrimaryKey方法的时候会进入切面类中切换数据源,方法调用完毕会把数据源切换回来

三、AbstractRoutingDataSource原理
源码:

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean
void afterPropertiesSet() throws Exception; 
 
    @Override
    public void afterPropertiesSet() {
        if (this.targetDataSources == null) {
            throw new IllegalArgumentException("Property 'targetDataSources' is required");
        }
        this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
        for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
            Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
            DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
            this.resolvedDataSources.put(lookupKey, dataSource);
        }
        if (this.defaultTargetDataSource != null) {
            this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
        }
    }

targetDataSources 是我们在xml配置文件中注入的 dataSourceMaster 和 dataSourceSlave. afterPropertiesSet方法就是使用注入的dataSourceMaster 和 dataSourceSlave来构造一个HashMap——resolvedDataSources。方便后面根据 key 从该map 中取得对应的dataSource。

protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    Object lookupKey = determineCurrentLookupKey();
    DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
        dataSource = this.resolvedDefaultDataSource;
    }
    if (dataSource == null) {
        throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    }
    return dataSource;
}

Object lookupKey = determineCurrentLookupKey(); 该方法是我们实现的,在其中获取ThreadLocal中保存的 key 值。(见上述步骤2)获得了key之后,在从afterPropertiesSet()(InitializingBean中实现的方法)中初始化好了的resolvedDataSources这个map中获得key对应的dataSource。而ThreadLocal中保存 key 值。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个用于构建独立的、可执行的Spring应用程序的框架,简化了Spring应用程序的配置和部署。JPA(Java Persistence API)是一种用于管理Java对象和关系数据库之间映射的规范。Druid是阿里巴巴开源的关系型数据库连接池。 在Spring Boot中配置多数据源需要以下几步: 1. 引入相关依赖:需要引入Spring Boot、Spring Data JPA和Druid的相关依赖。 2. 配置数据源:在application.properties或application.yml文件中配置多个数据源的连接信息,并指定每个数据源的名称和相关属性。 3. 配置数据源连接池:使用@ConfigurationProperties注解创建多个数据源的连接池对象,并指定数据源的名称以及相关属性。 4. 配置实体管理器工厂:为每个数据源配置对应的实体管理器工厂,用于处理JPA实体与数据库之间的映射关系。 5. 配置事务管理器:为每个数据源配置对应的事务管理器,用于处理事务操作。 6. 配置数据源路由:创建动态数据源,根据传入的数据源名称选择对应的数据源进行操作。 7. 配置JPA的Repository:创建接口继承JpaRepository,用于定义数据访问方法。 通过以上步骤配置多数据源后,就可以在Spring Boot应用程序中使用多个数据源进行数据库的操作。可以根据需要在Service或Controller中使用@PersistenceContext注解指定具体的数据源,或者使用@Primary注解指定默认的数据源。 总结:通过Spring Boot的自动配置Druid连接池,可以很方便地实现多数据源配置。使用JPA进行数据操作,能够有效地减少开发人员编写SQL语句的工作量,提高开发效率。通过合理的配置,可以根据需要选择不同的数据源进行操作,实现灵活的数据访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值