springBootjpa多数据源的动态切换
这周笨⼩葱⼀直在研究如何使⽤springBoot的多数据源配置。
看了3天的⽹上的博客,发现⼤多数都是多数据源的配置,并没有很详细的关于使⽤springBoot的多数据源动态切换的配置。前者整体配置过程是在springBoot的原有的jpa实体管理⼯⼚(entityManagerFactory)的基础上(这⾥,entityManagerFactory会绑定⼀个数据源,⽽transactionManager 只需将entityManagerFactory注⼊就可以绑定数据源了)再次创建⼀个实体类管理⼯⼚,然后绑定另外⼀个数据源,但是各⾃entityManagerFactory都需要绑定各⾃的repository。这种配置适合⼀个⽤户操作不同的数据库。⽽如果要不同的⽤户操作不同的数据源,同时对应同⼀个repository。那么就不能够实现啦。所以需要实现数据源的动态切换。
这⾥第⼀种配置,笨⼩葱就不详解了,⽹上有很多资料。
详细说⼀下,关于springBoot jpa的多数据源动态切换。
这⾥我们配置2个数据源,通过动态数据源来切换,对应⼀个entityManagerFactory,⼀个repository。主要功能是不同的⽤户操作不同的数据库,但是他们的数据库结构是⼀样的,所调⽤的controller⽅法也是⼀样的。
实现原理:
1、扩展Spring的AbstractRoutingDataSource抽象类(该类充当了DataSource的路由中介, 能有在运⾏时, 根据某种key值来动态切换到真正的DataSource上。)
从AbstractRoutingDataSource的源码中:
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean
我们可以看到,它继承了AbstractDataSource,⽽AbstractDataSource不就是javax.sql.DataSource的⼦类,So我们可以分析下它的getConnection ⽅法:
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}
获取连接的⽅法中,重点是determineTargetDataSource()⽅法,看源码:
/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = (lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = solvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
上⾯这段源码的重点在于determineCurrentLookupKey()⽅法,这是AbstractRoutingDataSource类中的⼀个抽象⽅法,⽽它的返回值是你所要⽤的数据源dataSource的key值,有了这个key值,resolvedDataSource(这是个map,由配置⽂件中设置好后存⼊的)就从中取出对应的DataSource,如果不到,就⽤配置默认的数据源。
看完源码,应该有点启发了吧,没错!你要扩展AbstractRoutingDataSource类,并重写其中的determineCurrentLookupKey()⽅法,来实现数据源的切换:
package st.util.database;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 获取数据源(依赖于spring)
* @author linhy
*/
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
DataSource();
}
}
DataSourceHolder这个类则是我们⾃⼰封装的对数据源进⾏操作的类:
package st.util.database;
/**
* 数据源操作
* @author linhy
*/
public class DataSourceHolder {
//线程本地环境
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
//设置数据源
public static void setDataSource(String customerType) {
dataSources.set(customerType);
}
//获取数据源
public static String getDataSource() {
return (String) ();
}
//清除数据源
springboot aoppublic static void clearDataSource() {
}
}
创建完这2个类之后,只需要在调⽤controller的⽅法之前调⽤对应的数据源就可以了。调⽤数据源即:
DataSourceHolder.setDataSource (xxxx);
这样在执⾏controller⽅法之前就完成了当前线程(http请求)的数据源切换。
这个⽅法也是参考的⽹上的。但是如何将其整合⼊springBoot中,还需要⾃⼰调试⼀下。笨⼩葱花了2天时间,终于配置调试好了。下⾯配上springBoot的各个⽂件。
⾸先:创建上⾯的2个类。
DynamicDataSource.java :
package cc.study.springboot.domain;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* Created by Administrator on 2015/11/25.
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
DataSource();
}
}
DataSourceHolder.java :
package cc.study.springboot.domain;
/**
* Created by Administrator on 2015/11/25.
*/
public class DataSourceHolder {
//线程本地环境
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
/
/设置数据源
public static void setDataSource(String customerType) {
dataSources.set(customerType);
}
//获取数据源
public static String getDataSource() {
return (String) ();
}
//清除数据源
public static void clearDataSource() {
}
}
然后是多数据源与动态数据源的配置,以及entityManagerFactory和transactionManager的配置⽂件l:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance" xmlns:tx="/schema/tx"
xsi:schemaLocation="/schema/beans /schema/beans/spring-beans.xsd www.springfram >
<bean id="ds1" name="ds1"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" primary="true"> //这⾥多数
据源,springBoot启动时需要指定⼀个默认的数据源,所以 <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="jdbc:sqlserver://127.0.0.1:1433;databaseName=xxx"/>
<property name="username" value="test"/>
<property name="password" value="xxx"/>
</bean>
<bean id="ds2" name="ds2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="jdbc:sqlserver://127.0.0.1:1433;databaseName=cc"/>
<property name="username" value="test"/>
<property name="password" value="xxx"/>
</bean>
<!--动态选择数据源-->
<bean id="dataSource" class="cc.study.springboot.domain.DynamicDataSource" >
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="1" value-ref="ds1"/>
<entry key="2" value-ref="ds2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="ds1"/> //不可少
</bean>
<bean id="entityManagerFactory"
class="jpa.LocalContainerEntityManagerFactoryBean"
destroy-method="destroy" >
<property name="dataSource" ref="dataSource" /> //这⾥将动态数据源bean注⼊
<property name="jpaVendorAdapter">
<bean
class="jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.SQLServer2008Dialect"/>
<property name="showSql" value="true"/>
</bean>
</property>
<property name="packagesToScan" value="cc.study.springboot.domain"/>
<property name="jpaPropertyMap">
<map>
<entry key="javax.persistence.schema-generation.database.action" value="none"/>
</map>
</property>
</bean>
<bean id="transactionManager"
class="jpa.JpaTransactionManager">
<property name="entityManagerFactory"
ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论