spring boot+mybatis 多數(shù)據(jù)源切換(實例講解)
由于公司業(yè)務(wù)劃分了多個數(shù)據(jù)庫,開發(fā)一個項目會同事調(diào)用多個庫,經(jīng)過學(xué)習(xí)我們采用了注解+aop的方式實現(xiàn)的
1.首先定義一個注解類
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TargetDataSource { String value();//此處接收的是數(shù)據(jù)源的名稱 }
2.然后建一個配置類,這個在項目啟動時會加載數(shù)據(jù)源,一開始采用了HikariCP,查資料說是最快性能最好的,然后又發(fā)現(xiàn)了阿里的druid,這個功能比較全面,而且性能也還可以,最主要他還有監(jiān)控功能,具體實現(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 { /*@Autowired private DBProperties properties;*/ @Value("${datasource.filePath}") private String filePath;//數(shù)據(jù)源配置 @Bean(name = "dataSource") public DataSource dataSource() { //按照目標(biāo)數(shù)據(jù)源名稱和目標(biāo)數(shù)據(jù)源對象的映射存放在Map中 Map<Object, Object> targetDataSources = new HashMap<>(); //查找xml數(shù)據(jù)連接字符串 targetDataSources=getdataMap(filePath); //動態(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的對象包裝多數(shù)據(jù)源 DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources); //設(shè)置默認(rèn)的數(shù)據(jù)源,當(dāng)拿不到數(shù)據(jù)源時,使用此配置 //dataSource.setDefaultTargetDataSource(properties.getUzaiTravel()); return dataSource; } @Bean public 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)計功能 dataSource.setFilters("stat");//設(shè)置是否顯示sql語句 targetDataSources.put(ele.getElementsByTagName("databasename").item(0).getTextContent(), dataSource); } return targetDataSources; } catch (Exception ex) { return null; } } //訪問的ip @Value("${druid.IP}") private String IP; //登錄名 @Value("${druid.druidLgoinName}") private String druidLgoinName; //密碼 @Value("${druid.druidLgoinPassword}") private String druidLgoinPassword; @Bean public ServletRegistrationBean DruidStatViewServle() { //org.springframework.boot.context.embedded.ServletRegistrationBean提供類的進(jìn)行注冊. ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); //添加初始化參數(shù):initParams //白名單: servletRegistrationBean.addInitParameter("allow",IP); //IP黑名單 (存在共同時,deny優(yōu)先于allow) : 如果滿足deny的話提示:Sorry, you are not permitted to view this page. // servletRegistrationBean.addInitParameter("deny", "192.168.1.73"); //登錄查看信息的賬號密碼. servletRegistrationBean.addInitParameter("loginUsername",druidLgoinName); servletRegistrationBean.addInitParameter("loginPassword",druidLgoinPassword); //是否能夠重置數(shù)據(jù). servletRegistrationBean.addInitParameter("resetEnable","false"); return servletRegistrationBean; } /** * 注冊一個:filterRegistrationBean * @return */ @Bean public FilterRegistrationBean druidStatFilter2(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter()); //添加過濾規(guī)則. filterRegistrationBean.addUrlPatterns("/*"); //添加不需要忽略的格式信息. filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); return filterRegistrationBean; } }
3.動態(tài)數(shù)據(jù)源,從之前已加載的數(shù)據(jù)源中選取,DynamicDataSource和DynamicDataSourceHolder配合使用
public class DynamicDataSource extends AbstractRoutingDataSource{ //數(shù)據(jù)源路由,此方用于產(chǎn)生要選取的數(shù)據(jù)源邏輯名稱 @Override protected Object determineCurrentLookupKey() { //從共享線程中獲取數(shù)據(jù)源名稱 return DynamicDataSourceHolder.getDataSource(); } } public class DynamicDataSourceHolder { /** * 本地線程共享對象 */ 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(); } }
4.就是使用aop,在dao層切換數(shù)據(jù)源
@Component @Aspect public class DataSourceAspect { //切換放在mapper接口的方法上,所以這里要配置AOP切面的切入點 @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路徑在配置文件里面配置,這樣適用讀寫分離和多個不同的數(shù)據(jù)源,而且多個項目可以共用這一個配置
最后引用注解,需要注意的是注解的數(shù)據(jù)庫名稱和xml里面databasename節(jié)點是一一對應(yīng)的,可以隨便自定義,比如讀寫是一個數(shù)據(jù)庫名字,這時候就可以定義成pringtest_r表示讀庫
至此多數(shù)據(jù)源就配置完成,至于阿里的druid下次再分享,代碼都貼出來,如果大家感覺還有哪些不足的地方,歡迎指正。
以上這篇spring boot+mybatis 多數(shù)據(jù)源切換(實例講解)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
- Spring+Mybatis動態(tài)切換數(shù)據(jù)源的方法
- Spring與Mybatis相結(jié)合實現(xiàn)多數(shù)據(jù)源切換功能
- Spring + Mybatis 項目實現(xiàn)動態(tài)切換數(shù)據(jù)源實例詳解
- 關(guān)于Spring3 + Mybatis3整合時多數(shù)據(jù)源動態(tài)切換的問題
- SpringMVC Mybatis配置多個數(shù)據(jù)源并切換代碼詳解
- spring boot + mybatis實現(xiàn)動態(tài)切換數(shù)據(jù)源實例代碼
- SpringBoot Mybatis動態(tài)數(shù)據(jù)源切換方案實現(xiàn)過程
- Mybatis多數(shù)據(jù)源切換實現(xiàn)代碼
- Spring AOP如何實現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換詳解
- mybatis多數(shù)據(jù)源動態(tài)切換的完整步驟
相關(guān)文章
IntelliJ IDEA Tomcat控制臺中文亂碼問題的四種解決方案
這篇文章主要給大家分享了4種方法完美解決IntelliJ IDEA Tomcat控制臺中文亂碼問題,文中有詳細(xì)的圖文介紹,對我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-08-08使用Spring Boot的LoggersEndpoint管理日志級別
這篇文章主要為大家介紹了使用Spring Boot的LoggersEndpoint管理日志級別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11重寫hashCode()和equals()方法詳細(xì)介紹
這篇文章主要介紹了重寫hashCode()和equals()方法詳細(xì)介紹,涉及重寫equals()方法,重寫hashCode()方法,重寫equals()而不重寫hashCode()的風(fēng)險等相關(guān)內(nèi)容的介紹,具有一定借鑒價值,需要的朋友可以參考下2018-01-01