zoukankan      html  css  js  c++  java
  • springboot + mybatis配置多数据源示例

    转:http://www.jb51.net/article/107223.htm

    在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源。

    代码结构:

    简要原理:

    1)DatabaseType列出所有的数据源的key---key

    2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法

    3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用DatabaseContextHolder获取当前线程的DatabaseType

    4)MyBatisConfig中生成2个数据源DataSource的bean---value

    5)MyBatisConfig中将1)和4)组成的key-value对写入到DynamicDataSource动态数据源的targetDataSources属性(当然,同时也会设置2个数据源其中的一个为DynamicDataSource的defaultTargetDataSource属性中)

    6)将DynamicDataSource作为primary数据源注入到SqlSessionFactory的dataSource属性中去,并且该dataSource作为transactionManager的入参来构造DataSourceTransactionManager

    7)使用的时候,在dao层或service层先使用DatabaseContextHolder设置将要使用的数据源key,然后再调用mapper层进行相应的操作,建议放在dao层去做(当然也可以使用spring aop+自定注解去做)

    注意:在mapper层进行操作的时候,会先调用determineCurrentLookupKey()方法获取一个数据源(获取数据源:先根据设置去targetDataSources中去找,若没有,则选择defaultTargetDataSource),之后在进行数据库操作。

     1、假设有两个数据库,配置如下

    application.properties

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #the first datasource
    jdbc.driverClassName = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://xxx:3306/mytestdb?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
    jdbc.username = root
    jdbc.password = 123
     
    #the second datasource
    jdbc2.driverClassName = com.mysql.jdbc.Driver
    jdbc2.url = jdbc:mysql://xxx:3306/mytestdb2?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8
    jdbc2.username = root
    jdbc2.password = 123

    说明:在之前的配置的基础上,只增加了上述的第二个数据源。 

    2、DatabaseType

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package com.xxx.firstboot.common.datasource;
     
    /**
     * 列出所有的数据源key(常用数据库名称来命名)
     * 注意:
     * 1)这里数据源与数据库是一对一的
     * 2)DatabaseType中的变量名称就是数据库的名称
     */
    public enum DatabaseType {
      mytestdb,mytestdb2
    }

    作用:列举数据源的key。

    3、DatabaseContextHolder

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.xxx.firstboot.common.datasource;
     
    /**
     * 作用:
     * 1、保存一个线程安全的DatabaseType容器
     */
    public class DatabaseContextHolder {
      private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
       
      public static void setDatabaseType(DatabaseType type){
        contextHolder.set(type);
      }
       
      public static DatabaseType getDatabaseType(){
        return contextHolder.get();
      }
    }

    作用:构建一个DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法

    4、DynamicDataSource

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.xxx.firstboot.common.datasource;
     
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
     
    /**
     * 动态数据源(需要继承AbstractRoutingDataSource)
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
      protected Object determineCurrentLookupKey() {
        return DatabaseContextHolder.getDatabaseType();
      }
    }

    作用:使用DatabaseContextHolder获取当前线程的DatabaseType 

    5、MyBatisConfig

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    package com.xxx.firstboot.common;
     
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
     
    import javax.sql.DataSource;
     
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    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.env.Environment;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
     
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import com.xxx.firstboot.common.datasource.DatabaseType;
    import com.xxx.firstboot.common.datasource.DynamicDataSource;
     
    /**
     * springboot集成mybatis的基本入口 1)创建数据源(如果采用的是默认的tomcat-jdbc数据源,则不需要)
     * 2)创建SqlSessionFactory 3)配置事务管理器,除非需要使用事务,否则不用配置
     */
    @Configuration // 该注解类似于spring配置文件
    @MapperScan(basePackages = "com.xxx.firstboot.mapper")
    public class MyBatisConfig {
     
      @Autowired
      private Environment env;
     
      /**
       * 创建数据源(数据源的名称:方法名可以取为XXXDataSource(),XXX为数据库名称,该名称也就是数据源的名称)
       */
      @Bean
      public DataSource myTestDbDataSource() throws Exception {
        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("jdbc.driverClassName"));
        props.put("url", env.getProperty("jdbc.url"));
        props.put("username", env.getProperty("jdbc.username"));
        props.put("password", env.getProperty("jdbc.password"));
        return DruidDataSourceFactory.createDataSource(props);
      }
     
      @Bean
      public DataSource myTestDb2DataSource() throws Exception {
        Properties props = new Properties();
        props.put("driverClassName", env.getProperty("jdbc2.driverClassName"));
        props.put("url", env.getProperty("jdbc2.url"));
        props.put("username", env.getProperty("jdbc2.username"));
        props.put("password", env.getProperty("jdbc2.password"));
        return DruidDataSourceFactory.createDataSource(props);
      }
     
      /**
       * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@autowire注解报错
       * @Qualifier 根据名称进行注入,通常是在具有相同的多个类型的实例的一个注入(例如有多个DataSource类型的实例)
       */
      @Bean
      @Primary
      public DynamicDataSource dataSource(@Qualifier("myTestDbDataSource") DataSource myTestDbDataSource,
          @Qualifier("myTestDb2DataSource") DataSource myTestDb2DataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DatabaseType.mytestdb, myTestDbDataSource);
        targetDataSources.put(DatabaseType.mytestdb2, myTestDb2DataSource);
     
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
        dataSource.setDefaultTargetDataSource(myTestDbDataSource);// 默认的datasource设置为myTestDbDataSource
     
        return dataSource;
      }
     
      /**
       * 根据数据源创建SqlSessionFactory
       */
      @Bean
      public SqlSessionFactory sqlSessionFactory(DynamicDataSource ds) throws Exception {
        SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
        fb.setDataSource(ds);// 指定数据源(这个必须有,否则报错)
        // 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
        fb.setTypeAliasesPackage(env.getProperty("mybatis.typeAliasesPackage"));// 指定基包
        fb.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations")));//
     
        return fb.getObject();
      }
     
      /**
       * 配置事务管理器
       */
      @Bean
      public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
        return new DataSourceTransactionManager(dataSource);
      }
     
    }

    作用:

    • 通过读取application.properties文件生成两个数据源(myTestDbDataSource、myTestDb2DataSource)
    • 使用以上生成的两个数据源构造动态数据源dataSource
      • @Primary:指定在同一个接口有多个实现类可以注入的时候,默认选择哪一个,而不是让@Autowire注解报错(一般用于多数据源的情况下)
      • @Qualifier:指定名称的注入,当一个接口有多个实现类的时候使用(在本例中,有两个DataSource类型的实例,需要指定名称注入)
      • @Bean:生成的bean实例的名称是方法名(例如上边的@Qualifier注解中使用的名称是前边两个数据源的方法名,而这两个数据源也是使用@Bean注解进行注入的)
    • 通过动态数据源构造SqlSessionFactory和事务管理器(如果不需要事务,后者可以去掉)

     6、使用

    ShopMapper:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.xxx.firstboot.mapper;
     
    import org.apache.ibatis.annotations.Param;
    import org.apache.ibatis.annotations.Result;
    import org.apache.ibatis.annotations.Results;
    import org.apache.ibatis.annotations.Select;
     
    import com.xxx.firstboot.domain.Shop;
     
    public interface ShopMapper {
     
      @Select("SELECT * FROM t_shop WHERE id = #{id}")
      @Results(value = { @Result(id = true, column = "id", property = "id"),
                @Result(column = "shop_name", property = "shopName") })
      public Shop getShop(@Param("id") int id);
     
    }

    ShopDao:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package com.xxx.firstboot.dao;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
     
    import com.xxx.firstboot.common.datasource.DatabaseContextHolder;
    import com.xxx.firstboot.common.datasource.DatabaseType;
    import com.xxx.firstboot.domain.Shop;
    import com.xxx.firstboot.mapper.ShopMapper;
     
    @Repository
    public class ShopDao {
      @Autowired
      private ShopMapper mapper;
     
      /**
       * 获取shop
       */
      public Shop getShop(int id) {
        DatabaseContextHolder.setDatabaseType(DatabaseType.mytestdb2);
        return mapper.getShop(id);
      }
    }

    注意:首先设置了数据源的key,然后调用mapper(在mapper中会首先根据该key从动态数据源中查询出相应的数据源,之后取出连接进行数据库操作)

    ShopService:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package com.xxx.firstboot.service;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
     
    import com.xxx.firstboot.dao.ShopDao;
    import com.xxx.firstboot.domain.Shop;
     
    @Service
    public class ShopService {
     
      @Autowired
      private ShopDao dao;
     
      public Shop getShop(int id) {
        return dao.getShop(id);
      }
    }

    ShopController:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package com.xxx.firstboot.web;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
     
    import com.xxx.firstboot.domain.Shop;
    import com.xxx.firstboot.service.ShopService;
     
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
     
    @RestController
    @RequestMapping("/shop")
    @Api("shopController相关api")
    public class ShopController {
     
      @Autowired
      private ShopService service;
     
      @ApiOperation("获取shop信息,测试多数据源")
      @RequestMapping(value = "/getShop", method = RequestMethod.GET)
      public Shop getShop(@RequestParam("id") int id) {
        return service.getShop(id);
      }
     
    }

    补:其实DatabaseContextHolder和DynamicDataSource完全可以合为一个类

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 相关阅读:
    Django--templates(模板层)
    基于 Hive 的文件格式:RCFile 简介及其应用
    Gobblin采集kafka数据
    Scala 中下划线的用途
    Gobblin编译支持CDH5.4.0
    Kafka到Hdfs的数据Pipeline整理
    Hadoop NameNode的ZKFC机制
    Windows下Eclipse提交MR程序到HadoopCluster
    Kettle实现MapReduce之WordCount
    hadoop中MapReduce多种join实现实例分析
  • 原文地址:https://www.cnblogs.com/ceshi2016/p/6735012.html
Copyright © 2011-2022 走看看