spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
73.59k stars 40.31k forks source link

Add a failure analyzer for Hibernate bootstrap failure #1573

Open kryger opened 9 years ago

kryger commented 9 years ago

I'm trying to make my application's configuration as foolproof as possible by failing as soon as missing properties are detected. My app has two DataSources so I followed the Configure Two DataSources how-to. This works nicely, but gets confusing if the properties with given prefix aren't there (this would happen if the production .yml file was missing or didn't contain the expected DB credentials). In a JPA application this causes:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1568)
(...snip...)
        at demo.Application.main(Application.java:14)
Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
        at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:104)
        at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:71)
(...snip...)
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:842)
        at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:341)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1627)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1564)
        ... 14 more

I'd expect something more precise like "Couldn't initialise DataSource with prefix=datasource.secondary; check your config!".


I tried it with a JDBC starter too and it... quietly succeeds, initializing the DataSource with nulls - not sure if it's a different underlying issue.

Haven't done more investigation, may try to debug to narrow it down a bit if needed. Submitted repros for both problems to spring-boot-issues.

bozzwin commented 7 years ago

I did a review of this issue. I analyzed the case with jpa dependencies and the stack trace is thrown because you forgot to add one properties in configuration file, for instance:

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

After that I got another exception:

java.sql.SQLException: The url cannot be null
    at java.sql.DriverManager.getConnection(DriverManager.java:556) ~[na:1.7.0_79]
    at java.sql.DriverManager.getConnection(DriverManager.java:187) ~[na:1.7.0_79]
    at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:308) ~[tomcat-jdbc-8.5.4.jar:na]
    at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203) ~[tomcat-jdbc-8.5.4.jar:na]
    at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:716) [tomcat-jdbc-8.5.4.jar:na]
    at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:648) [tomcat-jdbc-8.5.4.jar:na]
    at org.apache.tomcat.jdbc.pool.ConnectionPool.init(ConnectionPool.java:468) [tomcat-jdbc-8.5.4.jar:na]

which means that user should add a few properties describing data source:

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdb
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root

I would like to improve that issue but I need full description of how it should behave.

rzymek commented 7 years ago

First of all - this issue is still valid after updating to version to 1.5.1.RELEASE. (This updates hibernate dependency to 5.0.11.Final)

I'd say the main issue here is how hibernate itself handles empty config. Ever since at least 4.0.1 up to the current 5.2.8, hibernate-core complains about not having a dialect when run with an empty config.

When I modify hibernate to use a default dialect on error, your spring-boot project's output becomes:

2017-02-26 21:54:39.203  WARN 27618 --- [           main] o.a.tomcat.jdbc.pool.PooledConnection    : Not loading a JDBC driver as driverClassName property is null.
2017-02-26 21:54:39.214 ERROR 27618 --- [           main] o.a.tomcat.jdbc.pool.ConnectionPool      : Unable to create initial connections of pool.

java.sql.SQLException: The url cannot be null
    at java.sql.DriverManager.getConnection(DriverManager.java:649) ~[na:1.8.0_92]
    at java.sql.DriverManager.getConnection(DriverManager.java:208) ~[na:1.8.0_92]
    at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PcooledConnection.java:308) ~[tomcat-jdbc-8.5.11.jar:na]
    at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203) ~[tomcat-jdbc-8.5.11.jar:na]

(just like when spring.jpa.database-platform is set)

I'd say this isn't really a spring issue, but a hibernate one. At least concerning the jpa case. I'll be submitting a pull request to hibernate-orm to make it behave better on empty config.

philwebb commented 6 years ago

Relates to #9219

ezraroi commented 5 years ago

Is this issue still relevant or it was closed with #9219?

snicoll commented 5 years ago

9219 was closed as a duplicate of this one. Yes, it is still relevant.

ezraroi commented 5 years ago

Great, will work on it. Should be fixed on the master?

snicoll commented 5 years ago

Yes, thank you.

amutsch commented 5 years ago

Hibernate does not require a dialect. If it can connect to the DB without issue it will determine the proper dialect. The issue comes when hibernate can not connect then its first assumption is it doesn't have a dialect for the DB. In hibernates current state it is not common for it to not be able to determine the dialect. I agree with @rzymek that this is more of a hibernate issue since invalid credentials can identify as missing dialect property.