springboot學習筆記:9.springboot+mybatis+通用mapper+多數據源

来源:https://www.cnblogs.com/zhaojiatao/archive/2018/01/31/8393398.html
-Advertisement-
Play Games

springboot+mybatis+通用mapper+多數據源 ...


本文承接上一篇文章:springboot學習筆記:8. springboot+druid+mysql+mybatis+通用mapper+pagehelper+mybatis-generator+freemarker+layui

請確保根據上一篇文章的源代碼完成所有測試之後,再跟隨本文,繼續配置多數據源;

1.資料庫準備

資料庫表我們在springboot-mybatis數據之外,新建資料庫springboot-mybatis2;

springboot-mybatis資料庫中有t_class表;

 

springboot-mybatis2資料庫中有t_teacher表;

2.配置文件增加新數據源連接配置信息:

spring.datasource2.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource2.driver-class-name=com.mysql.jdbc.Driver
spring.datasource2.url=jdbc:mysql://localhost:3306/springboot-mybatis2?useUnicode=true&characterEncoding=utf-8
spring.datasource2.username=root
spring.datasource2.password=root

 

3.註意第二個數據源DataSource2對應的數據表實體及mapper和service的包結構:

兩套數據源對應的mapper、service包與第一個數據源是平行的

 4.DruidConfig.java中增加新數據源:dataSource2(加粗部分)

package com.zjt.config;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.context.annotation.Primary;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * Druid配置
 *
 * @author zhaojiatao
 */
@Configuration
public class DruidConfig {
    private Logger logger = LoggerFactory.getLogger(DruidConfig.class);

    @Value("${spring.datasource.url:#{null}}")
    private String dbUrl;
    @Value("${spring.datasource.username: #{null}}")
    private String username;
    @Value("${spring.datasource.password:#{null}}")
    private String password;
    @Value("${spring.datasource.driverClassName:#{null}}")
    private String driverClassName;

    @Value("${spring.datasource2.url:#{null}}")
    private String dbUrl2;
    @Value("${spring.datasource2.username: #{null}}")
    private String username2;
    @Value("${spring.datasource2.password:#{null}}")
    private String password2;
    @Value("${spring.datasource2.driverClassName:#{null}}")
    private String driverClassName2;

    @Value("${spring.datasource.initialSize:#{null}}")
    private Integer initialSize;
    @Value("${spring.datasource.minIdle:#{null}}")
    private Integer minIdle;
    @Value("${spring.datasource.maxActive:#{null}}")
    private Integer maxActive;
    @Value("${spring.datasource.maxWait:#{null}}")
    private Integer maxWait;
    @Value("${spring.datasource.timeBetweenEvictionRunsMillis:#{null}}")
    private Integer timeBetweenEvictionRunsMillis;
    @Value("${spring.datasource.minEvictableIdleTimeMillis:#{null}}")
    private Integer minEvictableIdleTimeMillis;
    @Value("${spring.datasource.validationQuery:#{null}}")
    private String validationQuery;
    @Value("${spring.datasource.testWhileIdle:#{null}}")
    private Boolean testWhileIdle;
    @Value("${spring.datasource.testOnBorrow:#{null}}")
    private Boolean testOnBorrow;
    @Value("${spring.datasource.testOnReturn:#{null}}")
    private Boolean testOnReturn;
    @Value("${spring.datasource.poolPreparedStatements:#{null}}")
    private Boolean poolPreparedStatements;
    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize:#{null}}")
    private Integer maxPoolPreparedStatementPerConnectionSize;
    @Value("${spring.datasource.filters:#{null}}")
    private String filters;
    @Value("{spring.datasource.connectionProperties:#{null}}")
    private String connectionProperties;

    @Bean
    @Primary
    public DataSource dataSource(){
        DruidDataSource datasource = new DruidDataSource();

        datasource.setUrl(this.dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        //configuration
        if(initialSize != null) {
            datasource.setInitialSize(initialSize);
        }
        if(minIdle != null) {
            datasource.setMinIdle(minIdle);
        }
        if(maxActive != null) {
            datasource.setMaxActive(maxActive);
        }
        if(maxWait != null) {
            datasource.setMaxWait(maxWait);
        }
        if(timeBetweenEvictionRunsMillis != null) {
            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        }
        if(minEvictableIdleTimeMillis != null) {
            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        }
        if(validationQuery!=null) {
            datasource.setValidationQuery(validationQuery);
        }
        if(testWhileIdle != null) {
            datasource.setTestWhileIdle(testWhileIdle);
        }
        if(testOnBorrow != null) {
            datasource.setTestOnBorrow(testOnBorrow);
        }
        if(testOnReturn != null) {
            datasource.setTestOnReturn(testOnReturn);
        }
        if(poolPreparedStatements != null) {
            datasource.setPoolPreparedStatements(poolPreparedStatements);
        }
        if(maxPoolPreparedStatementPerConnectionSize != null) {
            datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        }

        if(connectionProperties != null) {
            datasource.setConnectionProperties(connectionProperties);
        }

        List<Filter> filters = new ArrayList<>();
        filters.add(statFilter());
        filters.add(wallFilter());
        datasource.setProxyFilters(filters);

        return datasource;
    }


    @Bean
    public DataSource dataSource2(){
        DruidDataSource datasource = new DruidDataSource();

        datasource.setUrl(this.dbUrl2);
        datasource.setUsername(username2);
        datasource.setPassword(password2);
        datasource.setDriverClassName(driverClassName2);
        //configuration
        if(initialSize != null) {
            datasource.setInitialSize(initialSize);
        }
        if(minIdle != null) {
            datasource.setMinIdle(minIdle);
        }
        if(maxActive != null) {
            datasource.setMaxActive(maxActive);
        }
        if(maxWait != null) {
            datasource.setMaxWait(maxWait);
        }
        if(timeBetweenEvictionRunsMillis != null) {
            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        }
        if(minEvictableIdleTimeMillis != null) {
            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        }
        if(validationQuery!=null) {
            datasource.setValidationQuery(validationQuery);
        }
        if(testWhileIdle != null) {
            datasource.setTestWhileIdle(testWhileIdle);
        }
        if(testOnBorrow != null) {
            datasource.setTestOnBorrow(testOnBorrow);
        }
        if(testOnReturn != null) {
            datasource.setTestOnReturn(testOnReturn);
        }
        if(poolPreparedStatements != null) {
            datasource.setPoolPreparedStatements(poolPreparedStatements);
        }
        if(maxPoolPreparedStatementPerConnectionSize != null) {
            datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        }

        if(connectionProperties != null) {
            datasource.setConnectionProperties(connectionProperties);
        }

        List<Filter> filters = new ArrayList<>();
        filters.add(statFilter());
        filters.add(wallFilter());
        datasource.setProxyFilters(filters);

        return datasource;
    }



    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        //控制台管理用戶,加入下麵2行 進入druid後臺就需要登錄
        //servletRegistrationBean.addInitParameter("loginUsername", "admin");
        //servletRegistrationBean.addInitParameter("loginPassword", "admin");
        return servletRegistrationBean;
    }

    @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 StatFilter statFilter(){
        StatFilter statFilter = new StatFilter();
        statFilter.setLogSlowSql(true); //slowSqlMillis用來配置SQL慢的標準,執行時間超過slowSqlMillis的就是慢。
        statFilter.setMergeSql(true); //SQL合併配置
        statFilter.setSlowSqlMillis(1000);//slowSqlMillis的預設值為3000,也就是3秒。
        return statFilter;
    }

    @Bean
    public WallFilter wallFilter(){
        WallFilter wallFilter = new WallFilter();
        //允許執行多條SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);
        wallFilter.setConfig(config);
        return wallFilter;
    }
}

 

5.增加MybatisDatasource2Config.java配置dataSource2對應的mapper掃描包路徑、sqlSessionFactory2、以及事務管理器transactionManager2

 

package com.zjt.config;

import com.zjt.util.MyMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @author <a href="zhaojiatao"></a>
 * @version 1.0, 2017/11/24
 * @description
 */
@Configuration
// 精確到 mapper 目錄,以便跟其他數據源隔離
@MapperScan(basePackages = "com.zjt.mapper2", markerInterface = MyMapper.class, sqlSessionFactoryRef = "sqlSessionFactory2")
public class MybatisDatasource2Config {

    @Autowired
    @Qualifier("dataSource2")
    private DataSource ds;

    @Bean
    public SqlSessionFactory sqlSessionFactory2() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(ds);
        //指定mapper xml目錄
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath:mapper2/*.xml"));
        return factoryBean.getObject();

    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2()); // 使用上面配置的Factory
        return template;
    }

    //關於事務管理器,不管是JPA還是JDBC等都實現自介面 PlatformTransactionManager
    // 如果你添加的是 spring-boot-starter-jdbc 依賴,框架會預設註入 DataSourceTransactionManager 實例。
    //在Spring容器中,我們手工註解@Bean 將被優先載入,框架不會重新實例化其他的 PlatformTransactionManager 實現類。
    @Bean(name = "transactionManager2")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        //MyBatis自動參與到spring事務管理中,無需額外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的數據源
        // 與DataSourceTransactionManager引用的數據源一致即可,否則事務管理會不起作用。
        return new DataSourceTransactionManager(ds);
    }

}

 

6.增加TxAdvice2Interceptor.java配置datasource2數據源對應的事務配置

 

package com.zjt.interceptor;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * @author <a href="zhaojiatao"></a>
 * @version 1.0, 2017/11/29
 * @description
 * 註解聲明式事務
 */
@Aspect
@Configuration
public class TxAdvice2Interceptor {
    private static final int TX_METHOD_TIMEOUT = 50000;//單位秒
    private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.zjt.service2.*.*(..))";

    @Autowired
    @Qualifier("transactionManager2")
    private PlatformTransactionManager transactionManager;

    @Bean
    public TransactionInterceptor txAdvice2() {
        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
         /*只讀事務,不做更新操作*/
        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
        /*當前存在事務就使用當前事務,當前不存在事務就創建一個新的事務*/
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
        requiredTx.setRollbackRules(
                Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        requiredTx.setTimeout(TX_METHOD_TIMEOUT);
        Map<String, TransactionAttribute> txMap = new HashMap<>();
        txMap.put("add*", requiredTx);
        txMap.put("save*", requiredTx);
        txMap.put("insert*", requiredTx);
        txMap.put("update*", requiredTx);
        txMap.put("delete*", requiredTx);
        txMap.put("get*", readOnlyTx);
        txMap.put("select*", readOnlyTx);
        txMap.put("query*", readOnlyTx);
        source.setNameMap( txMap );
    TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
        return txAdvice;
}

    @Bean
    public Advisor txAdviceAdvisor2() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, txAdvice2());
    }
}

 

7.編寫TeacherController實現教師業務控制器

package com.zjt.web;

import com.zjt.entity.Teacher;
import com.zjt.model.PageRusult;
import com.zjt.model.QueryTeacherList;
import com.zjt.service2.TeacherService;
import com.zjt.util.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import tk.mybatis.mapper.util.StringUtil;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/teacher")
public class TeacherController {

    @Autowired
    @Qualifier("teacherServiceImpl")
    private TeacherService teacherService;


    @ResponseBody
    @RequestMapping("/queryTeacherList")
    public PageRusult selectByPages(Page<QueryTeacherList> page){
        List<Teacher> teacherList=teacherService.queryTeacherList(page);
        PageRusult<Teacher> pageRusult =new PageRusult<Teacher>(teacherList);
        pageRusult.setCode(0);
        return pageRusult;
    }

    @ResponseBody
    @RequestMapping("/saveOrUpdateTeacher")
    public Map<String,Object> saveOrUpdateTeacher(Teacher teacher){
        LinkedHashMap<String,Object> resultMap=new LinkedHashMap<String,Object>();
        try {
            return teacherService.saveOrUpdateTeacher(teacher);
        }catch (Exception e){
            resultMap.put("state","fail");
            resultMap.put("message","操作失敗");
            return resultMap;
        }
    }


    @ResponseBody
    @RequestMapping("/deleteTeacher")
    public Map<String,Object> deleteTeacher(String id){
        LinkedHashMap<String,Object> resultMap=new LinkedHashMap<String,Object>();
        try {
            if(StringUtil.isNotEmpty(id)){
                teacherService.delete(id);
                resultMap.put("state","success");
                resultMap.put("message","刪除班級成功");
                return resultMap;
            }else{
                resultMap.put("state","fail");
                resultMap.put("message","刪除班級失敗");
                return resultMap;
            }
        }catch (Exception e){
            resultMap.put("state","fail");
            resultMap.put("message","操作異常,刪除班級失敗");
            return resultMap;
        }
    }



}

 

8.啟動項目驗證:

8.1查詢驗證

 

 8.2事務驗證:

在TeacherServiceImpl.java的saveOrUpdateTeacher方法的更新操作(updateNotNull(teacher))後認為添加1/0,拋出運行時異常,看看是否回滾;

在TClassServiceImpl.java的saveOrUpdateTClass方法的更新操作(updateNotNull(tclass))後認為添加1/0,拋出運行時異常,看看是否回滾;

 

 可以驗證出兩個數據源的事務均回滾成功,打開druid監控也可以看到兩個數據源的事務均執行了回滾:

 

 

9.項目源代碼:

https://github.com/zhaojiatao/springboot-zjt-chapter09-springboot-mybatis-datasources.git

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、Spring? Spring興起:2003年,由Rod Johnson創建。總的來說,Spring Framwork從它誕生至今都一直為人所稱道,它的偉大之處自此可見一斑。 核心:IOC&AOP IOC 全稱:Inersion of control-->控制反轉。把對象的創建工作交給框架(有意取 ...
  • 一、什麼演算法 演算法:一個計算過程,解決問題的方法 二、時間複雜度 看代碼: Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<O(n2logn)< Ο(n3)<…<Ο(2^n)<Ο(n!) 三、空間複雜度 空間複雜度:用來評估演算法記憶體占用大小的一個式子 複習:遞歸 遞歸的兩個特點 ...
  • Item 1:View C++ as a federation of languages 單詞: sublanguage:子語言 Exception:異常 template:模板 procedual:過程 object-oriented:面向對象 generic:泛型 blocks:區塊{ } st ...
  • 前言 前面redis弄了那麼多, 就是為了在項目中使用. 那這裡, 就分別來看一下, 單機版和集群版在springboot中的使用吧. 在裡面, 我會同時貼出Jedis版, 作為比較. 單機版 1. pom.xml 2. application.yml 這裡為redis設置了一個密碼, 可以在 re ...
  • 一、模塊初識 便捷目錄: sys.path 獲取指定模塊搜索路徑的字元串集合(當前是sys) sys.argv 從外部程式向內部程式傳遞參數 sys.getdefaultencoding() 獲取當前系統編碼 sys.getfilesystemencoding()獲取文件系統使用編碼方式,Windo ...
  • ajax 快速入門 ajax作用:ajax 是在不重新載入整個頁面的情況下與伺服器交換數據並更新部分網頁的技術.(實現瀏覽器與伺服器之間的數據交互,實現頁面的無刷新請求伺服器,提高用戶體驗) 基本使用: 1.創建ajax對象: new XMLHttpRequest() //普通瀏覽器使用,ie瀏覽器 ...
  • Resource is out of sync with the file system: 該錯誤為替換了image中的圖片而沒有進行更新,造成找不到該資源,進而保存,解決只要eclipse刷新一下F5就可以了 ...
  • CAS 單點登錄設計與實現22 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...