Base項目建立完成後需要內建資料庫,能夠做到連接配接多資料庫且能夠切換到指定資料庫。
這個就用到了AbstractRoutingDataSource
連接配接池使用的是druid
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
資料庫使用的是postgresql
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
其他引用檢視 點選打開連結
1.resources下建立application-dev.properties檔案。
2.application.properties 裡面新增内容 spring.profiles.active=dev 這樣我們可以将不同的環境配置檔案區分開。
3.application-dev.properties内新增一下資料源配置資訊。
#應用通路名稱
server.servlet.contextPath=/control
server.port=8088
#serviceNode是自定義,用于區分不同的服務節點,暫時可不關注
server.serviceNode=control
# 資料庫通路配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
spring.datasource.logSlowSql=false
spring.hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
spring.hibernate.hbm2ddl.auto=none
spring.hibernate.show_sql=false
string.hibernate.format_sql=false
#postgresql master為主資料源,也是預設資料源
spring.datasource.master.url=jdbc:postgresql://127.0.0.1:5432/control?currentSchema=control
spring.datasource.master.username=root
spring.datasource.master.password=root
spring.datasource.master.driverClassName=org.postgresql.Driver
spring.datasource.master.initialSize=5
spring.datasource.master.minIdle=5
spring.datasource.master.maxActive=20
spring.datasource.master.maxWait=60000
spring.datasource.master.timeBetweenEvictionRunsMillis=60000
spring.datasource.master.minEvictableIdleTimeMillis=300000
spring.datasource.master.testWhileIdle=true
spring.datasource.master.testOnBorrow=false
spring.datasource.master.testOnReturn=false
spring.datasource.master.filters=stat
#nodes為不同的資料源節點格式為:node1,node2,node3,……
spring.datasource.nodes=node1
spring.datasource.node1.url=jdbc:postgresql://127.0.0.1:5432/ccs?currentSchema=ccs
spring.datasource.node1.username=root
spring.datasource.node1.password=root
spring.datasource.node1.driverClassName=org.postgresql.Driver
spring.datasource.node1.initialSize=5
spring.datasource.node1.minIdle=5
spring.datasource.node1.maxActive=20
spring.datasource.node1.maxWait=60000
spring.datasource.node1.timeBetweenEvictionRunsMillis=60000
spring.datasource.node1.minEvictableIdleTimeMillis=300000
#spring.datasource.node1.validationQuery=SELECT 1 FROM DUAL
spring.datasource.node1.testWhileIdle=true
spring.datasource.node1.testOnBorrow=false
spring.datasource.node1.testOnReturn=false
spring.datasource.node1.filters=stat
4.資料源配置檔案建立完成後需要初始化資料源,建立不同的DataSource Bean
以下是初始化全過程:
package com.cloud.base.server.config;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
@Configuration
public class DruidConfig implements ApplicationContextAware {
@Autowired
private Environment env;
@Value("${spring.datasource.type}")
private Class<? extends DruidDataSource> dataSourceType;
@Value("${spring.datasource.logSlowSql}")
private String logSlowSql;
@Value("${spring.hibernate.dialect}")
private String dialect;
@Value("${spring.hibernate.hbm2ddl.auto}")
private String hbm2ddl;
@Value("${spring.hibernate.show_sql}")
private String show_sql;
@Value("${string.hibernate.format_sql}")
private String format_sql;
@Value("${spring.datasource.nodes}")
private String nodes;
private ConfigurableApplicationContext app;
@Bean(name = "masterDataSource", destroyMethod = "close", initMethod = "init")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DruidDataSource masterDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean(name = "dataSource")
public DynamicDataSource dataSource() {
ApplicationConfig.serviceNode = env.getProperty("server.serviceNode");
DefaultListableBeanFactory acf = (DefaultListableBeanFactory) app.getAutowireCapableBeanFactory();
BeanDefinitionBuilder bdb;
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
DynamicDataSourceContextHolder.dataSourceIds.add("master");
if (!StringUtils.isEmpty(nodes)) {
for (int i = 0; i < nodes.split(",").length; i++) {
String beanKey = nodes.split(",")[i];
final String prefix = "spring.datasource." + beanKey;
bdb = BeanDefinitionBuilder.rootBeanDefinition(DruidDataSource.class);
bdb.setDestroyMethodName("close");
bdb.setInitMethodName("init");
bdb.getBeanDefinition().setAttribute("id", beanKey);
bdb.addPropertyValue("driverClassName", env.getProperty(prefix + ".driverClassName"));
bdb.addPropertyValue("url", env.getProperty(prefix + ".url"));
bdb.addPropertyValue("username", env.getProperty(prefix + ".username"));
bdb.addPropertyValue("password", env.getProperty(prefix + ".password"));
bdb.addPropertyValue("initialSize", env.getProperty(prefix + ".initialSize"));
bdb.addPropertyValue("minIdle", env.getProperty(prefix + ".minIdle"));
bdb.addPropertyValue("maxActive", env.getProperty(prefix + ".maxActive"));
bdb.addPropertyValue("maxWait", env.getProperty(prefix + ".maxWait"));
bdb.addPropertyValue("timeBetweenEvictionRunsMillis", env.getProperty(prefix + ".minEvictableIdleTimeMillis"));
bdb.addPropertyValue("minEvictableIdleTimeMillis", env.getProperty(prefix + ".minEvictableIdleTimeMillis"));
bdb.addPropertyValue("validationQuery", env.getProperty(prefix + ".validationQuery"));
bdb.addPropertyValue("testWhileIdle", env.getProperty(prefix + ".testWhileIdle"));
bdb.addPropertyValue("testOnBorrow", env.getProperty(prefix + ".testOnBorrow"));
bdb.addPropertyValue("testOnReturn", env.getProperty(prefix + ".testOnReturn"));
bdb.addPropertyValue("filters", env.getProperty(prefix + ".filters"));
/** 以node名稱為Bean的ID 注入到Spring*/
acf.registerBeanDefinition(beanKey, bdb.getBeanDefinition());
targetDataSources.put(beanKey, app.getBean(beanKey));
DynamicDataSourceContextHolder.dataSourceIds.add(beanKey);
}
}
dynamicDataSource.setTargetDataSources(targetDataSources);
/** 設定master為預設資料源*/
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
dynamicDataSource.afterPropertiesSet();
return dynamicDataSource;
}
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("loginUsername", "admin");
reg.addInitParameter("loginPassword", "admin");
reg.addInitParameter("logSlowSql", logSlowSql);
return reg;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.addInitParameter("profileEnable", "true");
return filterRegistrationBean;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource());
factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);
/** 按實際項目路徑修改,必須包含Entity和Service*/
factory.setPackagesToScan("com.cloud");
Map<String, String> jpaProperties = new HashMap<String, String>();
jpaProperties.put("hibernate.dialect", dialect);
jpaProperties.put("hibernate.hbm2ddl.auto", hbm2ddl);
jpaProperties.put("hibernate.show_sql", show_sql);
jpaProperties.put("hibernate.format_sql", format_sql);
factory.setJpaPropertyMap(jpaProperties);
factory.afterPropertiesSet();
return factory;
}
/** 重置JpaTransactionManager EntityManagerFactory,否則資料源切換後事務将不生效*/
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
app = (ConfigurableApplicationContext) applicationContext;
}
}
package com.cloud.base.server.config;
import java.util.ArrayList;
import java.util.List;
public class DynamicDataSourceContextHolder {
/** 注意此處有陷阱,因為我們大多都會用到線程池,而ThreadLocal資料是和線程綁定的。
是以在每次處理請求完成後必須清空,否則下次線程複用是DataSource就是錯誤的。
處理方式可以選擇實作ServletRequestListener的requestDestroyed方法。
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static List<String> dataSourceIds = new ArrayList<String>();
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
public static boolean containsDataSource(String dataSourceId) {
return dataSourceIds.contains(dataSourceId);
}
}
package com.cloud.base.server.config;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
return DynamicDataSourceContextHolder.getDataSource();
}
}