spring boot:使用mybatis訪問多個mysql數據源/查看Hikari連接池的統計信息(spring boot 2.3.1)


一,為什么要訪問多個mysql數據源?

實際的生產環境中,我們的數據並不會總放在一個數據庫,

例如:業務數據庫:存放了用戶/商品/訂單

        統計數據庫:按年、月、日的針對用戶、商品、訂單的統計表

       因為統計庫中的數據是對業務庫中數據的提取和挖掘,

       但與業務的運行沒有直接關系,所以我們會分開存放,

      把它們放到兩個庫中。

但有時我們會有訪問兩個庫中數據的需求,這時就需要訪問兩個或以上數據源

 

說明:劉宏締的架構森林是一個專注架構的博客,地址:https://www.cnblogs.com/architectforest

         對應的源碼可以訪問這里獲取: https://github.com/liuhongdi/

說明:作者:劉宏締 郵箱: 371125307@qq.com

 

二,演示項目的相關信息

1,項目地址:

https://github.com/liuhongdi/multimysql

 

2,項目原理

我們需要訪問兩個數據庫,一個名字:store,

一個名字:tiku

然后在一個方法中獲取到兩個庫中指定數據表的數據

 

3,項目結構,如圖:

三,配置文件說明

1,application.properties

#mysql
spring.datasource.store.url=jdbc:mysql://127.0.0.1:3306/store?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.store.username=root
spring.datasource.store.password=lhddemo
spring.datasource.store.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.store.maximum-pool-size=12
spring.datasource.store.minimum-idle=10
spring.datasource.store.idle-timeout=500000
spring.datasource.store.max-lifetime=540000

spring.datasource.tiku.url=jdbc:mysql://localhost:3306/tiku?useUnicode=true&characterEncoding=utf-8&useSSL=FALSE&serverTimezone=Asia/Shanghai
spring.datasource.tiku.username=root
spring.datasource.tiku.password=lhddemo
spring.datasource.tiku.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.tiku.maximum-pool-size=12
spring.datasource.tiku.minimum-idle=10
spring.datasource.tiku.idle-timeout=500000
spring.datasource.tiku.max-lifetime=540000

說明:我們需要訪問兩個數據庫:store,tiku

注意max-lifetime的設置:這個值是連接池中一個連接的生命時間長度,

設置應該低於mysql配置文件中的wait-timeout值的設置,

idle-timeout是指空閑的連接的超時時間,應該不大於max-lifetime的值

maximum-pool-size是池中連接的數據,默認值是10,我們設置為12,保留默認值也沒問題

 

2,兩個數據表的結構:

store.goods

CREATE TABLE `goods` (
 `goodsId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `goodsName` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'name',
 `subject` varchar(200) NOT NULL DEFAULT '' COMMENT '標題',
 `price` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '價格',
 `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock',
 PRIMARY KEY (`goodsId`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'

tiku.category

CREATE TABLE `category` (
 `category_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `category_name` varchar(100) NOT NULL DEFAULT '' COMMENT '分類名稱',
 PRIMARY KEY (`category_id`),
 UNIQUE KEY `category_name` (`category_name`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='分類'

 

四,java代碼說明

1,StoreDataSourceConfig.java

@Configuration
//得到mapper
@MapperScan(basePackages = StoreDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "storeSqlSessionFactory")
public class StoreDataSourceConfig {
    // 指定mapper 目錄,與其他數據源隔離
    static final String PACKAGE = "com.multimysql.demo.mapper.store";
    static final String MAPPER_LOCATION = "classpath:mapper/store/*.xml";

    @Value("${spring.datasource.store.url}")
    private String url;

    @Value("${spring.datasource.store.username}")
    private String username;

    @Value("${spring.datasource.store.password}")
    private String password;

    @Value("${spring.datasource.store.driver-class-name}")
    private String driverClass;

    @Value("${spring.datasource.store.maximum-pool-size}")
    private int maximumPoolSize;

    @Value("${spring.datasource.store.minimum-idle}")
    private int minimumIdle;

    @Value("${spring.datasource.store.idle-timeout}")
    private long idleTimeout;

    @Value("${spring.datasource.store.max-lifetime}")
    private long maxLifetime;

    //得到datasource
    @Bean(name = "storeDataSource")
    @Primary
    public DataSource storeDataSource()  {
        HikariDataSource dataSource=new HikariDataSource();
        dataSource.setJdbcUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driverClass);
        dataSource.setMaximumPoolSize(maximumPoolSize);
        dataSource.setMinimumIdle(minimumIdle);
        dataSource.setIdleTimeout(idleTimeout);
        dataSource.setMaxLifetime(maxLifetime);
        return dataSource;
    }

    //得到TransactionManager
    @Bean(name = "storeTransactionManager")
    @Primary
    public DataSourceTransactionManager storeTransactionManager() {
        return new DataSourceTransactionManager(storeDataSource());
    }

    //得到SqlSessionFactory
    @Bean(name = "storeSqlSessionFactory")
    @Primary
    public SqlSessionFactory storeSqlSessionFactory(@Qualifier("storeDataSource") DataSource storeDataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(storeDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(StoreDataSourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}

生成到store數據庫的數據源,

注意指定了mapper文件的路徑,

兩個不同數據源的mapper分別放到了不同的目錄下,避免有沖突

 

2,TikuDataSourceConfig.java,

   為節省篇幅,不再貼出代碼,大家可以在github上自取

 

3,HomeController.java

@Controller
@RequestMapping("/home")
public class HomeController {

    @Resource
    private GoodsMapper goodsMapper;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private HikariDataSource storeDataSource;

    @Resource
    private HikariDataSource tikuDataSource;

    //商品詳情 參數:商品id
    @GetMapping("/goodsone")
    @ResponseBody
    public String goodsOne(@RequestParam(value="goodsid",required = true,defaultValue = "0") Long goodsId) throws SQLException {
        System.out.println("------goodsInfo begin");
        Goods goods = goodsMapper.selectOneGoods(goodsId);
        Category category=categoryMapper.selectOneCategory("4");
        return "goods:"+goods.toString()+";category:"+category.toString();
    }

    //顯示統計信息
    @GetMapping("/stats")
    @ResponseBody
    public Object stats() throws SQLException {

        Connection connection = storeDataSource.getConnection();
        connection.close();
        HikariPoolMXBean storePool = storeDataSource.getHikariPoolMXBean();
        int active = storePool.getActiveConnections();
        int total = storePool.getTotalConnections();
        int idle = storePool.getIdleConnections();
        int theadsAwaitting = storePool.getThreadsAwaitingConnection();
        int maximumPoolsize = storeDataSource.getMaximumPoolSize();
        int minimumIdle = storeDataSource.getMinimumIdle();
        String poolName = storeDataSource.getPoolName();
        long connTimeout = storeDataSource.getConnectionTimeout();
        long idleTimeout = storeDataSource.getIdleTimeout();
        long maxLifetime = storeDataSource.getMaxLifetime();

        String status = "store pool:<br/>";
        status += "poolName:" + poolName+"<br/>";
        status += "active:" + active+"<br/>";
        status += "total:" + total+"<br/>";
        status += "idle:" + idle+"<br/>";
        status += "theadsAwaitting:" + theadsAwaitting+"<br/>";
        status += "maximumPoolsize:" + maximumPoolsize+"<br/>";
        status += "minimumIdle:" + minimumIdle+"<br/>";
        status += "connTimeout:" + connTimeout+"<br/>";
        status += "idleTimeout:" + idleTimeout+"<br/>";
        status += "maxLifetime:" + maxLifetime+"<br/>";

        Connection tikuConnection = tikuDataSource.getConnection();
        tikuConnection.close();
        HikariPoolMXBean tikuPool = tikuDataSource.getHikariPoolMXBean();
        int active2 = tikuPool.getActiveConnections();
        int total2 = tikuPool.getTotalConnections();
        int idle2 = tikuPool.getIdleConnections();
        int theadsAwaitting2 = tikuPool.getThreadsAwaitingConnection();
        int maximumPoolsize2 = tikuDataSource.getMaximumPoolSize();
        int minimumIdle2 = tikuDataSource.getMinimumIdle();
        String poolName2 = tikuDataSource.getPoolName();
        long connTimeout2 = tikuDataSource.getConnectionTimeout();
        long idleTimeout2 = tikuDataSource.getIdleTimeout();
        long maxLifetime2 = tikuDataSource.getMaxLifetime();

        status += "tiku pool:<br/>";
        status += "poolName:" + poolName2+"<br/>";
        status += "active:" + active2+"<br/>";
        status += "total:" + total2+"<br/>";
        status += "idle:" + idle2+"<br/>";
        status += "theadsAwaitting:" + theadsAwaitting2+"<br/>";
        status += "maximumPoolsize:" + maximumPoolsize2+"<br/>";
        status += "minimumIdle:" + minimumIdle2+"<br/>";
        status += "connTimeout:" + connTimeout2+"<br/>";
        status += "idleTimeout:" + idleTimeout2+"<br/>";
        status += "maxLifetime:" + maxLifetime2+"<br/>";

         return status;
    }

說明:goodsOne方法:返回store.goods表中的商品信息和tiku.category表中的分類信息

         stats方法:打印兩個Hikari連接池的信息

 

4,其他的 類文件和mapper文件,請訪問github獲取

 

五,效果測試

1,訪問商品信息地址:

http://127.0.0.1:8080/home/goodsone?goodsid=3

返回:

goods: Goods:goodsId=3 goodsName=100分電動牙刷 subject=好用到讓你愛上刷牙 price=59.00 stock=15
category: Goods:category_id=4 category_name=bash

 

2,訪問統計地址:

http://127.0.0.1:8080/home/stats

返回:

store pool:
poolName:HikariPool-1
active:0
total:10
idle:10
theadsAwaitting:0
maximumPoolsize:12
minimumIdle:10
connTimeout:30000
idleTimeout:500000
maxLifetime:540000
tiku pool: poolName:HikariPool-2 active:0 total:10 idle:10 theadsAwaitting:0 maximumPoolsize:12 minimumIdle:10 connTimeout:30000 idleTimeout:500000 maxLifetime:540000

 

六,查看spring boot的版本

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.1.RELEASE)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM