参考文档: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);
}
}