1. 程式人生 > >SpringBoot整合MybatisPlus配置多資料來源

SpringBoot整合MybatisPlus配置多資料來源

首先建立SpringBoot專案,匯入web模組;

匯入依賴:

<!--aop-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!--mybatis plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!--druid-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

使用自動生成工具生成實體以及mapper等:

public class MyGenerator {


    /**
     * <p>
     * MySQL 生成演示
     * </p>
     */
    public static void main(String[] args) {
        AutoGenerator mpg = new AutoGenerator();

        // 全域性配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir("D:\\test");
        gc.setFileOverride(true);
        gc.setActiveRecord(true);
        gc.setEnableCache(false);// XML 二級快取
        gc.setBaseResultMap(false);// XML ResultMap
        gc.setBaseColumnList(false);// XML columList
        gc.setAuthor("baiyongcheng");

        // 自定義檔案命名,注意 %s 會自動填充表實體屬性!
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
//         gc.setControllerName("%sController");
        mpg.setGlobalConfig(gc);

        // 資料來源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.MYSQL);
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setUrl("jdbc:mysql://127.0.0.1:3306/woniulearn?characterEncoding=utf8");
        mpg.setDataSource(dsc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
//        strategy.setTablePrefix(new String[]{""});// 此處可以修改為您的表字首
//        strategy.setNaming(NamingStrategy.remove_prefix_and_camel);// 表名生成策略
//        strategy.setNaming(NamingStrategy.removePrefixAndCamel());// 表名生成策略
        strategy.setInclude(new String[]{"users"}); // 需要生成的表
//        strategy.setExclude(new String[]{"t_rong_bid"}); // 排除生成的表
        // 欄位名生成策略
        strategy.setNaming(NamingStrategy.underline_to_camel);
        // 自定義實體父類
//         strategy.setSuperEntityClass("hello.entity.BaseEntity");
        // 自定義實體,公共欄位
//        strategy.setSuperEntityColumns(new String[]{"id"});
        // 自定義 mapper 父類
        // strategy.setSuperMapperClass("com.fcs.demo.TestMapper");
        // 自定義 service 父類
        // strategy.setSuperServiceClass("com.fcs.demo.TestService");
        // 自定義 service 實現類父類
        // strategy.setSuperServiceImplClass("com.fcs.demo.TestServiceImpl");
        // 自定義 controller 父類
//         strategy.setSuperControllerClass("com.risk.controller.BaseController");
        // 【實體】是否生成欄位常量(預設 false)
        // public static final String ID = "test_id";
        // strategy.setEntityColumnConstant(true);
        // 【實體】是否為構建者模型(預設 false)
        // public User setName(String name) {this.name = name; return this;}
        // strategy.setEntityBuliderModel(true);
        mpg.setStrategy(strategy);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.demo");
        pc.setModuleName("");
        pc.setController("controller");

        mpg.setPackageInfo(pc);

        // 執行生成
        mpg.execute();
    }

}

將生成的程式碼拷貝到專案檔案結構中;

建立application.yml配置檔案:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    druid:
      first:
        url: jdbc:mysql://localhost:3306/woniulearn?useSSL=false
        username: root
        password: root
      second:
        url: jdbc:mysql://localhost:3306/bossqiang?useSSL=false
        username: root
        password: root

編寫動態資料來源類:

public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    /*
     *ThreadLocal 用於提供執行緒區域性變數,在多執行緒環境可以保證各個執行緒裡的變數獨立於其它執行緒裡的變數。
     * 也就是說 ThreadLocal 可以為每個執行緒建立一個【單獨的變數副本】
     * 相當於執行緒的 private static 型別變數。
     */
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }
    public static String getDataSource() {
        return contextHolder.get();
    }
    public static void clearDataSource() {
        contextHolder.remove();
    }

}

編寫配置類:

@Configuration
public class DynamicDataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.druid.first")
    public DataSource firstDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource secondDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
        targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
        return new DynamicDataSource(firstDataSource, targetDataSources);
    }
}

替換預設的配置為自己編寫的配置:

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@Import({DynamicDataSourceConfig.class})
@MapperScan("com.demo.mapper")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

建立註解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String name() default "";
}

為了提升可讀性,可配置性建立一個列舉類:

public interface DataSourceNames {
    String FIRST = "first";
    String SECOND = "second";
}

建立切面解析註解:

@Aspect
@Component
public class DataSourceAspect implements Ordered {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(com.demo.DataSource)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();

        DataSource ds = method.getAnnotation(DataSource.class);
        if(ds == null){
            DynamicDataSource.setDataSource(DataSourceNames.FIRST);
            logger.debug("set datasource is " + DataSourceNames.FIRST);
        }else {
            DynamicDataSource.setDataSource(ds.name());
            logger.debug("set datasource is " + ds.name());
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            logger.debug("clean datasource");
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

建立service的介面:

public interface UsersService extends IService<Users> {
    Users findUserByFirstDb(int id);

    Users findUserBySecondDb(int id);
}

實現類:

@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {

    @Override
    public Users findUserByFirstDb(int id) {
        return this.baseMapper.selectById(id);
    }

    @Override
    @DataSource(name = DataSourceNames.SECOND)
    public Users findUserBySecondDb(int id) {
        return this.baseMapper.selectById(id);
    }
}

測試類:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @Autowired
    UsersService userService;

    @Test
    public void contextLoads() {

        Users user = userService.getById(1);
        System.out.println(user.toString());

    }

    @Test
    public void test() {
        Users user = userService.findUserByFirstDb(1);
        System.out.println("第one個數據庫---------》" + user.toString());


        Users user2 = userService.findUserBySecondDb(1);
        System.out.println("第二個資料庫---------》" + user2.toString());
    }

}

執行測試類結果為:

第one個數據庫---------》Users{userId=1, tel=18682558655, password=DAF2ABB2A61A210C82E374AC2A5BFFD6, userRoleId=2, flag=0, userName=鄧強, sex=null, birthday=null, qq=null, email=null, icon=page/image/userIcon/teacherIcon/dengqiang.JPG, registerTime=null, school=null, education=null, integralNum=30, position=蝸牛學院資深講師, description=蝸牛學院資深導師,四川大學碩士,企業內訓講師,獨立諮詢顧問,14年軟體研發及管理經驗,測試架構師,豐富的實施經驗。2009年到2015年期間擔任某培訓機構總經理兼資深講師職位,2004年到2009年擔任蘇州華冠科技測試經理。, fans=2075, company=null, technology=null, major=null, city=null, realName=鄧強, address=null, rewardIntegral=30}
2018-10-08 13:19:34.870  INFO 28324 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-2} inited
第二個資料庫---------》Users{userId=1, tel=13123123, password=文峰區威鋒網缺乏權威, userRoleId=null, flag=null, userName=null, sex=null, birthday=null, qq=null, email=null, icon=null, registerTime=null, school=null, education=null, integralNum=null, position=null, description=null, fans=null, company=null, technology=null, major=null, city=null, realName=null, address=null, rewardIntegral=null}

目錄結構為: