HikariPool-1 – dataSource or dataSourceClassName or jdbcUrl is required.解决方案[通俗易懂]

HikariPool-1 – dataSource or dataSourceClassName or jdbcUrl is required.解决方案[通俗易懂]HikariPool-1-dataSourceordataSourceClassNameorjdbcUrlisrequired.解决方案

大家好,又见面了,我是你们的朋友全栈君。

故障错误

最近在使用Spring Boot 2.x with H2 Database 以及JPA整合一个项目的时候出现了下面这一个故障:

ERROR 21448 --- [ main] com.zaxxer.hikari.HikariConfig : HikariPool-1 - dataSource or dataSourceClassName or jdbcUrl is required.
....
Caused by: java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
at com.zaxxer.hikari.HikariConfig.validate(HikariConfig.java:955) ~[HikariCP-3.2.0.jar:na]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:109) ~[HikariCP-3.2.0.jar:na]
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:43) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.jdbcStatement(GenerationTargetToDatabase.java:77) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:53) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.SchemaDropperImpl.applySqlString(SchemaDropperImpl.java:375) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.SchemaDropperImpl.applySqlStrings(SchemaDropperImpl.java:359) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.SchemaDropperImpl.dropFromMetadata(SchemaDropperImpl.java:241) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.SchemaDropperImpl.performDrop(SchemaDropperImpl.java:154) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:126) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.internal.SchemaDropperImpl.doDrop(SchemaDropperImpl.java:112) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:144) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939) ~[hibernate-core-5.3.12.Final.jar:5.3.12.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58) ~[spring-orm-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.1.10.RELEASE.jar:5.1.10.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:391) ~[spring-orm-5.1.10.RELEASE.jar:5.1.10.RELEASE]
... 20 common frames omitted

故障分析

废话不多说,我们来一起分析下故障。

根据上面的错误日志关键信息可以得知:

HikariPool-1 - dataSource or dataSourceClassName or jdbcUrl is required.

这里提示 dataSource or dataSourceClassName or jdbcUrl 是必须配置的,但是我反复检查了好几遍,我的配置文件中是已经配置了的,而且我发誓没有使用多个数据源,就是之前引入过Druid 依赖,后来又删掉了。

# 配置 Spring Data JPA
# 配置使用数据库类型
spring.jpa.database=h2
# 创建表的方式
# 方式一:通过表注解映射方式
# 自动建表规则
# create:Create the schema and destroy previous data
# create-drop:Create and then destroy the schema at the end of the session.
# update:Update the schema if necessary.
# none:Disable DDL handling
spring.jpa.hibernate.ddl-auto=create
# 自动建表和列映射规则
# 第一种规则:org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl:会把nickName映射为nickName
# 第二种规则:org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy:会把nickName映射为nick_name
# 第三种自定义规则:com.xingyun.customize.UpperTableColumnStrategy:会把nickName映射为NIKE_NAME
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
# 方式二:通过脚本初始化建立数据库表
# 初始化数据库表
#spring.datasource.schema=classpath*:/script/test-schema.sql
# 初始化数据库数据
#spring.datasource.data=classpath*:/script/test_data.sql
# 是否显示SQL
spring.jpa.show-sql=true
# 是否显示Web 控制台
spring.jpa.open-in-view=true
# 配置Datasource
# 配置存储数据到内存
spring.datasource.url=jdbc:h2:mem:test_h2_db
##配置存储数据到文件
#spring.datasource.url=jdbc:h2:file:~/test_h2_db
### 配置数据库连接账号
spring.datasource.username=sa
### 配置数据库连接密码
spring.datasource.password=sa
### 配置使用数据库驱动
spring.datasource.driver-class-name=org.h2.Driver
### 配置数据源初始化类型 embedded|always|never
### 注意:spring.datasource.initialize=true已经过时,使用spring.datasource.initialization-mode替代
spring.datasource.initialization-mode=embedded
## 配置数据源类型
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# 配置H2 Database
# H2 web管理控制台需要devtools,如果没有添加该依赖仍然想要使用web 控制台,那么需要配置如下属性为true
spring.h2.console.enabled=true
# 配置H2 web 管理控制台的上下文
spring.h2.console.path=/h2-console
#进行该配置后,h2 web console就可以在远程访问了。否则只能在本机访问。
spring.h2.console.settings.web-allow-others=false

关于这个故障,网上好多都是说

  • spring.datasource.url 修改为spring.datasource.jdbc-url
  • spring.datasource.driverClassName修改为spring.datasource.driver-class-name

但是实际测试发现,效果貌似并不奏效。

那就自己找吧,我们根据上面的错误提示可以知道,错误发生在HikariDataSource 类和HikariConfig 类中。

HikariDataSource.java

public class HikariDataSource extends HikariConfig implements DataSource{ 

... 
public HikariDataSource(HikariConfig configuration) { 

configuration.validate();
... 
} 
}

HikaruiDatasource.java中有一个构造方法,构造方法中调用了validate方法,而错误就发生在这个验证方法中。

HikariConfig.java

public void validate() { 

if (this.poolName == null) { 

this.poolName = this.generatePoolName();
} else if (this.isRegisterMbeans && this.poolName.contains(":")) { 

throw new IllegalArgumentException("poolName cannot contain ':' when used with JMX");
}
this.catalog = UtilityElf.getNullIfEmpty(this.catalog);
this.connectionInitSql = UtilityElf.getNullIfEmpty(this.connectionInitSql);
this.connectionTestQuery = UtilityElf.getNullIfEmpty(this.connectionTestQuery);
this.transactionIsolationName = UtilityElf.getNullIfEmpty(this.transactionIsolationName);
this.dataSourceClassName = UtilityElf.getNullIfEmpty(this.dataSourceClassName);
this.dataSourceJndiName = UtilityElf.getNullIfEmpty(this.dataSourceJndiName);
this.driverClassName = UtilityElf.getNullIfEmpty(this.driverClassName);
this.jdbcUrl = UtilityElf.getNullIfEmpty(this.jdbcUrl);
if (this.dataSource != null) { 

if (this.dataSourceClassName != null) { 

LOGGER.warn("{} - using dataSource and ignoring dataSourceClassName.", this.poolName);
}
} else if (this.dataSourceClassName != null) { 

if (this.driverClassName != null) { 

LOGGER.error("{} - cannot use driverClassName and dataSourceClassName together.", this.poolName);
throw new IllegalStateException("cannot use driverClassName and dataSourceClassName together.");
}
if (this.jdbcUrl != null) { 

LOGGER.warn("{} - using dataSourceClassName and ignoring jdbcUrl.", this.poolName);
}
} else if (this.jdbcUrl == null && this.dataSourceJndiName == null) { 

if (this.driverClassName != null) { 

LOGGER.error("{} - jdbcUrl is required with driverClassName.", this.poolName);
throw new IllegalArgumentException("jdbcUrl is required with driverClassName.");
}
LOGGER.error("{} - dataSource or dataSourceClassName or jdbcUrl is required.", this.poolName);
throw new IllegalArgumentException("dataSource or dataSourceClassName or jdbcUrl is required.");
}
this.validateNumerics();
if (LOGGER.isDebugEnabled() || unitTest) { 

this.logConfiguration();
}
}

根据代码来看,当 jdbcUrl 或者dataSourceJndiName 变量为空,那么就会抛出这个错误。

于是我尝试在代码里添加

spring.datasource.hikari.jdbc-url=jdbc:h2:mem:test_h2_db

但是还是不得行。。。

因此我怀疑是Spring Boot 的自动配置不知道什么原因失效了。

Spring Boot 项目拥有智能的自动配置功能,当检测到有H2 相关数据库连接的jar 包就会进行自动配置。

所谓的自动配置根据我的理解至少需要有两个操作:

  • 读取application.properties 配置文件中属性
  • 然后设置到实例对象中

然后通过查资料,在org.springframework.boot.jdbc 包下找到了DataSourceBuilder.java 这个类。

这个类很关键,令我茅塞顿开,明白了spring.datasource.urlspring.datasource.jdbc-url 之间的关系。

核心如下:

 private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { 
"com.zaxxer.hikari.HikariDataSource",
"org.apache.tomcat.jdbc.pool.DataSource",
"org.apache.commons.dbcp2.BasicDataSource"};
aliases.addAliases("url", new String[]{ 
"jdbc-url"});
aliases.addAliases("username", new String[]{ 
"user"});
  • 上面可以看到,这个类默认数组中有三种数据源连接池,HikariDataSource 就是其中一种。
  • 其次,url设置了别名映射,因此spring.datasource.url 就等价于 spring.datasource.jdbc-url

完整代码如下:

import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.util.ClassUtils;
public final class DataSourceBuilder<T extends DataSource> { 

private static final String[] DATA_SOURCE_TYPE_NAMES = new String[]{ 
"com.zaxxer.hikari.HikariDataSource", "org.apache.tomcat.jdbc.pool.DataSource", "org.apache.commons.dbcp2.BasicDataSource"};
private Class<? extends DataSource> type;
private ClassLoader classLoader;
private Map<String, String> properties = new HashMap();
public static DataSourceBuilder<?> create() { 

return new DataSourceBuilder((ClassLoader)null);
}
public static DataSourceBuilder<?> create(ClassLoader classLoader) { 

return new DataSourceBuilder(classLoader);
}
private DataSourceBuilder(ClassLoader classLoader) { 

this.classLoader = classLoader;
}
public T build() { 

Class<? extends DataSource> type = this.getType();
DataSource result = (DataSource)BeanUtils.instantiateClass(type);
this.maybeGetDriverClassName();
this.bind(result);
return result;
}
private void maybeGetDriverClassName() { 

if (!this.properties.containsKey("driverClassName") && this.properties.containsKey("url")) { 

String url = (String)this.properties.get("url");
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
this.properties.put("driverClassName", driverClass);
}
}
private void bind(DataSource result) { 

ConfigurationPropertySource source = new MapConfigurationPropertySource(this.properties);
ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
aliases.addAliases("url", new String[]{ 
"jdbc-url"});
aliases.addAliases("username", new String[]{ 
"user"});
Binder binder = new Binder(new ConfigurationPropertySource[]{ 
source.withAliases(aliases)});
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}
public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) { 

this.type = type;
return this;
}
public DataSourceBuilder<T> url(String url) { 

this.properties.put("url", url);
return this;
}
public DataSourceBuilder<T> driverClassName(String driverClassName) { 

this.properties.put("driverClassName", driverClassName);
return this;
}
public DataSourceBuilder<T> username(String username) { 

this.properties.put("username", username);
return this;
}
public DataSourceBuilder<T> password(String password) { 

this.properties.put("password", password);
return this;
}
public static Class<? extends DataSource> findType(ClassLoader classLoader) { 

String[] var1 = DATA_SOURCE_TYPE_NAMES;
int var2 = var1.length;
int var3 = 0;
while(var3 < var2) { 

String name = var1[var3];
try { 

return ClassUtils.forName(name, classLoader);
} catch (Exception var6) { 

++var3;
}
}
return null;
}
private Class<? extends DataSource> getType() { 

Class<? extends DataSource> type = this.type != null ? this.type : findType(this.classLoader);
if (type != null) { 

return type;
} else { 

throw new IllegalStateException("No supported DataSource type found");
}
}
}

解决方案一

  • 配置一个数据源
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import javax.sql.DataSource;
/** * @author xing yun * @功能 */
@Configuration
public class MyDataSourceConfig { 

@Autowired
DataSourceProperties dataSourceProperties;
@Primary
@Bean
public DataSource dataSource(){ 

HikariConfig hikariConfig=new HikariConfig();
hikariConfig.setJdbcUrl(dataSourceProperties.getUrl());
hikariConfig.setUsername(dataSourceProperties.getUsername());
hikariConfig.setPassword(dataSourceProperties.getPassword());
hikariConfig.setDriverClassName(dataSourceProperties.getDriverClassName());
HikariDataSource hikariDataSource=new HikariDataSource(hikariConfig);
return hikariDataSource;
}
}

值得注意的我们通过操作DataSourceProperties 这个类就可以获取applicaion-dev.properties 里面如下变量的配置值。

  • spring.datasource.url
  • spring.datasource.username
  • spring.datasource.passowrd
  • spring.datasource.drivcer-class-name

解决方案二

当然网上还有一种写法同样可以获取application.properties 里面的值。

感谢前辈的博文 springboot 2 Hikari 多数据源配置问题(dataSourceClassName or jdbcUrl is required)

代码做了精简后如下:

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import javax.sql.DataSource;
/** * @author xing yun * @功能 */
@Configuration
public class MyDataSourceConfig { 

@ConfigurationProperties(prefix = "spring.datasource")
@Primary
@Bean
public DataSource dataSource(DataSourceProperties properties){ 

return DataSourceBuilder.create(properties.getClassLoader())
.type(HikariDataSource.class)
.driverClassName(properties.determineDriverClassName())
.url(properties.determineUrl())
.username(properties.determineUsername())
.password(properties.determinePassword())
.build();
}
}

这种写法可用于配置多种数据源。

  • @Primary :自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
  • 如果配置的是 @ConfigurationProperties(prefix = "spring.datasource")那么配置文件中就是
    • spring.datasource.url
    • spring.datasource.username
    • spring.datasource.password
  • 如果这里配置成 @ConfigurationProperties(prefix = "spring.datasource.one")那么配置文件中就是
    • spring.datasource.one.url
    • spring.datasource.one.username
    • spring.datasource.one.password

采取以上方案后虽然可以正常使用了,但是,健康检查还是通不过。 那就暂时注释掉它好了。

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

参考资料

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/152362.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号