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)


相关推荐

  • SynchronousQueue详解「建议收藏」

    SynchronousQueue详解「建议收藏」SynchronousQueue是BlockingQueue的一种,所以SynchronousQueue是线程安全的。SynchronousQueue和其他的BlockingQueue不同的是SynchronousQueue的capacity是0。即SynchronousQueue不存储任何元素。也就是说SynchronousQueue的每一次insert操作,必须等待其他线性的remove操作。而每一个remove操作也必须等待其他线程的insert操作。这种特性可以让我们想起了Exchanger

  • Error filterStart 错误解决「建议收藏」

    Error filterStart 错误解决「建议收藏」2019独角兽企业重金招聘Python工程师标准>>>…

  • oracle之表空间(tablespace)、方案(schema)、段(segment)、区(extent)、块(block)

    oracle之表空间(tablespace)、方案(schema)、段(segment)、区(extent)、块(block)

    2021年11月13日
  • 新浪微博爬虫分享(一天可抓取 1300 万条数据)「建议收藏」

    From:https://blog.csdn.net/bone_ace/article/details/50903178微博爬虫单机每日千万级的数据微博爬虫总结:https://blog.csdn.net/nghuyong/article/details/81251948Python爬虫——新浪微博(网页版):https://blog.csdn.net/qq_37267015/ar…

  • waf(web安全防火墙)主要功能点

    waf(web安全防火墙)主要功能点注入攻击SQL注入防护:阻止恶意SQL代码在网站服务器上执行。命令注入防护:阻止攻击者利用网站漏洞直接执行系统命令。XPATH注入防护:阻止攻击者构造恶意输入数据,形成XML文件实施注入。LDAP注入防护:阻止攻击者将网站输入的参数引入LDAP查询实施注入。SSI注入防护:阻止攻击者将SSI命令在服务端执行,主要发生在.shtml,.shtm,.stm文件。缓冲区溢出防护:阻止请求中填入超过缓冲区容量的数据,防止恶意代码被执行。HPP攻击防护:阻止攻击者利用HPP漏洞来发起注入…

  • 1024是程序员的什么节日(重阳节的时候干什么)

    1024程序员节1024程序员节是广大程序员的共同节日。1024是2的十次方,二进制计数的基本计量单位之一。针对程序员经常周末加班与工作日熬夜的情况,部分互联网机构倡议每年的10月24日为1024程序员节,在这一天建议程序员拒绝加班。程序员就像是一个个1024,以最低调、踏实、核心的功能模块搭建起这个科技世界。1G=1024M,而1G与1级谐音,也有一级棒的意思。1、节日背景程序员(英文Programmer)是从事前端、后端程序开发、系统运维、测试等的专业人员。一般将程序员分为程序设计人员和程序.

发表回复

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

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