1、Springboot多数据源配置后导致的打印日志问题
(1)描述:springboot整合多数据源后无法打印控制台没打印sql
(2)原因:因为(mybatis.configuration.log-impl)tk.mybatis 当SqlSessionFactory不存在定义的时候定义SqlSessionFactory;而我们配置多数据源时,重新定义了SqlSessionFactory;因此,如果要打印sql 语句的话,需在我们配置多数据源的SqlSessionFactory时添加。
具体解决方案:
(1)确认application配置
mybatis.configuration.jdbc-type-for-null=NULL
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.type-aliases-package=com.**.entity
mybatis.mapper-locations=classpath*:mapper/**/*Mapper.xml
#### print sql
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(2)在数据源配置目录下加入MybatisProperties即可
@Primary
@Bean(name="damsqlSessionFac")
public SqlSessionFactory damsqlSessionFactory(@Qualifier("damData") DataSource ds
, MybatisProperties mybatisConfigLog) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
DataSource damDatasource=ds;
bean.setDataSource(damDatasource);
//此处设置打印日志问题
bean.setConfiguration(mybatisConfigLog.getConfiguration());
return bean.getObject();
}
2、解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)问题,一般考虑的问题是mapper的xml对应问题,可考虑如下方向:
(1)是否mapper.java文件上使用了注解@Mapper 或者 在启动类上扫描了Mapper类 @MapperScan(“com.heima.model.mappers”) 【注意扫描的包名是否正确】
(2)注意mapper.xml文件中的namespace是否正确指向到Mapper.java类的位置 【】
(3)注意被调用的方法名在mapper.java中和mapper.xml中的id保持一致性
(4)注意mapper.xml方法的parameterType尽量指定全路径的domain
(5)注意Mapper.java在被调用的地方需要注入@Autowired
(6)最终,一定一定要注意你建立的mapper.xml是xml文件,因为你创建的file在idea上默认会被识别为xml。
如果上述几项未解决问题,可考虑下方
该问题存在的地方为服务启动后的报错,说明在初始化过程中mapper对应的xml已经映射完成,在调用对应的功能是,mybatis找不到对应的typeAliasesPackage,故,可以在sqlsessionfactory中把所有的typeAliasesPackage设置在内,具体的解决方案如下:
public static String setTypeAliasesPackage(String typeAliasesPackage) {
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
List<String> allResult = new ArrayList<String>();
try {
for (String aliasesPackage : typeAliasesPackage.split(",")) {
List<String> result = new ArrayList<String>();
aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = resolver.getResources(aliasesPackage);
if (resources != null && resources.length > 0) {
MetadataReader metadataReader = null;
for (Resource resource : resources) {
if (resource.isReadable()) {
metadataReader = metadataReaderFactory.getMetadataReader(resource);
try {
result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
if (result.size() > 0) {
HashSet<String> hashResult = new HashSet<String>(result);
allResult.addAll(hashResult);
}
}
if (allResult.size() > 0) {
typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
} else {
throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
}
} catch (IOException e) {
e.printStackTrace();
}
return typeAliasesPackage;
}
public Resource[] resolveMapperLocations(String[] mapperLocations) {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList<Resource>();
if (mapperLocations != null) {
for (String mapperLocation : mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException e) {
// ignore
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
@Bean(name="creditsqlSessionFac")
public SqlSessionFactory sqlSessionFactorySecondary(@Qualifier("creditData") DataSource ds) throws Exception {
String typeAliasesPackage = env.getProperty("mybatis.type-aliases-package");
String mapperLocations = env.getProperty("mybatis.mapper-locations");
typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
VFS.addImplClass(SpringBootVFS.class);
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(ds);
bean.setTypeAliasesPackage(typeAliasesPackage);
bean.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
return bean.getObject();
}
3、1 字节的 UTF-8 序列的字节 1 无效
该问题一般解决思路为找到报错位置,将格式强制转换为utf8即可(也可能出现xml文件中utf8设置有误的问题,可作为排查项)
4、解决mybatis resultMap根据type找不到对应的包问题
该问题主要是在mapper.xml中resultmap中type和sql语句中parameterType设置对应的entity无法找到的问题,
具体如下
<resultMap type="CreditAccessMaster" id="CreditAccessMasterResult">
<select id="selectCreditAccessMasterList" parameterType="CreditAccessMaster" resultMap="CreditAccessMasterResult">
(1)首先需要查看type和parameterType的名称与mapper的java文件的的命名是否一致。
(2)注意mapper.xml文件中的namespace是否正确指向到Mapper.java类的位置。
(3)注意被调用的方法名在mapper.java中和mapper.xml中的id保持一致性。
(4)名称无误的话则需要在application.properties中配置mybatis的type-aliases-package以及mapper-locations,具体如下所示
mybatis.type-aliases-package=com.**.entity
mybatis.mapper-locations=classpath*:mapper/**/*Mapper.xml
5 springboot数据连接池配置问题
在谈论该问题之前首先需要了解数据访问基本概念:
(1)JDBC
Java数据库连接(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
JDBC的API主要有:
- DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。
- Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。
- Connection:数据库连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement
- Statement:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。
- PreparedStatement:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。
- CallableStatement:用以调用数据库中的存储过程。
- SQLException:代表在数据库连接的建立和关闭和SQL语句的执行过程中发生了例外情况(即错误)。
(2)数据源
可以看到,在 java.sql 中并没有数据源(Data Source)的概念。这是由于在java.sql中包含的是JDBC内核API,另外还有个javax.sql包,其中包含了JDBC标准的扩展API。而关于数据源(Data Source)的定义,就在javax.sql这个扩展包中。
实际上,在JDBC内核API的实现下,就已经可以实现对数据库的访问了,那么我们为什么还需要数据源呢?主要出于以下几个目的:
封装关于数据库访问的各种参数,实现统一管理
通过对数据库的连接池管理,节省开销并提高效率
在Java这个自由开放的生态中,已经有非常多优秀的开源数据源可以供大家选择,比如:DBCP、C3P0、Druid、HikariCP等。
而在Spring Boot 2.x中,对数据源的选择也紧跟潮流,采用了目前性能最佳的HikariCP。
接下来,我们就来具体说说,这个Spring Boot中的默认数据源配置
默认数据源HikariCP
由于Spring Boot的自动化配置机制,大部分对于数据源的配置都可以通过配置参数的方式去改变。
只有一些特殊情况,比如:更换默认数据源,多数据源共存等情况才需要去修改覆盖初始化的Bean内容。本节我们主要讲Hikari的配置。
在Spring Boot自动化配置中,对于数据源的配置可以分为两类:
1、通用配置:以 spring.datasource.* 的形式存在,主要是对一些即使使用不同数据源也都需要配置的一些常规内容。
比如:数据库链接地址、用户名、密码等。通常就这些配置:
spring.datasource.url=jdbc:mysql://**/app
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
2、数据源连接池配置:以 spring.datasource.<数据源名称>. 的形式存在。
比如:Hikari的配置参数就是 spring.datasource.hikari.* 形式。下面这个是我们最常用的几个配置项及对应说明:
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=500000
spring.datasource.hikari.max-lifetime=540000
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.connection-test-query=SELECT 1
这些配置的含义:
spring.datasource.hikari.minimum-idle: 最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
spring.datasource.hikari.maximum-pool-size: 最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值
spring.datasource.hikari.idle-timeout: 空闲连接超时时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。
spring.datasource.hikari.max-lifetime: 连接最大存活时间,不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
spring.datasource.hikari.connection-timeout: 连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒
spring.datasource.hikari.connection-test-query: 用于测试连接是否可用的查询语句。
Druid
Druid是数据库连接池,它能够提供强大的监控和扩展功能。
整合方式:
导入Druid依赖,pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
配置application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/xiaomissm
username: root
password: 123
driver-class-name: com.mysql.cj.jdbc.Driver
编写配置类,设置数据源为Druid
@Configuration
public class mydatasourceConfig {
/**
* 如果没有配置数据源,也就是数据连接池,会有默认的自动配置的数据源
* 默认的自动配置是判断容器中没有 DataSource类型的组件才会配,用@ConditionalOnMissingBean({DataSource.class})判断
* 现在定义为Druid数据源,默认的则不生效
* @return
*/
@ConfigurationProperties("spring.datasource") //复用配置文件的数据源配置
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
// druidDataSource.setUrl();
// druidDataSource.setUsername();
// druidDataSource.setPassword();
return druidDataSource;
}
}
其它配置:配置监控页面、防火墙、web应用等
@Configuration
public class mydatasourceConfig {
/**
* 如果没有配置数据源,也就是数据连接池,会有默认的自动配置的数据源
* 默认的自动配置是判断容器中没有 DataSource类型的组件才会配,用@ConditionalOnMissingBean({DataSource.class})判断
* 现在定义为Druid数据源,默认的则不生效
* @return
*/
@ConfigurationProperties("spring.datasource") //复用配置文件的数据源配置
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
// "stat"打开Druid的监控统计功能,"stat,wall"加入监控和防火墙功能功能
druidDataSource.setFilters("stat,wall");
return druidDataSource;
}
// 配置druid的监控页功能
@Bean
public ServletRegistrationBean statViewServlet(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
//监控页账号密码:
registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","123456");
return registrationBean;
}
/**
* WebStatFilter 用于采集web-jdbc关联监控的数据。
*/
@Bean
public FilterRegistrationBean webStatFilter(){
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}