当前位置: 首页 > 工具软件 > fastmybatis > 使用案例 >

fastmybatis多数据源配置

钱欣然
2023-12-01

参考文档:fastmybatis: 一个mybatis开发框架,其宗旨为:简单、快速、有效 - Gitee.com

配置过程中遇到很多书写错误,但是最核心的一个问题是有个

MybatisAutoConfiguration类提前加载了mapper,导致多数据源DBMasterConfig和DbSecondConfig 配置加载mapper跳过了,(WARN 99535 --- [           main] o.m.s.mapper.ClassPathMapperScanner      : Skipping MapperFactoryBean with name)

2022-03-22 17:37:37.213  INFO 99535 --- [           main] com.ang.jh.EurekaServiceApplication      : The following profiles are active: dev
2022-03-22 17:37:38.041 DEBUG 99535 --- [           main] c.g.f.s.b.a.MybatisAutoConfiguration     : Searching for mappers annotated with @Mapper
2022-03-22 17:37:38.049 DEBUG 99535 --- [           main] c.g.f.s.b.a.MybatisAutoConfiguration     : Using auto-configuration base package 'com.ang.jh'
2022-03-22 17:37:38.296  WARN 99535 --- [           main] o.m.s.mapper.ClassPathMapperScanner      : Skipping MapperFactoryBean with name 'fdBusSystemMapper' and 'com.ang.jh.pay.dao.FdBusSystemMapper' mapperInterface. Bean already defined with the same name!
然后第二个数据源对应的mapper查询一直报错org.apache.ibatis.binding.BindingException: Invalid bound statement (not found),
2022-03-22 17:38:29.865 ERROR 99535 --- [nio-8712-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.ang.jh.fund.dao.BasicIotDeviceMapper.list] with root cause

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.ang.jh.fund.dao.BasicIotDeviceMapper.list
	at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:235)
	at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:53)
	at org.apache.ibatis.binding.MapperProxy.lambda$cachedInvoker$0(MapperProxy.java:108)
	at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
	at org.apache.ibatis.util.MapUtil.computeIfAbsent(MapUtil.java:36)
	at org.apache.ibatis.binding.MapperProxy.cachedInvoker(MapperProxy.java:95)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
	at com.sun.proxy.$Proxy68.list(Unknown Source)
	at com.ang.jh.pay.controller.FdBusSystemController.listAll(FdBusSystemController.java:41)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

后来将该类从springboot配置类中排除后,mapper终于可以正常查询了。

文末贴代码:

pom文件相关依赖

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.6.1</version>
</parent>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>net.oschina.durcframework</groupId>
  <artifactId>fastmybatis-spring-boot-starter</artifactId>
  <version>2.1.0</version>
</dependency>
<!-- 分页插件,非必须 -->
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.2.0</version>
</dependency>

application-dev.properties


# mysql
mysql.ip=127.0.0.1
mysql.username=root
mysql.password=123456


spring.datasource.master.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.master.jdbc-url=jdbc:mysql://${mysql.ip}:3306/aaa?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
spring.datasource.master.username=${mysql.username}
spring.datasource.master.password=${mysql.password}

spring.datasource.second.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.second.jdbc-url=jdbc:mysql://${mysql.ip}:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
spring.datasource.second.username=${mysql.username}
spring.datasource.second.password=${mysql.password}

DbMasterConfig
package com.ang.jh.config;

import com.gitee.fastmybatis.core.FastmybatisConfig;
import com.gitee.fastmybatis.core.ext.SqlSessionFactoryBeanExt;
import com.gitee.fastmybatis.core.support.DateFillInsert;
import com.gitee.fastmybatis.core.support.DateFillUpdate;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Arrays;

@Configuration
@MapperScan(basePackages = {DbMasterConfig.basePackage} , sqlSessionFactoryRef = DbMasterConfig.sqlSessionFactoryName)
public class DbMasterConfig {
    Logger logger = LoggerFactory.getLogger(DbMasterConfig.class);
    /* ********************只需要改这里的配置******************** */
    static final String dbName = "master";
    /** 配置文件前缀 */
    public static final String prefix = "spring.datasource.master";
    /** 存放mapper包路径 */
    public static final String basePackage = "com.ang.jh.pay.dao";
    /** mybatis的config文件路径 */
    public static final String mybatisConfigLocation = "classpath:/mybatis/mybatisConfig.xml";
    /** mybatis的mapper文件路径 */
    public static final String mybatisMapperLocations = "classpath:/mybatis/mapper/*.xml";
    /** 表新增时间字段名 */
    public static final String dbInsertDateColumnName = "createdTime";
    /** 表更新时间字段名 */
    public static final String dbUpdateDateColumnName = "updatedTime";
    /* **************************************************** */

    /** 数据源名称 */
    public static final String dataSourceName = "dataSource" + dbName;
    /** sqlSessionTemplate名称 */
    public static final String sqlSessionTemplateName = "sqlSessionTemplate" + dbName;
    /** sqlSessionFactory名称 */
    public static final String sqlSessionFactoryName = "sqlSessionFactory" + dbName;
    /** transactionManager名称 */
    public static final String transactionManagerName = "transactionManager" + dbName;
    /** transactionTemplate名称 */
    public static final String transactionTemplateName = "transactionTemplate" + dbName;


    @Bean(name = dataSourceName)
    @Primary
    @ConfigurationProperties(prefix = prefix) // application.properteis中对应属性的前缀
    public DataSource dataSource() {
        logger.info("DbMasterConfig start loading");
        return DataSourceBuilder.create().build();
    }

    public FastmybatisConfig fastmybatisConfig() {
        FastmybatisConfig config = new FastmybatisConfig();
        /*
         * 驼峰转下划线形式,默认是true 开启后java字段映射成数据库字段将自动转成下划线形式 如:userAge -> user_age
         * 如果数据库设计完全遵循下划线形式,可以启用 这样可以省略Entity中的注解,@Table,@Column都可以不用,只留
         *
         * @Id
         *
         * @GeneratedValue 参见:UserInfo.java
         */
        config.setCamel2underline(true);
        config.setFills(Arrays.asList(new DateFillInsert(dbInsertDateColumnName),
                new DateFillUpdate(dbUpdateDateColumnName)));

        return config;
    }

    @Bean(name = sqlSessionFactoryName)
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Autowired @Qualifier(dataSourceName) DataSource dataSource) throws Exception {
        Assert.notNull(dataSource, "dataSource can not be null.");

        SqlSessionFactoryBeanExt bean = new SqlSessionFactoryBeanExt();

        bean.setDataSource(dataSource);
        bean.setConfigLocation(this.getResource(mybatisConfigLocation));
        bean.setMapperLocations(this.getResources(mybatisMapperLocations));


        // ====以下是附加属性====

        // dao所在的包名,跟MapperScannerConfigurer的basePackage一致,多个用;隔开
        bean.setBasePackage(basePackage);
        bean.setConfig(fastmybatisConfig());

        return bean.getObject();

    }

    @Bean(name = sqlSessionTemplateName)
    @Primary
    public SqlSessionTemplate sqlSessionTemplate(
            @Autowired @Qualifier(sqlSessionFactoryName) SqlSessionFactory sessionFactory) throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sessionFactory); // 使用上面配置的Factory
        return template;
    }

    @Bean(name = transactionManagerName)
    @Primary
    public PlatformTransactionManager annotationDrivenTransactionManager(
            @Autowired @Qualifier(dataSourceName) DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = transactionTemplateName)
    @Primary
    public TransactionTemplate transactionTemplate(@Autowired @Qualifier(transactionManagerName)PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager);
    }


    private Resource[] getResources(String path) throws IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        return resolver.getResources(path);
    }

    private Resource getResource(String path) {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        return resolver.getResource(path);
    }

}
DbSecondConfig
package com.ang.jh.config;

import com.gitee.fastmybatis.core.FastmybatisConfig;
import com.gitee.fastmybatis.core.ext.SqlSessionFactoryBeanExt;
import com.gitee.fastmybatis.core.support.DateFillInsert;
import com.gitee.fastmybatis.core.support.DateFillUpdate;
import com.gitee.fastmybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Arrays;

/**
 * 第二个数据源,后续有第三个数据,复制这个文件,然后改下配置即可
 * @author tanghc
 */
@Configuration
@MapperScan(basePackages = {DbSecondConfig.basePackage} , sqlSessionFactoryRef = DbSecondConfig.sqlSessionFactoryName)
public class DbSecondConfig {

    /* ********************只需要改这里的配置******************** */
    static final String dbName = "Second";
    /** 配置文件前缀 */
    public static final String prefix = "spring.datasource.second";
    /** 存放mapper包路径 */
    public static final String basePackage = "com.ang.jh.fund.dao";
    /** mybatis的config文件路径 */
    public static final String mybatisConfigLocation = "classpath:/mybatis2/mybatisConfig.xml";
    /** mybatis的mapper文件路径 */
    public static final String mybatisMapperLocations = "classpath:/mybatis2/mapper/*.xml";
    /** 表新增时间字段名 */
    public static final String dbInsertDateColumnName = "createdTime";
    /** 表更新时间字段名 */
    public static final String dbUpdateDateColumnName = "updatedTime";
    /* **************************************************** */

    /** 数据源名称 */
    public static final String dataSourceName = "dataSource" + dbName;
    /** sqlSessionTemplate名称 */
    public static final String sqlSessionTemplateName = "sqlSessionTemplate" + dbName;
    /** sqlSessionFactory名称 */
    public static final String sqlSessionFactoryName = "sqlSessionFactory" + dbName;
    /** transactionManager名称 */
    public static final String transactionManagerName = "transactionManager" + dbName;
    /** transactionTemplate名称 */
    public static final String transactionTemplateName = "transactionTemplate" + dbName;
    @Autowired
    private ApplicationContext applicationContext;

    @Bean(name = dataSourceName)
    @ConfigurationProperties(prefix = prefix) // application.properteis中对应属性的前缀
    public DataSource dataSourceMater() {
        return DataSourceBuilder.create().build();
    }

    public FastmybatisConfig fastmybatisConfig() {
        FastmybatisConfig config = new FastmybatisConfig();
        /*
         * 驼峰转下划线形式,默认是true 开启后java字段映射成数据库字段将自动转成下划线形式 如:userAge -> user_age
         * 如果数据库设计完全遵循下划线形式,可以启用 这样可以省略Entity中的注解,@Table,@Column都可以不用,只留
         *
         * @Id
         *
         * @GeneratedValue 参见:UserInfo.java
         */
        config.setCamel2underline(true);
        config.setFills(Arrays.asList(new DateFillInsert(dbInsertDateColumnName),
                new DateFillUpdate(dbUpdateDateColumnName)));

        return config;
    }

    @Bean(name = sqlSessionFactoryName)
    public SqlSessionFactory sqlSessionFactory(@Autowired @Qualifier(dataSourceName) DataSource dataSource) throws Exception {
        Assert.notNull(dataSource, "dataSource can not be null.");
        SqlSessionFactoryBeanExt bean = new SqlSessionFactoryBeanExt();

        bean.setDataSource(dataSource);
        bean.setConfigLocation(this.getResource(mybatisConfigLocation));
        bean.setMapperLocations(this.getResources(mybatisMapperLocations));

        // ====以下是附加属性====

        // dao所在的包名,跟MapperScannerConfigurer的basePackage一致,多个用;隔开
        bean.setBasePackage(basePackage);
        bean.setConfig(fastmybatisConfig());

        return bean.getObject();

    }

    @Bean(name = sqlSessionTemplateName)
    public SqlSessionTemplate sqlSessionTemplate(
            @Autowired @Qualifier(sqlSessionFactoryName) SqlSessionFactory sessionFactory) throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sessionFactory); // 使用上面配置的Factory
        return template;
    }

    @Bean(name = transactionManagerName)
    public PlatformTransactionManager annotationDrivenTransactionManager(
            @Autowired @Qualifier(dataSourceName) DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = transactionTemplateName)
    public TransactionTemplate transactionTemplate(@Autowired @Qualifier(transactionManagerName)PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager);
    }


    private Resource[] getResources(String path) throws IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        return resolver.getResources(path);
    }

    private Resource getResource(String path) {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        return resolver.getResource(path);
    }

}

EurekaServiceApplication(重点!!!

@SpringBootApplication(exclude = MybatisAutoConfiguration.class)没这一行除主数据源以外的mapper会报错

package com.ang.jh;


import com.gitee.fastmybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(exclude = MybatisAutoConfiguration.class)
public class EurekaServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServiceApplication.class,args);
    }
}

 类似资料: