springboot新增多資料來源連線池並配置Mybatis

funnyZpC發表於2018-06-16

springboot新增多資料來源連線池並配置Mybatis

轉載請註明出處:https://www.cnblogs.com/funnyzpc/p/9190226.html

    May 12, 2018  星期六,那是個晴天,天湛藍湛藍的非常乾淨,彷彿飄過一粒塵埃也能看得清清楚楚,然後就發生了些事情。。。很傷心很難過,至今也沒能抹去,欸,我是怎樣一步步把自己變成這個樣子呢。

    難過的事情總會在縈繞很久,罷了,這裡還是不回憶了,就這樣吧。

    首先我說說這次配置多資料來源的原因吧:原因大致有二:

一是我們的線上的有兩大業務系統雲像系統和線上交易系統,這兩個系統的分別使用各自的mysql例項,交合業務的情況下目前通過定時指令碼做資料更新和同步,遂在開發新的業務模組的時候就想到了將springboot配置兩個資料來源(mysql和mysql)

二是最近在改造資料庫日誌表的時候發現mysql的優化捉襟見肘,遂就想到了換資料庫,換個我個人研究了許久的PostgreSQL,老大一開始不怎麼樂意這麼幹,但是看我又研究了這麼久,效能也確實較mysql高許多,再加上公司技術團隊並不是很大的情況下(主要是業務量上去了資料庫效能跟不上,也沒有獨立的DBA來維護和調優Mysql),就給了我一週的時間研究資料庫(下次具體聊),故就涉及到兩個資料來源(mysql和PostgreSQL)的問題。

    嗯,對於以上兩個問題,我嘗試了差異化的解決方式,對於mysl和mysql資料來源我選擇的是 阿里Druid+TK.Mybatis的解決方式,對於mysql和PG資料來源我選擇的是Hikari+TK.Mybatis的解決方式.這兩種方式在實際配置中是有些許差異的,這裡我略去不講,只講第二種,首先羅列下這其中碰到的問題:

      A>DataSource和SessionFactory引用指定注入問題。

      B>Hikari資料來源配置引數名稱差異問題。

      C>Springboot init時配置類先後順序的問題

      D>多資料來源下Mybatis Mapper類重複問題

    我先講講我大致的配置過程吧,首先新建立兩個配置類,以隔離開(剛在一個包 中不隔離開也可以):

然後在兩個包中分別新建兩個配置類,一個是MyBatis配置類和資料來源、session工廠配置類,我這裡是這樣子:

這時候,我先展示下資料來源配置類:

 1 package **.task.config.mysql;
 2 
 3 
 4 import com.github.pagehelper.PageHelper;
 5 import com.zaxxer.hikari.HikariConfig;
 6 import com.zaxxer.hikari.HikariDataSource;
 7 import org.apache.commons.lang3.StringUtils;
 8 import org.apache.ibatis.plugin.Interceptor;
 9 import org.apache.ibatis.session.SqlSessionFactory;
10 import org.mybatis.spring.SqlSessionFactoryBean;
11 import org.mybatis.spring.SqlSessionTemplate;
12 import org.springframework.beans.factory.annotation.Qualifier;
13 import org.springframework.beans.factory.annotation.Value;
14 import org.springframework.boot.context.properties.ConfigurationProperties;
15 import org.springframework.context.annotation.Bean;
16 import org.springframework.context.annotation.Configuration;
17 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
18 import org.springframework.core.io.support.ResourcePatternResolver;
19 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
20 import org.springframework.transaction.annotation.EnableTransactionManagement;
21 
22 import javax.sql.DataSource;
23 import java.sql.SQLException;
24 import java.util.Properties;
25 
26 /**
27  * mybatis 配置資料來源類
28  *
29  * @author
30  * @date 2016年12月15日
31  * @since 1.7
32  */
33 @Configuration("mysqlCfg")
34 @EnableTransactionManagement
35 @ConfigurationProperties(prefix = "spring.db_mysql") //引用配置檔案引數字首
36 public class MybatisConfiguration extends HikariConfig {
37     @Value("${mybatis.mysql.xmlLocation}")
38     private String xmlLocation;
39 
40     private String typeAliasesPackage;
41   //配置資料來源
42     @Bean("mysqlDataSource")
43     public DataSource dataSource(){
44         /*
45         HikariDataSource ds=HikariDataSource();
46         ds.setJdbcUrl();
47         ds.setConnectionTimeout();
48         */
49         return new HikariDataSource(this);
50     }
51   //配置Session工廠
52     @Bean(name = "mysqlSqlSessionFactory")
53     public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("mysqlDataSource")DataSource dataSource) {
54         SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
55         bean.setDataSource(dataSource);
56         if(StringUtils.isNotBlank(typeAliasesPackage)){
57             bean.setTypeAliasesPackage(typeAliasesPackage);
58         }
59         //分頁外掛
60         PageHelper pageHelper = new PageHelper();
61         Properties properties = new Properties();
62         properties.setProperty("reasonable", "true");
63         properties.setProperty("supportMethodsArguments", "true");
64         properties.setProperty("returnPageInfo", "check");
65         properties.setProperty("params", "count=countSql");
66         pageHelper.setProperties(properties);
67         //新增XML目錄
68         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
69         Interceptor[] plugins =  new Interceptor[]{pageHelper};
70         bean.setPlugins(plugins);
71         try {
72 
73             bean.setMapperLocations(resolver.getResources(xmlLocation));
74             return bean.getObject();
75         } catch (Exception e) {
76             e.printStackTrace();
77             throw new RuntimeException(e);
78         }
79     }
80   //mybatis會用到的SqlSession模板
81     @Bean
82     public SqlSessionTemplate sqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
83         return new SqlSessionTemplate(sqlSessionFactory);
84     }
85   //資料來源事物配置
86     @Bean
87     public DataSourceTransactionManager transactionManager(@Qualifier("mysqlDataSource") DataSource dataSource) {
88         return new DataSourceTransactionManager(dataSource);
89     }
90 
91 }

在這裡需要說明的是我通過繼承HikariConfig來配置資料來源以及Session工廠,配置DataSource和Session工廠的時候需要使用指定註解名稱,在這裡是“mysqlDataSource“和”mysqlSqlSessionFactory“,如果專案只有一個資料來源的話大可不必寫這個的,另外需要特別注意的是在配置session工廠一定要在形式引數前使用@Qualifier註解引用指定的資料來源,同時SqlSession模板和事物也需要通過@Qualifier註解指定session工廠和資料來源,這裡這樣做的原因是為了解決多資料來源配置引用不明的問題。

  OK,資料來源配置完成了,現在將配置第二個配置類Mybatis配置類,以下是具體配置內容:

 1 package **.task.config.mysql;
 2 
 3 import org.springframework.boot.autoconfigure.AutoConfigureAfter;
 4 import org.springframework.boot.bind.RelaxedPropertyResolver;
 5 import org.springframework.context.EnvironmentAware;
 6 import org.springframework.context.annotation.Bean;
 7 import org.springframework.context.annotation.Configuration;
 8 import org.springframework.core.env.Environment;
 9 import tk.mybatis.spring.mapper.MapperScannerConfigurer;
10 
11 /**
12  * mybatis mapper 掃描配置類
13  *
14  * @author
15  * @date 2018年05月15日
16  */
17 @Configuration("mysqlMapper")
18 @AutoConfigureAfter(MybatisConfiguration.class)//init時指定先後順序,這裡是資料來源在mybatis之前配置
19 public class MapperConfiguration implements EnvironmentAware {
20 
21     private RelaxedPropertyResolver propertyResolver;
22 
23     private String basePackage;
24     //這裡為mybatis配置指定session工廠和Dao類基礎包路徑
25     @Bean("mysqlMapperScannerConfigurer")
26     public MapperScannerConfigurer mapperScannerConfigurer(Environment environment){
27 
28         MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
29         mapperScannerConfigurer.setSqlSessionFactoryBeanName("mysqlSqlSessionFactory");
30         mapperScannerConfigurer.setBasePackage(basePackage);
31         return mapperScannerConfigurer;
32     }
33 
34    //設定環境變數(引用配置檔案中的)
35     @Override
36     public void setEnvironment(Environment environment) {
37         this.propertyResolver = new RelaxedPropertyResolver(environment, null);
38         this.basePackage = propertyResolver.getProperty("mybatis.mysql.basepackage");
39     }
40 }

配置Mybaits的時候需要將資料來源配置置於之後配置,這裡通過註解@AutoConfigureAfter來指定資料來源配置類,在配置Mybatis引用的Session工廠時也要指定為資料來源配置類中的sqlSession工廠,同時也需要指定生成的Mapper的包名,這個包的路徑這裡我寫在application.yml的配置檔案中。

  配置類已經寫完,現在最後一步了,在配置檔案中指定配置類所引用的配置引數,大致是這樣子:

spring:
    application:
        name: **-task
    jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
        default-property-inclusion: non_null
    db_mysql:
        name: DEV_MYSQL

        #LOCAL
        jdbc-url: jdbc:mysql://${MYSQL_HOST:192.168.10.141}:${MYSQL_PORT:3306}/p2p?useUnicode=true&characterEncoding=UTF8
        driver-class-name: com.mysql.jdbc.Driver
        username: p2p
        password: p2p

        # Hikari connection pool
        type: com.zaxxer.hikari.HikariDataSource
        minimum-idle: 5
        maximum-pool-size:  15
        auto-commit:  true
        idle-timeout: 30000
        pool-name:  DatebookHikariCP
        max-lifetime: 1800000
        connection-timeout: 30000
        connection-test-query:  SELECT 1
        validation-timeout: 10000
    db_pg:
        name: DEV_PG

        # JDBC config
        jdbc-url:  jdbc:postgresql://192.168.10.141:5432/log
        driver-class-name:  org.postgresql.Driver
        username: log
        password: log

        # Hikari connection pool
        type: com.zaxxer.hikari.HikariDataSource
        minimum-idle: 5
        maximum-pool-size:  15
        auto-commit:  true
        idle-timeout: 30000
        pool-name:  DatebookHikariCP
        max-lifetime: 1800000
        connection-timeout: 30000
        connection-test-query:  SELECT 1
        validation-timeout: 10000

以上配置檔案中主要展示了資料來源的一些配置(注意db_mysqldb_pg這兩項),這裡需要特別注意的是在Hikari資料來源的配置引數中沒有url和driverClass,只有jdbc-urldriver-class-name這兩個,其它的配置配置引數名稱與c3p0和Druid的無異,具體的連線池大小需要根據實際的專案和資料庫伺服器的硬體引數來配置,這裡我只給出常見配置。

  哦,對了,還需要在配置檔案中追加Mybatis的配置引數,具體是這樣:

1 mybatis:
2     mysql:
3         basepackage: **.task.mapper.mysql
4         xmlLocation: classpath:mapper/mysql/*.xml
5     pg:
6         basepackage: **.task.mapper.pg
7         xmlLocation: classpath:mapper/pg/*.xml

  Mybatis的配置完成了,對於PostgreSQL的配置只需要注意響應的配置別名即可(比如資料來源、session工廠、SqlSession工廠等等)

  本節所講的配置貌似已經完成,但是這裡我順帶講一下我在效能測試的時候所遇見的兩個TK.mybatis這個外掛的問題(原生mybatis也可能存在):

    A>對於兩個庫中存在同名的Mapper名字,在@Autowired使用時會產生衝突

    B>持久化需要返回主鍵時對於mysql和PG兩中資料庫的處理方式存在差異

  對於以上第一個問題(多資料來源Mapper衝突),我給出的解決方式是在生成的Mapper類中指定衝突的那個Mapper的Service別名,這樣:

1 package **.task.mapper.pg;
2 
3 import com.github.carvechris.security.task.entity.pg.TestEmp;
4 import org.springframework.stereotype.Service;
5 import tk.mybatis.mapper.common.Mapper;
6 
7 @Service("pgTestEmpMapper")//這裡指定別名
8 public interface TestEmpMapper extends Mapper<TestEmp> {
9 }

這個Dao在使用的時候需要使用@Resource註解來指定的Dao類:

1     @Autowired
2     @Resource(name = "pgTestEmpMapper")
3     private **.task.mapper.pg.TestEmpMapper pgEmpMapper;

  

  對於以上第二個問題(持久化返回主鍵問題),mysql和pg的處理方式不同,具體為:

對於mysql需要在實體類中指定主鍵的生成方式,即可在呼叫insert方法時返回生成的主鍵:

    /**
     * 表ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)//設定為主鍵自增以回寫主鍵
    private Integer id;

以上的主鍵策略可以是主鍵表,也可以是UUID的方式,根據專案實際需求而定。

對於PG:首先需要在實體類的主鍵中這樣宣告

    /**
     * 表ID
     */
    @Id
    @Column(insertable=false)//指定主鍵為資料庫生成(同時需要在DB中將ID宣告為serial型別)
    private Long id;

在Dao(生成的Mapper類中)宣告一個獨立的查入方法

 1 package **.task.mapper.pg;
 2 
 3 import **.task.entity.pg.ZwPlBalancequery;
 4 import org.apache.ibatis.annotations.InsertProvider;
 5 import org.a2018-06-16pache.ibatis.annotations.Options;
 6 import org.apache.ibatis.annotations.Select;
 7 import tk.mybatis.mapper.common.Mapper;
 8 import tk.mybatis.mapper.provider.base.BaseInsertProvider;
 9 
10 public interface ZwPlBalancequeryMapper extends Mapper<ZwPlBalancequery> {
11     //需要需要獨立宣告插入方法以返回插入記錄的ID      
12     @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
13     @InsertProvider(type = BaseInsertProvider.class, method = "dynamicSQL")
14     int insertWBack(ZwPlBalancequery record);
15 }

以上插入方法(insertWBack)中的註解是將id的生成方式改為資料庫生成,至此,完美解決持久化返回記錄ID問題。

現在是  2018-06-16 17:40:42 ,後天就是端午節了,預祝各位節日快樂!

相關文章