spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.36k stars 40.72k forks source link

Provide auto-configuration for Liquibase with NoSQL databases #37936

Open fbiville opened 1 year ago

fbiville commented 1 year ago

Spring Boot version is 3.1.4.

Reproducer (ignore the README title, the initial scope of the reproducer is unrelated to this issue)

git clone git@github.com:fbiville/liquibase-neo4j-issue-479.git -b liquibase-only-props
docker run --rm \
    --env NEO4J_AUTH='neo4j/letmein!' \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --publish=7687:7687 \
    --health-cmd "cypher-shell -u neo4j -p 'letmein!' 'RETURN 1'" \
    --health-interval 5s \
    --health-timeout 5s \
    --health-retries 5 \
    neo4j:5-enterprise
mvn spring-boot:run --file liquibase-neo4j-issue-479/pom.xml

Description

I believed (maybe wrongly so) that:

would theoretically be enough to run Liquibase:

spring:
  liquibase:
    driver-class-name: liquibase.ext.neo4j.database.jdbc.Neo4jDriver
    change-log: classpath:changeLog.xml
    url: jdbc:neo4j:neo4j://example.com
    user: neo4j
    password: *********

That is however not the case in practice.

First, I have to add spring-jdbc to the classpath, as org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration.LiquibaseConfiguration requires it (via @ConditionalOnClass(ConnectionCallback.class)).

But when I do that, here is what I get:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class

The quick fix would be move most of the properties under the spring.datasource namespace.

However, this is a bad idea, at the very least in the case of the Neo4j extension.

Indeed, liquibase.ext.neo4j.database.jdbc.Neo4jDriver is a JDBC implementation specifically created for Liquibase purposes: only the required subset of all the JDBC APIs are implemented, and Liquibase only uses a single connection per execution - which eliminates a lot of complexity that would arise in generic JDBC implementations.

In other words, the extension does NOT provide a generic JDBC implementation .

Configuring a Datasource with spring.datasource could lead to that data source being used outside Liquibase, thus exercising the aforementioned implementation that is not designed for general use.

I believe some other Liquibase extensions provide their own limited JDBC implementation as well (at least Mongo iirc), so it is very likely they would suffer from the same issue.

Is there a way to make the above configuration sufficient for executing Liquibase? It looks like it should be.

wilkinsona commented 1 year ago

Spring Boot's support for Liquibase is intended for initializing an SQL database. Given this, it's to be expected that it may not work for initialization of a NoSQL database.

We can look at expanding the scope of our Liquibase integration to support Neo4j, Mongo, and the like, but that would be an enhancement in 3.3 or later rather than a bug fix in an existing version. In the meantime, to use Liquibase with Neo4j in a Spring Boot application you should define the SpringLiquibase bean yourself.

rursprung commented 3 days ago

note: this is also relevant for liquibase-opensearch. i'll now probably build a bare minimum spring integration there for the time being. i don't know if this can be handled purely on the Spring side or if SpringLiquibase (which is in liquibase-standard itself, not in spring boot) needs some modifications (to do it cleanly it most likely needs some as liquibase in general is tightly interwoven with java.sql. see also liquibase/liquibase#4236 for further details.