SpringBoot2+Mybatis+Druid+mysql+Kylin 配置多数据源详解+BUG

目录

参考文档:

一: 添加pom

二: 添加application.yml

三: 添加主数据源和从数据源Config。

四:测试接口

五:接口调用:

         BUG记录

报错一:

报错二:

报错三:

报错四:

报错五:

报错六:


参考文档:

Springboot2实现多数据源_springboot2多数据源-CSDN博客

https://www.cnblogs.com/aizen-sousuke/p/11756279.html

SpringBoot2+Mybatis+Druid+Kylin 配置多数据源+BUG

整体目录树

一: 添加pom

  <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--SpringBoot MyBatis启动器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.2.2</version>
        </dependency>     

      <dependency>

         <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

     </dependency>

二: 添加application.yml

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.apache.kylin.jdbc.Driver
    # 配置Druid的其他参数,以下配置必须增加一个配置文件才能有效
    druid:
      # 主库数据源
      kylin1:
        url: jdbc:kylin://ip:port/gxy   #生产主节点
        username: 账号        #生产的
        password: 密码        #生产的
        connectionInitSqls: select count(1) from V_GXY_STUDENT_STATUS where SCHOOL_ID = 'a' and BATCH = 'a' LIMIT 1 OFFSET 0
        validation-query: select count(1) from V_GXY_STUDENT_STATUS where SCHOOL_ID = 'a' and BATCH = 'a' LIMIT 1 OFFSET 0
      # 从数据库
      kylin2:
        url: jdbc:kylin://ip:port/steaming   #生产主节点
        username: 账号        #生产的
        password: 密码        #生产的
        connectionInitSqls: select count(1) from KAFKSIGN where SCHOOLID = 'a' and PLANID = 'a' LIMIT 1 OFFSET 0
        validation-query: select count(1) from KAFKSIGN where SCHOOLID = 'a' and PLANID = 'a' LIMIT 1 OFFSET 0
      # 连接池配置
      initial-size: 3
      max-active: 40
      min-idle: 3
      max-wait: 600000
      pool-prepared-statements: false
      max-pool-prepared-statement-per-connection-size: -1
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        login-username: 账号
        login-password: 密码
      filter:
        stat:
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: false
        wall:
          config:
            multi-statement-allow: true

三: 添加主数据源和从数据源Config。

Kylin1:

package com.*.*.bigdata.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = {"com.*.*.bigdata.dao.mapper"}, sqlSessionFactoryRef = "kylin1SqlSessionFactory")
public class Kylin1DataSourcesConfig {
    //主数据源mapper xml文件夹路径
    private static final String MAPPER_LOCAL = "classpath:com/*/*/bigdata/dao/mapper/*.xml";

    @ConfigurationProperties("spring.datasource.druid.kylin1")
    @Primary
    @Bean(name = "kylin1DataSource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "kylin1TransactionManager")
    @Primary
    public DataSourceTransactionManager kylin1TransactionManager() {
        return new DataSourceTransactionManager(druidDataSource());
    }

    @Bean(name = "kylin1SqlSessionFactory")
    @Primary
    public SqlSessionFactory kylin1SqlSessionFactory(@Qualifier("kylin1DataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCAL));
        return sessionFactoryBean.getObject();
    }
}

kylin2:

package com.zhangtao.moguding.bigdata.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = {"com.*.*.bigdata.dao.steaming"}, sqlSessionFactoryRef = "kylin2SqlSessionFactory")
public class kylin2DataSourceConfig {
    //主数据源mapper xml文件夹路径
    private static final String MAPPER_LOCAL = "classpath:com/*/*/bigdata/dao/steaming/*.xml";

    @ConfigurationProperties("spring.datasource.druid.kylin2")
    @Bean(name = "kylin2DataSource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "kylin2TransactionManager")
    public DataSourceTransactionManager kylin2TransactionManager() {
        return new DataSourceTransactionManager(druidDataSource());
    }

    @Bean(name = "kylin2SqlSessionFactory")
    public SqlSessionFactory kylin2SqlSessionFactory(@Qualifier("kylin2DataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCAL));
        return sessionFactoryBean.getObject();
    }

}

四:测试接口

Controller:

package com.*.*.bigdata.controller;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/bigdata")
public class GxyTestController {
    @Autowired
    GxyTestService gxyTestService;

    @RequestMapping("/v1/test")
    @ApiOperation(value = "测试接口")
    public ObjectResponse testController(@RequestBody StatisticsQuery query) throws Exception {
        return  gxyTestService.testService();
    }

    @RequestMapping("/v2/test")
    @ApiOperation(value = "测试接口")
    public ObjectResponse test2Controller(@RequestBody StatisticsQuery query) throws Exception {
        return  gxyTestService.test2Service();
    }

}

Service:

package com.*.*.bigdata.service;


public interface GxyTestService {

    ObjectResponse testService();

    ObjectResponse test2Service();
}

ServiceImpl:

package com.*.*.bigdata.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/** 
* @Description: 测试接口 
*/

@Service
public class GxyTestServiceImpl implements GxyTestService {
    @Autowired
    GxyTestMapper gxyTestMapper;
    @Autowired
    GxyTestSteamingMaapper GxyTestSteamingMaapper;

    @Override
    public ObjectResponse testService() {

        Integer test =  gxyTestMapper.testCount();
        System.out.println("test"+test);
        return ObjectResponse.resObj(test);
    }

    @Override
    public ObjectResponse test2Service() {
        Integer test =  GxyTestSteamingMaapper.testSteaming();
        System.out.println("test"+test);
        return ObjectResponse.resObj(test);
    }

}

Mapper:

package com.*.*.bigdata.dao.mapper;
import org.springframework.stereotype.Repository;

import java.util.Date;

@Repository
public interface GxyTestMapper {
    Integer testCount();

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.*.*.bigdata.dao.mapper.GxyTestMapper">
    <!-- 查询学生状态表最新分区 -->
    <select id="testCount"  resultType="java.lang.Integer" >
        <!--select count(1) from KAFKSIGN -->

        select count(1) from GXY_JOB02 where NOWDATE ='2021-05-27'
        <!--这里加where条件只为了命中状态表新的cube,不然会命中老的cube导致日期获取不准-->
    </select>

</mapper>

xml:

package com.*.*.bigdata.dao.steaming;

import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

@Repository
public interface GxyTestSteamingMaapper {
    Integer testSteaming();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.*.*.bigdata.dao.steaming.GxyTestSteamingMaapper">
<!-- 查询学生状态表最新分区 -->
<select id="testSteaming"  resultType="java.lang.Integer" >
        select count(1) from KAFKSIGN
    </select>

</mapper>

五:接口调用:

kylin1:

kylin2:

BUG记录

报错一:

java.sql.SQLException: url not set

org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: url not set

springboot application.properties druid:url 和 jdbc-url的区别

写 jdbc-url 是因为这是两种数据库类型不一样时候的写法,同一种数据库用url就可以

如果我这里写的是jdbc-url, 那么会报标题的错误,还有一种原因是因为config文件夹多了一个DruidConfig。

在DruidConfig中配置了 @ConfigurationProperties(prefix = "spring.datasource.druid") 但其实DruidConfig的作用是如果只有一个数据源的情况下默认的配置,有两个数据源可以讲这个类删掉或者整个类都注释掉即可

报错二:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.*.*.*.dao.steaming.GxyTestSteamingMaapper.testSteaming

这个错误是xml和mapper接口类无法识别, 图中框起来的 <resource> 如果不写那么编译后target下的class内也不会有xml文件,在pom文件的build增加对应的resource配置后,运行正常。

配置如下:

    <build>
        <resources>
            <!-- 打包java里mapper的xml文件 -->
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <!-- 打包resource里的项目配置文件 -->
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                    <include>**/*.docx</include>
                    <include>**/*.jpg</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

 

报错三:

ERROR 2224 --- [reate-192486017] com.alibaba.druid.pool.DruidDataSource   : create connection RuntimeException

java.lang.NullPointerException: null
    at org.apache.kylin.jdbc.shaded.org.apache.calcite.avatica.UnregisteredDriver.acceptsURL(UnregisteredDriver.java:144)
    at org.apache.kylin.jdbc.shaded.org.apache.calcite.avatica.UnregisteredDriver.connect(UnregisteredDriver.java:130)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1596)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1662)
    at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2697)

这个原因是因为DruidConfig的 @ConfigurationProperties(prefix = "spring.datasource") 配置到了datasource,但datasource下面有两个数据源,而DruidConfig的作用是有一个数据源的时候的一些默认配置,所以有两个数据源的话,DruidConfig类可以注释掉或者删掉。

报错四:

mapper-locations:

#mybatis
mybatis-plus:
  mapper-locations: classpath*:com/zhangtao/moguding/*/dao/**/*.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: com.zhangtao.moguding.*.dto
  configuration:
        #配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
        map-underscore-to-camel-case: true
        cache-enabled: false
        #配置JdbcTypeForNull, oracle数据库必须配置
        jdbc-type-for-null: 'null'

application.yml中mapper-locations 配置项主要用于mapper.xml和mapper接口不在同一个目录下的时候使用,如果mapper.xml和mapper接口在同一个目录,那么该配置项无作用

参考博客:mybatis mapper-locations作用 - 灰信网(软件开发博客聚合)

报错五:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-05-27 20:25:28.199 ERROR 1824 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name '*': Unsatisfied dependency expressed through field '*'; nested exception is org.springframework.beans.factory

这个错的原因是在Kylin1DataSourceConfig.java类中sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources());方法的调用中xml指定错误,导致无法正常绑定bean,错误写法是:

正确写法有两种: 

private static final String MAPPER_LOCAL = "classpath*:/mapper/*.xml";
private static final String MAPPER_LOCAL = "classpath:com/*/*/bigdata/dao/mapper/*.xml";

报错六:

org.apache.ibatis.binding.BindingException: Incalid bound statement (not found) : com.*.*.bigdata.dao.GxyTestSteamingMapper.testSteaming

报这个错的原因是因为Mapper类和xml没有映射到。下面是我原目录结构:Mapper类和xml没有在同一个文件夹中

然后把目录结构改为:

将Mapper类和xml放在同一个文件夹中,项目可以运行接口可以正常调用,据体原因不是太清楚但是看第二篇参考链接得知大佬的目录结构都是划分很明确的,不知道是不是这个原因。还有就是在Kylin2DataSourceConfig中, @MapperScan注解扫描的是mapper下面的mapper类,但是我最开始的目录结构steaming下面没有mapper类只有xml,所以报错。而 sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources())  方法 扫描的是xml。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot项目中使用MyBatis Plus和Druid多数据源的步骤如下: 1. 添加依赖 在`pom.xml`文件中添加以下依赖: ```xml <!-- MyBatis Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.1</version> </dependency> <!-- Druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> ``` 2. 配置Druid数据源 在`application.yml`中添加Druid数据源的配置: ```yaml spring: datasource: # 主数据源 druid: url: jdbc:mysql://localhost:3306/main_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver # Druid配置 initialSize: 5 minIdle: 5 maxActive: 20 testOnBorrow: false testOnReturn: false testWhileIdle: true timeBetweenEvictionRunsMillis: 60000 validationQuery: SELECT 1 FROM DUAL # 从数据源 druid2: url: jdbc:mysql://localhost:3306/sub_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver # Druid配置 initialSize: 5 minIdle: 5 maxActive: 20 testOnBorrow: false testOnReturn: false testWhileIdle: true timeBetweenEvictionRunsMillis: 60000 validationQuery: SELECT 1 FROM DUAL ``` 3. 配置MyBatis Plus 在`application.yml`中添加MyBatis Plus的配置: ```yaml mybatis-plus: # 主数据源配置 mapper-locations: classpath:mapper/main/*.xml type-aliases-package: com.example.main.entity global-config: db-config: id-type: auto field-strategy: not_empty logic-delete-value: 1 logic-not-delete-value: 0 configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 从数据源配置 multi-datasource: main: mapper-locations: classpath:mapper/main/*.xml type-aliases-package: com.example.main.entity sub: mapper-locations: classpath:mapper/sub/*.xml type-aliases-package: com.example.sub.entity ``` 4. 配置数据源路由 在`com.example.config`包下创建`DynamicDataSourceConfig`类,用于配置数据源路由: ```java @Configuration public class DynamicDataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.druid") public DataSource mainDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid2") public DataSource subDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object, Object> dataSourceMap = new HashMap<>(2); dataSourceMap.put("main", mainDataSource()); dataSourceMap.put("sub", subDataSource()); // 将主数据源作为默认数据源 dynamicDataSource.setDefaultTargetDataSource(mainDataSource()); dynamicDataSource.setTargetDataSources(dataSourceMap); return dynamicDataSource; } @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource()); sqlSessionFactoryBean.setTypeAliasesPackage("com.example.main.entity"); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/main/*.xml")); return sqlSessionFactoryBean.getObject(); } @Bean public SqlSessionTemplate sqlSessionTemplate() throws Exception { return new SqlSessionTemplate(sqlSessionFactory()); } } ``` 5. 配置数据源切换 在`com.example.config`包下创建`DynamicDataSource`类,用于实现数据源切换: ```java public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } } ``` 在`com.example.config`包下创建`DataSourceContextHolder`类,用于存储当前数据源: ```java public class DataSourceContextHolder { private static final ThreadLocal<String> DATASOURCE_CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSource(String dataSource) { DATASOURCE_CONTEXT_HOLDER.set(dataSource); } public static String getDataSource() { return DATASOURCE_CONTEXT_HOLDER.get(); } public static void clearDataSource() { DATASOURCE_CONTEXT_HOLDER.remove(); } } ``` 在`com.example.aop`包下创建`DataSourceAspect`类,用于切换数据源: ```java @Aspect @Component public class DataSourceAspect { @Pointcut("@annotation(com.example.annotation.DataSource)") public void dataSourcePointCut() { } @Before("dataSourcePointCut()") public void before(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); DataSource dataSource = signature.getMethod().getAnnotation(DataSource.class); if (dataSource != null) { String value = dataSource.value(); DataSourceContextHolder.setDataSource(value); } } @After("dataSourcePointCut()") public void after(JoinPoint joinPoint) { DataSourceContextHolder.clearDataSource(); } } ``` 6. 使用多数据源 在需要使用从数据源的方法上加上`@DataSource("sub")`注解,如: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public List<User> listUsers() { DataSourceContextHolder.setDataSource("sub"); List<User> users = userMapper.selectList(null); DataSourceContextHolder.clearDataSource(); return users; } } ``` 这样就完成了Spring Boot项目中使用MyBatis Plus和Druid多数据源配置

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李指导、

您的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值