精华内容
下载资源
问答
  • java项目配置多数据源
    万次阅读
    2019-05-26 13:59:36

    有些web应用需要连接多个数据源,本文讲解一下如何使用多个数据源,大题思路是这样的,系统启动的时候创建多个数据源,然后具体执行sql的时候去切换数据源执行对应的sql。如何切换数据源呢?spring提供了一个AbstractRoutingDataSource抽象类,只要继承这个类就可以了,这个类需要设置多个数据源,每个数据源有一个key对应,继承这个类必须实现determineCurrentLookupKey()方法,这个方法返回一个Object值,这个值应该是数据源的key,执行sql的时候会调用这个方法 获取key,然后根据这个key获取到的数据源执行sql。下面看具体的例子。
    前面说了determineCurrentLookupKey()方法的返回值决定,选择什么数据库,方法执行的时候如何动态设置返回值呢?为了不影响其他线程的使用,使用线程本地变量是最好的,这样只会影响当前线程。看如下类

    public class DataSourceRouter {
        //默认数据源的key
        public final static String DEFAULT = "default";
        //数据源1的key
        public final static String KEY_ONE = "key_one";
        private final static ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> DEFAULT);
    
        /**
         * 获取线程本地变量
         *
         * @return
         */
        static String getCurrentDataSourceKey() {
            return threadLocal.get();
        }
    
        /**
         * @param key      数据源对应的key
         * @param supplier sql执行的方法
         * @param <T>
         * @return sql执行的结果
         */
        public static <T> T doWithKey(String key, Supplier<T> supplier) {
            threadLocal.set(key);
            T t = supplier.get();
            threadLocal.set(DEFAULT);
            return t;
        }
    }
    
    

    这里通过doWithKey执行sql,通过determineCurrentLookupKey动态获取当前数据源。
    下面来实现AbstractRoutingDataSource

    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        @Autowired
        ApplicationContext applicationContext;
    
        /**
         * 是否在创建数据源时,立即初始化连接
         */
        private boolean initConnectionsOnCreate = false;
    
        /**
         * 返回配置的数据源的key,这样下面执行的sql就是使用该数据源的
         *
         * @return
         */
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceRouter.getCurrentDataSourceKey();
        }
    
        public void init() {
            DruidDataSource defaultTargetDataSource = applicationContext.getBean("defaultDataSource", DruidDataSource.class);
            DruidDataSource dataSource1 = applicationContext.getBean("dataSourceTemplate", DruidDataSource.class);
            dataSource1.setUrl("jdbc:mysql://localhost:3306/social?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
            dataSource1.setUsername("root");
            dataSource1.setPassword("998973");
            if (initConnectionsOnCreate) {
                try {
                    defaultTargetDataSource.init();
                    dataSource1.init();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            Map<Object, Object> map = new HashMap<>();
            map.put(DataSourceRouter.DEFAULT, defaultTargetDataSource);
            map.put(DataSourceRouter.KEY_ONE, dataSource1);
            setTargetDataSources(map);
            //默认先执行配置文件中的targetDataSources属性的数据源,这里设置的数据源不会生效,必须调用afterPropertiesSet
            afterPropertiesSet();
        }
    
        public void setInitConnectionsOnCreate(boolean initConnectionsOnCreate) {
            this.initConnectionsOnCreate = initConnectionsOnCreate;
        }
    }
    

    这里说明了,配置文件中配置的targetDataSources是必配的,对象创建的时候会读取这个配置然后调用afterPropertiesSet加载数据源,因为我们这里的数据源可能通过数据库等其他途径获取,所以没有写在配置文件中,这里需要手动调用一下afterPropertiesSet重新设置一下数据源。
    配置文件

    <?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:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <!-- 数据源模板,动态增加数据源时需要用到,scope是prototype,非单例对象 -->
        <bean id="dataSourceTemplate" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="filters" value="stat"/>
            <property name="maxActive" value="20"/>
            <property name="initialSize" value="1"/>
            <property name="maxWait" value="60000"/>
            <property name="minIdle" value="1"/>
            <property name="timeBetweenEvictionRunsMillis" value="60000"/>
            <property name="minEvictableIdleTimeMillis" value="300000"/>
            <property name="testWhileIdle" value="true"/>
            <property name="testOnBorrow" value="false"/>
            <property name="testOnReturn" value="false"/>
            <property name="poolPreparedStatements" value="true"/>
            <property name="maxOpenPreparedStatements" value="20"/>
            <property name="asyncInit" value="true"/>
        </bean>
    
        <bean id="defaultDataSource" parent="dataSourceTemplate">
            <property name="url"
                      value="jdbc:mysql://localhost:3306/easytour?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="998973"/>
        </bean>
    
        <!--统一的dataSource-->
        <bean id="dynamicDataSource" class="com.zhan.design.config.datasource.DynamicDataSource" init-method="init">
            <!-- 设置true时,随便一个key,找不到就走默认数据源,很可能带来不好的效果 -->
            <property name="lenientFallback" value="false"/>
            <property name="targetDataSources">
                <map></map>
            </property>
            <!--设置默认的dataSource-->
            <property name="defaultTargetDataSource" ref="defaultDataSource">
            </property>
            <!--是否在创建数据源时,立即初始化连接-->
            <property name="initConnectionsOnCreate" value="true"/>
        </bean>
    
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dynamicDataSource"/>
            <!--扫描mybatis配置文件,在哪里可以做细配置-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
            <!--扫描映射文件所在目录-->
            <property name="mapperLocations" value="classpath:com/zhan/design/mapper/**/*.xml"/>
        </bean>
    
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--扫描接口的基础包,会把该包下面的所有接口注册为spring的bean-->
            <property name="basePackage" value="com.zhan.design.dao"/>
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        </bean>
    
        <!--配置spring的事务-->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dynamicDataSource"/>
        </bean>
    
        <!--开启事务注解-->
        <tx:annotation-driven transaction-manager="txManager"/>
    </beans>
    
    

    这样就完成了
    下面看下具体使用的例子

        @GetMapping("/test")
        public Map<String, String> test() {
            //通过调用doWithKey就完成对数据源的切换了
            String label1 = DataSourceRouter.doWithKey(DataSourceRouter.DEFAULT, () -> dictionaryService.getByTypeAndKey("1", "1"));
            String label2 = DataSourceRouter.doWithKey(DataSourceRouter.KEY_ONE, () -> dictionaryService.getByTypeAndKey("1", "1"));
            Map<String, String> map = new HashMap<>();
            map.put("default_label", label1);
            map.put("one_label", label2);
            return map;
        }
    

    至此就完成了。

    更多相关内容
  • Spring动态切换多数据源Demo

    千次下载 热门讨论 2015-02-03 15:24:55
    请自行修改com/resources/datasource.properties中数据库配置,Demo中配置的两个数据源,一个是Mysql,一个是Oracle。 运行之前请自行建立数据库的表。
  • 为什么要配置数据源ODBC

    千次阅读 热门讨论 2016-08-18 13:35:37
    1.敲完学生和机房后,只知道配置ODBC数据源是为了连接vb与SQL 实现vb与数据库的完美交互,那么为什么要配置ODBC那: 主要是方便管理 1 你肯定链接数据库 2 那么你肯定用户名和密码 3 正式的数据库和应用...

    1.敲完学生和机房后,只知道配置ODBC数据源是为了连接vb与SQL 实现vb与数据库的完美交互,那么为什么要配置ODBC那:

    主要是方便管理

    1 你肯定要链接数据库

    2 那么你肯定要用户名和密码

    3 正式的数据库和应用服务器应该是单独的人员管理,而不是开发人员

    4 密码会定期修改

    5 如果链接数据库是各自书写代码和配置,则运行环境的密码修改将会是一个噩梦,一不小心就忘记一个

    6 所以,大家全部到一个数据源那里获取连接。管理员只需要修改数据源的配置,而无需修改应用的配置

    7 如果数据库的地址变更,则同样不会影响到应用,也只是修改数据源

    8 开发人员无需知道正式数据库的密码


    目前的数据源都是基于数据库连接池技术的,通过连接池连接数据库效率更高

    连接池有关知识http://baike.baidu.com/link?url=BTsngDU3VzxxGbvm8BtGFMt7zN-7huzbhO0mnu9JCsfRSWpjcqZpw5EBiblahMKs9rCqFbZOw3rLKgsqUbr6R_

    JDBC有关知识http://baike.baidu.com/link?url=22vA35WgnTuHvdSyW2E4yOaJBdWPkDNtti3LdwChtt4FKLJKKzrUCXfATezhykZNptTmqRou02i5rmOVi7YQhK

    2.名词解释即用途

    ODBC即OPEN DATABASE CONNECITON的缩写,意为开放数据库连接,是微软提出的一种数据访问的方法,只要数据库提供了ODBC驱动程序,应用程序就能以ODBC的方式访问数据库中的数据。 

            为了便于访问数据,WINDOWS系统提供了ODBC数据源管理工具,该工具用来设置数据源的名字


    DSN(DATA SOURCE NAME)。所谓DSN只不过是一个数据源的标志,设置它的目的是便于应用程序访问数据,也就是说,只要为某个数据库设置了相应的DSN,应用程序就不必理会该数据库存储的位置和驱动程序,可以按DSN直接访问数据库。 


            DSN有三种类型:用户DSN、系统DSN和文件DSN,下面分别讲述这三种DSN。 
            用户DSN  用户DSN只对设置它的用户可见,而且只能在设置了该DSN的机器上使用。 
            系统DSN  系统DSN对机器上的所用户都是可见的,包括NT服务。

            文件DSN  将DSN的配置信息存在一个文件里,这样的文件就叫文件DSN。


    展开全文
  • Spring+SpringMVC+Mybatis多数据源

    千次下载 热门讨论 2015-12-15 15:14:18
    Spring+SpringMVC+Mybatis多数据源整合。基于spring3.0和mybatis3.2的
  • DBCP 数据源jar包

    千次下载 热门讨论 2012-08-13 17:44:34
    其中包括两个jar包,commons-dbcp.jar,commons-pool.jar
  • 这样是挺麻烦的,每添加一个数据源,都要做重复的工作,而且好多代码都是重复的。 现在小弟的做法是,把xml的配置转到用java代码装配,数据源参数从数据库获取,按xml配置文件的意思,只要给...
  • Spring Boot, Spring Cloud项目,变单数据源数据源配置问题

    应项目的需求,要把项目改成多数据源,将两张表单独抽取出来,放入另一个数据库中,由于spring cloud 架构不熟悉,也是花了点时间才做好。

    1、禁用srping boot自动配置的单数据源,
    启动类上加上注解@SpringBootApplication(exclude= DataSourceAutoConfiguration.class)

    @SpringBootApplication(exclude= DataSourceAutoConfiguration.class)
    @EnableDiscoveryClient
    @EnableFeignClients
    public class UserServerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(UserServerApplication.class, args);
        }
    }
    

    2、修改配置文件,项目中用spring cloud config统一管理配置文件的
    在datasource节点下再加一个新数据源配置,如下所示

    datasource:
      user-server:
        url: jdbc:mysql://***?characterEncoding=utf-8&autoReconnect=true&f***
        username: ***
        password: ***
        driver-class-name: com.mysql.jdbc.Driver
        max-idle: 10
        max-wait: 10000
        min-idle: 5
        initial-size: 5
        validation-query: SELECT 1
        test-on-borrow: false
        test-while-idle: true
        time-between-eviction-runs-millis: 18800
      user-server-separate:
        url: jdbc:mysql://***?characterEncoding=utf-8&autoReconnect=true****
        username: ***
        password: ***
        driver-class-name: com.mysql.jdbc.Driver
        max-idle: 10
        max-wait: 10000
        min-idle: 5
        initial-size: 5
        validation-query: SELECT 1
        test-on-borrow: false
        test-while-idle: true
        time-between-eviction-runs-millis: 18800

    3、分包  两个数据库对应两个实体类的包,xml文件也一样,分两个文件夹,如下图所示
    这里写图片描述
    domain 和 domain_separate 存放不同库的实体类
    这里写图片描述
    mybatis和separate存放不同库对应的xml文件

    4、创建新的数据源
    这里写图片描述
    UcSeparateDataSourceConfig这是新配置的数据源,具体内容如下

    @Configuration
    @MapperScan(basePackages = {"com.baojun.user_server.application.domain_separate"}, sqlSessionFactoryRef = "ucSeparateSqlSessionFactory")
    public class UcSeparateDataSourceConfig {
        @Primary
        @Bean(name = "ucSeparateDataSource")
        @ConfigurationProperties(prefix = "datasource.user-server-separate")
        public DataSource dataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Primary
        @Bean(name = "ucSeparateTransactionManager")
        public DataSourceTransactionManager transactionManager(@Qualifier("ucSeparateDataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    
        @Primary
        @Bean(name = "ucSeparateSqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory(@Qualifier("ucSeparateDataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource);
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:separate/*.xml"));
            return factoryBean.getObject();
        }
    
        @Bean(name = "ucSeparateSqlSessionTemplate")
        @Primary
        public SqlSessionTemplate sqlSessionTemplate(@Qualifier("ucSeparateSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }

    这里要注意包路径和xml文件的路径,
    @ConfigurationProperties(prefix = “datasource.user-server-separate”)前缀与配置文件中一致

    5、在需要更改数据源的地方,加入对应的注解
    @Resource(name=”ucSeparateSqlSessionTemplate”)这个就是新配置的数据源

    @Repository
    public class CarDaoImpl extends BaseDaoImpl<Car> implements CarDao {
        public CarDaoImpl() {
        super(Car.class);
        }
        @Resource(name="ucSeparateSqlSessionTemplate")
        public void sqlSessionTemplate(SqlSessionTemplate sessionTemplate) {
        setSqlSessionTemplate(sessionTemplate);
        }
    }

    http://blog.csdn.net/neosmith/article/details/61202084,这是本人参考的网上例子

    展开全文
  • 本文介绍两种切换数据库的方法。 方法1:数据库信息都配置在spring xml中,适用于一般数据库切换。...方法2:将数据库信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。...

    鉴于很多同学反应,在看的过程中,自己搭建的demo,没有成功。
    首先附上本项目github地址,可以对照着看,看一下相关的配置是否正确配置。

    本文介绍两种动态切换数据库的方法。
    方法一:数据源信息配置在xml中,适用于一般数据库切换。执行完某操作,切换数据库,执行另一个操作。
    方法二:数据源信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。

    两种方法核心都是AbstractRoutingDataSource,由spring提供,用来动态切换数据源。我们需要继承它,来进行操作。本博客中,隐藏了部分spring相关的配置信息。

    源码分析可以看下一篇文章源码分析

    方法一:数据源信息都配置在xml中

    1…继承AbstractRoutingDataSource,重写determineCurrentLookupKey方法

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class MultiDataSource extends AbstractRoutingDataSource{
    
    	/* ThreadLocal,叫线程本地变量或线程本地存储。
    	 * ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
    	 * 这里使用它的子类InheritableThreadLocal用来保证父子线程都能拿到值。
    	 */
    	private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();
    	
    	/**
    	 * 设置dataSourceKey的值
    	 * @param dataSource
    	 */
    	public static void setDataSourceKey(String dataSource) {
    		dataSourceKey.set(dataSource);
    	}
    	/**
    	 * 清除dataSourceKey的值
    	 */
    	public static void toDefault() {
    		dataSourceKey.remove();
    	}
    	/**
    	 * 返回当前dataSourceKey的值
    	 */
    	@Override
    	protected Object determineCurrentLookupKey() {
    		return dataSourceKey.get();
    	}
    }
    

    2… 配置xml,这里将上面创建的MultiDataSource注入到spring容器中,这里主要用到AbstractRoutingDataSource的两个属性defaultTargetDataSource和targetDataSources。defaultTargetDataSource默认目标数据源,targetDataSources(map类型)存放用来切换的数据源。配置完以后,其他地方用到数据源的话,都引用multiDataSource。

    <?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:context="http://www.springframework.org/schema/context"
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    
    
    	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
    		init-method="init" destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    			<property name="url" value="第一个数据库的url" />
    			<property name="username" value="账号" />
    			<property name="password" value="密码" />
    			<property name="validationQuery" value="select 1"></property>
    			<property name="testOnBorrow" value="true"></property>
    	</bean>
    		<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource"
    		init-method="init" destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    		<property name="url" value="第二个数据库的url" />
    		<property name="username" value="账号" />
    		<property name="password" value="密码" />
    		<property name="validationQuery" value="select 1"></property>
    		<property name="testOnBorrow" value="true"></property>
    	</bean>
    	<bean id="multiDataSource" class="com.wbb.dataSource.MultiDataSource">
    		<property name="defaultTargetDataSource" ref="dataSource"></property>
    		<property name="targetDataSources">
    			<map>
    				<entry key="dataSource2" value-ref="dataSource2"></entry>
    			</map>
    		</property>
    	</bean>
    	<!-- mybatis -->
    	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    		<property name="dataSource" ref="multiDataSource"></property>
    		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
    		<property name="mapperLocations" value="classpath:com/wbb/mapper/*.xml"></property>
    	</bean>
    	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    		<property name="basePackage" value="com.wbb.mapper"></property>
    	</bean>
    
    	<!-- 添加事务管理 -->
    	<tx:annotation-driven transaction-manager="transactionManager" />
    
    	<bean id="transactionManager"
    		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="multiDataSource"></property>
    	</bean>
    
    </beans>
    

    3…手动切换数据源,切换完以后,记得再切回默认数据库。

    MultiDataSource.setDataSourceKey("dataSource2");//切换到dataSource2数据源
    XXX在该数据源下的操作XXX
    MultiDataSource.toDefault();//操作完以后,清除dataSourceKey的值,即切回默认数据源,原理后面会讲。
    

    4… 利用aop切换数据源,这里记得开启aop,配置文件中使用<aop:aspectj-autoproxy />
    4.1首先定义一个注解,来调用注解切换数据库

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DynamicRoutingDataSource {
    
    	String value() default "dataSource";//本文默认dataSource
    
    }
    

    4.2 这里使用@Before和@After,在调用目标方法前,进行aop拦截,通过解析注解上的值来切换数据源。在调用方法结束后,切回默认数据源。如果目标方法无返回值,也可以使用@Around,调用ProceedingJoinPoint的proceed()方法前切换数据源,调用proceed()方法后切回默认数据源。

    import java.lang.reflect.Method;
    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.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import com.wbb.annotation.DynamicRoutingDataSource;
    import com.wbb.dataSource.MultiDataSource;
    
    @Aspect
    @Component
    public class HandlerDataSourceAop {
    
    	private static Logger logger = LoggerFactory.getLogger(HandlerDataSourceAop.class);
    	/**
    	 * @within匹配类上的注解
    	 * @annotation匹配方法上的注解
    	 */
    	@Pointcut("@within(com.wbb.annotation.DynamicRoutingDataSource)||@annotation(com.wbb.annotation.DynamicRoutingDataSource)")
    	public void pointcut(){}
    	
    	@Before(value = "pointcut()")
    	public void beforeOpt(JoinPoint joinPoint) {
    	/** 先查找方法上的注解,没有的话再去查找类上的注解
    	*-----------------------------------------------------------------------
     	* 这里使用的是接口的模式,注解在实现类上,所以不能使用如下方式获取目标方法的对象,
    	* 因为该方式获取的是该类的接口或者顶级父类的方法的对象.
    	* MethodSignature methodSignature = (MethodSignature)point.getSignature();
         * Method method = methodSignature.getMethod();
         * DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
         * 通过上面代码是获取不到方法上的注解的,如果真要用上面代码来获取,可以修改aop代理模式,修改为cglib代理
         * 在xml配置文件修改为<aop:aspectj-autoproxy proxy-target-class="true" /> ,
         * proxy-target-class属性true为cglib代理,默认false为jdk动态代理 。
         * ---------------------------------------------------------
         * 本文使用是jdk动态代理, 这里使用反射的方式获取方法
    	*/
    	//反射获取Method 方法一
    	Object target = joinPoint.getTarget();
    	Class<?> clazz = target.getClass();
    	Method[] methods = clazz.getMethods();
    	DynamicRoutingDataSource annotation = null;
    	for (Method method : methods) {
    		if (joinPoint.getSignature().getName().equals(method.getName())) {
    			annotation = method.getAnnotation(DynamicRoutingDataSource.class);
    			if (annotation == null) {
    				annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
    				if (annotation == null) {
    					return;
    				}
    			}
    		}
    	}
    	// 	反射获取Method 方法二
    	//		Object[] args = joinPoint.getArgs();
    	//		Class<?>[] argTypes = new Class[joinPoint.getArgs().length];
    	//		for (int i = 0; i < args.length; i++) {
    	//			argTypes[i] = args[i].getClass();
    	//		}
    	//		Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), argTypes);
    	//		DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
    	//		if (annotation == null) {
    	//			annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
    	//			if (annotation == null) {
    	//				return;
    	//			}
    	//		}
    		
    		String dataSourceName = annotation.value();
    		MultiDataSource.setDataSourceKey(dataSourceName);
    		logger.info("切到" + dataSourceName + "数据库");
    	}
    	@After(value="pointcut()")
    	public void afterOpt(){
    		MultiDataSource.toDefault();
    		logger.info("切回默认数据库");
    	}
    }
    

    4.3 使用:只需要把@DynamicRoutingDataSource注解加到方法或者类上即可

    @DynamicRoutingDataSource("dataSource2")
    

    方法一到此就结束了

    方法二:数据源信息配置在数据库中

    1.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:context="http://www.springframework.org/schema/context"
    		xmlns:tx="http://www.springframework.org/schema/tx"
    		xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    	
    		<bean id="defaultDataSource" class="com.alibaba.druid.pool.DruidDataSource"
    			init-method="init" destroy-method="close">
    			<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    			<property name="url" value="第一个数据库的url" />
    			<property name="username" value="账号" />
    			<property name="password" value="密码" />
    			<property name="validationQuery" value="select 1"></property>
    			<property name="testOnBorrow" value="true"></property>
    		</bean>
    	
    		<!--动态数据源相关-->
    	    <bean id="dynamicDataSource" class="com.wbb.dataSource.dynamic.DynamicDataSource">
    	        <property name="targetDataSources">
    	            <map key-type="java.lang.String">
    	                <entry key="defaultDataSource" value-ref="defaultDataSource"/>
    	            </map>
    	        </property>
    	        <property name="defaultTargetDataSource" ref="defaultDataSource"/>
    	    </bean>
    		<!-- mybatis -->
    		<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    			<property name="dataSource" ref="dynamicDataSource"></property>
    			<property name="configLocation" value="classpath:mybatis-config.xml"></property>
    			<property name="mapperLocations" value="classpath:com/wbb/mapper/*.xml"></property>
    		</bean>
    		<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    			<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    			<property name="basePackage" value="com.wbb.mapper"></property>
    		</bean>
    	
    		<!-- 添加事务管理 -->
    		<tx:annotation-driven transaction-manager="transactionManager" />
    	
    		<bean id="transactionManager"
    			class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    			<property name="dataSource" ref="dynamicDataSource"></property>
    		</bean>
    	
    	</beans>
    

    2.数据源表的设计

    DROP TABLE IF EXISTS `other_datasource`;
    CREATE TABLE `other_datasource` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `datasource_name` varchar(64) DEFAULT NULL,
      `database_ip` varchar(64) DEFAULT NULL,
      `database_port` varchar(64) DEFAULT NULL,
      `database_name` varchar(64) DEFAULT NULL,
      `database_username` varchar(64) DEFAULT NULL,
      `database_password` varchar(64) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    

    这里写图片描述
    3.数据源表对应的bean

    /**
     * 数据源bean构建器
     */
    public class DataSourceBean {
    
    	private final String beanName;		//注册在spring中bean名字
    	private final String driverClassName; 
    	private final String url;
    	private final String username;
    	private final String password;
    	private final String validationQuery;
    	private final Boolean testOnBorrow;
    
    	public String getBeanName() {
    		return beanName;
    	}
    
    	public String getDriverClassName() {
    		return driverClassName;
    	}
    
    	public String getUrl() {
    		return url;
    	}
    
    	public String getUsername() {
    		return username;
    	}
    
    	public String getPassword() {
    		return password;
    	}
    
    	public String getValidationQuery() {
    		return validationQuery;
    	}
    
    	public Boolean getTestOnBorrow() {
    		return testOnBorrow;
    	}
    
    	public DataSourceBean(DataSourceBeanBuilder builder) {
    		this.beanName = builder.getBeanName();
    		this.driverClassName = builder.getDriverClassName();
    		this.url = builder.getUrl();
    		this.username = builder.getUsername();
    		this.password = builder.getPassword();
    		this.validationQuery = builder.getValidationQuery();
    		this.testOnBorrow = builder.getTestOnBorrow();
    	}
    
    	public static class DataSourceBeanBuilder {
    		private String beanName;
    		private String driverClassName = "com.mysql.jdbc.Driver";
    		private String url = "jdbc:mysql://%s:%s/%s";
    	    private String databaseIP;
    	    private String databasePort;
    	    private String databaseName;
    		private String username;
    		private String password;
    		private String validationQuery = "select 1";
    		private Boolean testOnBorrow = true;
    		public DataSourceBeanBuilder(String beanName, String databaseIP, String databasePort, String databaseName,
    				String username, String password) {
    			super();
    			this.beanName = beanName;
    			this.databaseIP = databaseIP;
    			this.databasePort = databasePort;
    			this.databaseName = databaseName;
    			this.username = username;
    			this.password = password;
    		}
    		public DataSourceBeanBuilder() {
    			super();
    		}
    		public DataSourceBeanBuilder driverClassName(String driverClassName) {
    			this.driverClassName = driverClassName;
    			return this;
    		}
    		public DataSourceBeanBuilder validationQuery(String validationQuery) {
    			this.validationQuery = validationQuery;
    			return this;
    		}
    		public DataSourceBeanBuilder testOnBorrow(Boolean testOnBorrow) {
    			this.testOnBorrow = testOnBorrow;
    			return this;
    		}
    		public String getUrl() {
    			return String.format(url,this.databaseIP,this.databasePort,this.databaseName);
    		}
    		public String getBeanName() {
    			return beanName;
    		}
    		public String getDriverClassName() {
    			return driverClassName;
    		}
    		public String getDatabaseIP() {
    			return databaseIP;
    		}
    		public String getDatabasePort() {
    			return databasePort;
    		}
    		public String getDatabaseName() {
    			return databaseName;
    		}
    		public String getUsername() {
    			return username;
    		}
    		public String getPassword() {
    			return password;
    		}
    		public String getValidationQuery() {
    			return validationQuery;
    		}
    		public Boolean getTestOnBorrow() {
    			return testOnBorrow;
    		}
    	}
    

    4.这里将操作抽离出,建立数据源操作类,方便操作

    /**
     * 数据源操作类
     */
    public class DataSourceContext {
    
    	private static ThreadLocal<DataSourceBean> threadLocal = new InheritableThreadLocal<DataSourceBean>();
    
    	/**
    	 * 获取数据源
    	 */
    	public static DataSourceBean getDataSource() {
    		return threadLocal.get();
    	}
    	/**
    	 * 设置数据源
    	 */
    	public static void setDataSource(DataSourceBean dataSourceBean) {
    		threadLocal.set(dataSourceBean);
    	}
    	/**
    	 * 清除数据源
    	 * 清除后,数据源为默认时间
    	 */
    	public static void toDefault() {
    		threadLocal.remove();
    	}
    }
    
    

    5.定义AbstractRoutingDataSource的继承类DynamicDataSource,同时实现ApplicationContextAware接口,因为该方法切换数据源,需要到spring上下文中生成和获取数据源bean。

    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    import com.alibaba.druid.pool.DruidDataSource;
    
    
    public class  DynamicDataSource extends AbstractRoutingDataSource implements ApplicationContextAware{
    
    	private ApplicationContext applicationContext ;
    
    	/**
    	 * 连接数据源前,调用该方法
    	 */
    	@Override
    	protected Object determineCurrentLookupKey() {
    		//1.获取手动设置的数据源参数DataSourceBean
    		DataSourceBean dataSourceBean = DataSourceContext.getDataSource();
    		if(dataSourceBean == null) {
    			return null;
    		}
    		try {
    			//2.获取AbstractRoutingDataSource的targetDataSources属性,该属性存放数据源属性
    			Map<Object, Object> targetSourceMap = getTargetSource();
    			synchronized(this) {
    				/*
    				 * 3.判断targetDataSources中是否已经存在要设置的数据源bean
    				 * 存在的话,则直接返回beanName
    				 * 
    				 */
    				if(!targetSourceMap.keySet().contains(dataSourceBean.getBeanName())) {
    					/*不存在,则进行以下几步
    					3.1 先在spring容器中创建该数据源bean
    					*/
    					Object dataSource = createDataSource(dataSourceBean);
    					//3.2 在创建后的bean,放入到targetDataSources Map中
    					targetSourceMap.put(dataSourceBean.getBeanName(), dataSource);
    					/*
    					 * 3.3 通知spring有bean更新
    					 * 主要更新AbstractRoutingDataSource的resolvedDefaultDataSource(Map)属性,
    					 * 更新完以后,AbstractRoutingDataSource的determineTargetDataSource()中,才能找到数据源
    					 * 代码如下:
    					 * Object lookupKey = determineCurrentLookupKey();
    					   DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    					 */
    					super.afterPropertiesSet();
    				}
    			}
    			for(Map.Entry<Object, Object> entry : targetSourceMap.entrySet()) {
    				System.out.println(entry.getKey()+"-"+entry.getValue());
    			}
    			return dataSourceBean.getBeanName();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    	/**
    	 * 根据数据源信息在spring中创建bean,并返回
    	 * @param dataSourceBean 数据源信息
    	 * @return
    	 * @throws IllegalAccessException
    	 */
    	public Object createDataSource(DataSourceBean dataSourceBean) throws IllegalAccessException {
    		//1.将applicationContext转化为ConfigurableApplicationContext
    		ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
    		//2.获取bean工厂并转换为DefaultListableBeanFactory
    		DefaultListableBeanFactory beanFactory =  (DefaultListableBeanFactory) context.getBeanFactory();
    		/*
    		 * 3.本文用的是DruidDataSource,所有在这里我们获取的是该bean的BeanDefinitionBuilder,
    		 * 通过BeanDefinitionBuilder来创建bean定义
    		 */
    		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);
    		/**
    		 * 4.获取DataSourceBean里的属性和对应值,并将其交给BeanDefinitionBuilder创建bean的定义
    		 */
    		Map<String, Object> propertyKeyValues = getPropertyKeyValues(DataSourceBean.class, dataSourceBean);
    		for(Map.Entry<String,Object> entry : propertyKeyValues.entrySet()) {
    			beanDefinitionBuilder.addPropertyValue(entry.getKey(), entry.getValue());
    		}
    		//5.bean定义创建好以后,将其交给beanFactory注册成bean对象,由spring容器管理
    		beanFactory.registerBeanDefinition(dataSourceBean.getBeanName(), beanDefinitionBuilder.getBeanDefinition());
    		//6.最后获取步骤5生成的bean,并将其返回
    		return context.getBean(dataSourceBean.getBeanName());
    	}
    	//获取类属性和对应的值,放入Map中
        @SuppressWarnings("unused")
    	private <T> Map<String, Object> getPropertyKeyValues(Class<T> clazz, Object object) throws IllegalAccessException {
           Field[] fields = clazz.getDeclaredFields();
           Map<String,Object> map = new HashMap<>();
           for (Field field : fields) {
        	   field.setAccessible(true);
        	   map.put(field.getName(), field.get(object));
           }
           map.remove("beanName");
           return map;
        }
        //通过反射获取AbstractRoutingDataSource的targetDataSources属性
    	@SuppressWarnings("unchecked")
    	public Map<Object, Object> getTargetSource() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    		Field field = AbstractRoutingDataSource.class.getDeclaredField("targetDataSources");
    		field.setAccessible(true);
    		return (Map<Object, Object>) field.get(this);
    	}
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    			this.applicationContext = applicationContext;
    	}
     
    }
    
    

    6.测试用例测试一下

    	@Test
    	public void testDynamicDataSource() {
    		1.从表中获取需要切换的数据源信息
    		List<DataSourceDO> dataSourceDOList = dataSourceMapper.getAllDataSources();
    		2.循环遍历,切换数据源,执行操作,切完就得切回默认数据源
    		for (DataSourceDO dataSourceDO : dataSourceDOList) {
    			DataSourceBean dataSourceBean = new DataSourceBean(new DataSourceBeanBuilder(dataSourceDO.getDatasourceName(),
    					dataSourceDO.getDatabaseIp(), dataSourceDO.getDatabasePort(), dataSourceDO.getDatabaseName(),
    					dataSourceDO.getUsername(), dataSourceDO.getPassword()));
    			DataSourceContext.setDataSource(dataSourceBean);
    			XXX你的操作XXX
    			DataSourceContext.toDefault();
    		}
    	}
    

    方法二部分参考https://blog.csdn.net/yizhenn/article/details/53965552该博客的思想。

    源码分析可以看下一篇文章https://blog.csdn.net/u013034378/article/details/81661706

    讲到这里,本篇文章到此也就结束了,如果文章中有问题,或者有一些不够严谨完善的地方,希望大家体谅体谅。欢迎大家留言,交流交流。
    最后附上本项目github的地址

    展开全文
  • SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用多个DataSource 然后创建多个SessionFactory,在使用Dao层的时候通过不同的...
  • 【JAVA】JAVA数据源

    千次阅读 多人点赞 2018-11-16 15:09:19
    文章目录前言连接池、数据源、JNDI连接池、数据源连接池思想JNDI直连数据源 前言 我们在进行数据访问,声明模板和repository之前【JAVA】Spring对JDBC的支持,都需要配置数据源用来连接数据库。数据源又可以分为两大...
  • Win10如何配置数据源ODBC数据源

    万次阅读 2019-05-06 20:03:25
    如何配置数据源ODBC数据源 1.打开控制面板,按下图,依次进行操作: 2.双击打开 “ODBC数据源” ,按下图进行操作: 3.选择 “Microsoft Access Driver”,如下图: 4.对 “ODBC Microsoft Access” 进行安装,...
  • 假设需要一个工具,由于保密需要只描述场景。 A下面有 B、C、D三家或者更多分公司,人员信息统一在总公司的A数据库,分公司各自使用各自的业务数据库,用户登陆访问A数据库校验成功后将用户所属分公司的KEY保存...
  • JdbcTemplate动态多数据源配置

    千次阅读 多人点赞 2019-09-04 22:44:24
    数据源就是javax.sql.DataSource,所有实现了这个接口的DataSource就叫做数据源,现在比较常用阿里巴巴的DruidDataSource,支持监控多数据源下的sql运行状况,便于以此实现以sql核心的应用系统,比如BI报表系统、...
  • Springboot整合Mybatis多数据源配置

    万次阅读 多人点赞 2019-06-09 14:06:44
    数据源配置类: 次数据源配置类: 三、项目结构(重点是mapper结构和配置类里的路径对应) 四、启动类——启动类需要取消加载数据源自动配置 五、测试: controller: Service: Mapper: 数据...
  • 为什么会有这篇文章呢,那是因为小编在使用mybatis-plus这个框架,明明一个多数据源配置很简单,但是网上其他博客着实太过于复杂了。 从数据源配置,每一个数据源配置一个config类,还有需要自己实现AOP的,当时...
  • SpringBoot多数据源解决方案

    万次阅读 多人点赞 2018-12-28 16:09:58
    最近在一个数据统计的项目,这里涉及到多个数据库的数据统计,所以找到了一个不错的解决方案。 写一篇博客作为笔记。 例子介绍 类库:例子中使用的是MyBatis Plus和Dynamic Datasource的组合 开源项目地址:...
  • spring多数据源解决方案

    万次阅读 多人点赞 2018-07-02 18:10:46
    在平时开发过程中,很多内部的项目都是直接访问多个数据库,这样平时一个项目一个数据库就不够用了,spring支持多数据源。笔者这里记录三种平时常看到的多数据源整合方式。 第一种:复制多个bean 情景:数据库的...
  • SpringBoot配置JPA多数据源

    千次阅读 2019-05-22 10:43:17
    SpringBoot版本 不同的版本其配置方式可能有所区别,我采用的是...多数据源配置 application.yml配置如下: spring: application: name: test jpa: hibernate: ddl-auto: update show-sql: true datasour...
  • BW系列之数据源

    千次阅读 2016-12-14 09:53:52
    BW系列之数据源
  • 数据源(DataSource)是什么以及SpringBoot中数据源配置

    千次阅读 多人点赞 2018-08-09 16:46:10
    数据源,简单理解数据源头,提供了应用程序所需要数据的位置。数据源保证了应用程序与目标数据之间交互的规范和协议,它可以是数据库,文件系统等等。其中数据源定义了位置信息,用户验证信息和交互时所需的一些...
  • 动态数据源切换

    千次阅读 2019-07-03 15:49:27
    简介 场景:在实际业务中,数据量迅速增长,一个库一个表已经满足不了我们的需求了,...首先将主、从数据库都加入自定义的数据源库DynamicRouterDataSource中(继承自springboot自己的数据源库AbstractRoutingDa...
  • 1、独立数据库,通过动态切换数据源来实现多租户,安全性最高,但成本也高。 2、共享数据库,隔离数据架构,比如使用oracle用多个schema。 3、共享数据库,共享数据库表,使用字段来区分不同租户,此...
  • 将Anaconda数据源切换国内源

    万次阅读 2020-02-13 20:10:23
    使用命令的方式直接添加国内 1 清华 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels ...
  • 业务需求或者业务的扩展需要分库,多数据源的配置使用在业务开发中常常会用到。MyBatis-plus官网(https://mp.baomidou.com/guide/dynamic-datasource.html)对如何配置多数据源给出了范例,但对于整合druid数据库...
  • 全栈工程师开发手册 (作者:栾鹏) 架构系列文章 官网:... 先在grafana的页面添加一个mysql的数据库 然后就可以创建面板,在面板中通过配置时间戳列,度量key,度量的value,进行显示。
  • 最近在配置spring框架时需要使用多数据源,其中遇到一些问题,特此记录 无事务数据源切换 ...主要说一下事务数据源切换的配置 首先在spring-database.xml中配置两个数据源(默认数据源),和应用的切面
  • 文引出了Flink程序自定义数据源的方法,我们来再次回顾下,并自定义数据源类 从Mysql中加载数据源
  • springboot+mybatisplus多数据源配置和使用(一)

    万次阅读 热门讨论 2019-05-15 15:39:52
    一般多数据源的配置方式有两种 - 静态的多数据源:每个数据源配置一套mapper - 动态的多数据源:多个数据源通用一套mapper
  • springboot结合druid使用多数据源,动态切换

    万次阅读 热门讨论 2019-06-10 14:39:48
    实现方法应该有不少,我知道有插件实现,判断写请求还是读请求来请求不同的数据库,还有代码实现,不同的SQL访问不同的数据源,也就是接下来说的多数据源。 一、基础介绍 代码层多数据源的实现方法也有很多,...
  • SpringBoot配置多Redis数据源

    万次阅读 多人点赞 2019-07-19 17:22:12
    最近在项目中用到了两个Redis数据源,特此记录一下 一. 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,625,690
精华内容 1,050,276
关键字:

做数据为什么要带上数据源