spring boot+mybatis 多数据源切换
由于公司業(yè)務(wù)劃分了多個(gè)數(shù)據(jù)庫(kù),開(kāi)發(fā)一個(gè)項(xiàng)目會(huì)同時(shí)調(diào)用多個(gè)庫(kù),經(jīng)過(guò)學(xué)習(xí)我們采用了注解+aop的方式實(shí)現(xiàn)的
1.首先定義一個(gè)注解類
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TargetDataSource {String value();//此處接收的是數(shù)據(jù)源的名稱 }2.然后建一個(gè)配置類,這個(gè)在項(xiàng)目啟動(dòng)時(shí)會(huì)加載數(shù)據(jù)源,一開(kāi)始采用了HikariCP,查資料說(shuō)是最快性能最好的,然后又發(fā)現(xiàn)了阿里的druid,這個(gè)功能比較全面,而且性能也還可以,最主要他還有監(jiān)控功能,具體實(shí)現(xiàn)看如下代碼
?
package com.example.demo.datasource;import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import com.example.demo.datasource.DynamicDataSource; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.transaction.PlatformTransactionManager; import org.w3c.dom.NodeList; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node;import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.sql.DataSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.io.File; import com.alibaba.druid.support.http.StatViewServlet; /*** Author: wangchao* Version:* Date: 2017/9/11* Description:數(shù)據(jù)源配置* Modification History:* Date Author Version Description* --------------------------------------------------------------* Why & What is modified:*/@Configuration @EnableScheduling public class DataSourceConfig {/*@Autowiredprivate DBProperties properties;*/@Value("${datasource.filePath}")private String filePath;//數(shù)據(jù)源配置@Bean(name = "dataSource")public DataSource dataSource() {//按照目標(biāo)數(shù)據(jù)源名稱和目標(biāo)數(shù)據(jù)源對(duì)象的映射存放在Map中Map<Object, Object> targetDataSources = new HashMap<>();//查找xml數(shù)據(jù)連接字符串targetDataSources=getdataMap(filePath);//動(dòng)態(tài)獲取DBProperties類申明的屬性/*Field[] fields=properties.getClass().getDeclaredFields();for(int i=0;i<fields.length;i++){targetDataSources.put(fields[i].getName(), getFieldValueByName(fields[i].getName(),properties));}*///采用是想AbstractRoutingDataSource的對(duì)象包裝多數(shù)據(jù)源DynamicDataSource dataSource = new DynamicDataSource();dataSource.setTargetDataSources(targetDataSources);//設(shè)置默認(rèn)的數(shù)據(jù)源,當(dāng)拿不到數(shù)據(jù)源時(shí),使用此配置//dataSource.setDefaultTargetDataSource(properties.getUzaiTravel());return dataSource;}@Beanpublic PlatformTransactionManager txManager() {return new DataSourceTransactionManager(dataSource());}/***獲取數(shù)據(jù)源集合*/private Map<Object, Object> getdataMap(String fiePath){try {Map<Object, Object> targetDataSources = new HashMap<>();File xmlFile = new File(fiePath);DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = builderFactory.newDocumentBuilder();Document doc = builder.parse(xmlFile);doc.getDocumentElement().normalize();System.out.println("Root element: " + doc.getDocumentElement().getNodeName());NodeList nList = doc.getElementsByTagName("db");for(int i = 0 ; i<nList.getLength();i++) {Node node = nList.item(i);Element ele = (Element)node;/*HikariConfig config = new HikariConfig();config.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());config.setJdbcUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());config.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());config.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());//config.addDataSourceProperty("password", ele.getElementsByTagName("password").item(0).getTextContent());HikariDataSource dataSource = new HikariDataSource(config);*/DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(ele.getElementsByTagName("driver-class").item(0).getTextContent());dataSource.setUsername(ele.getElementsByTagName("username").item(0).getTextContent());dataSource.setPassword(ele.getElementsByTagName("password").item(0).getTextContent());dataSource.setUrl(ele.getElementsByTagName("jdbc-url").item(0).getTextContent());dataSource.setInitialSize(5);dataSource.setMinIdle(1);dataSource.setMaxActive(10);// 啟用監(jiān)控統(tǒng)計(jì)功能dataSource.setFilters("stat");//設(shè)置是否顯示sql語(yǔ)句targetDataSources.put(ele.getElementsByTagName("databasename").item(0).getTextContent(), dataSource);}return targetDataSources;}catch (Exception ex){return null;}}//訪問(wèn)的ip@Value("${druid.IP}")private String IP;//登錄名@Value("${druid.druidLgoinName}")private String druidLgoinName;//密碼@Value("${druid.druidLgoinPassword}")private String druidLgoinPassword;@Beanpublic ServletRegistrationBean DruidStatViewServle() {//org.springframework.boot.context.embedded.ServletRegistrationBean提供類的進(jìn)行注冊(cè).ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");//添加初始化參數(shù):initParams//白名單:servletRegistrationBean.addInitParameter("allow",IP);//IP黑名單 (存在共同時(shí),deny優(yōu)先于allow) : 如果滿足deny的話提示:Sorry, you are not permitted to view this page.// servletRegistrationBean.addInitParameter("deny", "192.168.1.73");//登錄查看信息的賬號(hào)密碼.servletRegistrationBean.addInitParameter("loginUsername",druidLgoinName);servletRegistrationBean.addInitParameter("loginPassword",druidLgoinPassword);//是否能夠重置數(shù)據(jù).servletRegistrationBean.addInitParameter("resetEnable","false");return servletRegistrationBean;}/*** 注冊(cè)一個(gè):filterRegistrationBean* @return*/@Beanpublic FilterRegistrationBean druidStatFilter2(){FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());//添加過(guò)濾規(guī)則.filterRegistrationBean.addUrlPatterns("/*");//添加不需要忽略的格式信息.filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");return filterRegistrationBean;}}3.動(dòng)態(tài)數(shù)據(jù)源,從之前已加載的數(shù)據(jù)源中選取,DynamicDataSource和DynamicDataSourceHolder配合使用
public class DynamicDataSource extends AbstractRoutingDataSource{//數(shù)據(jù)源路由,此方用于產(chǎn)生要選取的數(shù)據(jù)源邏輯名稱@Overrideprotected Object determineCurrentLookupKey() {//從共享線程中獲取數(shù)據(jù)源名稱return DynamicDataSourceHolder.getDataSource();} } public class DynamicDataSourceHolder {/*** 本地線程共享對(duì)象*/private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();public static void putDataSource(String name) {THREAD_LOCAL.set(name);}public static String getDataSource() {return THREAD_LOCAL.get();}public static void removeDataSource() {THREAD_LOCAL.remove();} }5.就是使用aop,在dao層切換數(shù)據(jù)源
@Component @Aspect public class DataSourceAspect {//切換放在mapper接口的方法上,所以這里要配置AOP切面的切入點(diǎn)@Pointcut("execution( * com.example.demo.dao.*.*(..))")public void dataSourcePointCut() {}@Before("dataSourcePointCut()")public void before(JoinPoint joinPoint) {Object target = joinPoint.getTarget();String method = joinPoint.getSignature().getName();Class<?>[] clazz = target.getClass().getInterfaces();Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();try {Method m = clazz[0].getMethod(method, parameterTypes);//如果方法上存在切換數(shù)據(jù)源的注解,則根據(jù)注解內(nèi)容進(jìn)行數(shù)據(jù)源切換if (m != null && m.isAnnotationPresent(TargetDataSource.class)) {TargetDataSource data = m.getAnnotation(TargetDataSource.class);String dataSourceName = data.value();DynamicDataSourceHolder.putDataSource(dataSourceName);} else {}} catch (Exception e) {}}//執(zhí)行完切面后,將線程共享中的數(shù)據(jù)源名稱清空@After("dataSourcePointCut()")public void after(JoinPoint joinPoint){DynamicDataSourceHolder.removeDataSource();} }
數(shù)據(jù)連接都配置在xml里面
?xml路徑在配置文件里面配置,這樣適用讀寫(xiě)分離和多個(gè)不同的數(shù)據(jù)源,而且多個(gè)項(xiàng)目可以共用這一個(gè)配置
?最后引用注解,需要注意的是注解的數(shù)據(jù)庫(kù)名稱和xml里面databasename節(jié)點(diǎn)是一一對(duì)應(yīng)的,可以隨便自定義,比如讀寫(xiě)是一個(gè)數(shù)據(jù)庫(kù)名字,這時(shí)候就可以定義成pringtest_r表示讀庫(kù)
?
至此多數(shù)據(jù)源就配置完成,至于阿里的druid下次再分享,代碼都貼出來(lái),如果大家感覺(jué)還有哪些不足的地方,歡迎指正。
轉(zhuǎn)載于:https://www.cnblogs.com/ok123/p/7523106.html
總結(jié)
以上是生活随笔為你收集整理的spring boot+mybatis 多数据源切换的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 41.栈
- 下一篇: ES6,Array.copyWithin