quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.73k stars 2.67k forks source link

Cannot use Liquibase extension with hibernate-reactive #14682

Open deathwaiting opened 3 years ago

deathwaiting commented 3 years ago

Describe the bug It seems it is not possible to use hibernate-reactive while providing JDBC properties, and those are required buy Liquibase extension, effectively, Liquibase cannot be used with hibernate reactive

Expected behavior Should be able to use JDBC properties alongside reactive properties.

Actual behavior using JDBC properties while using hibernate-reactive leads to build exception.

Caused by: java.lang.IllegalStateException: Booting an Hibernate Reactive serviceregistry on a non-reactive RecordedState!
    at io.quarkus.hibernate.reactive.runtime.boot.registry.PreconfiguredReactiveServiceRegistryBuilder.checkIsReactive(PreconfiguredReactiveServiceRegistryBuilder.java:76)
    at io.quarkus.hibernate.reactive.runtime.boot.registry.PreconfiguredReactiveServiceRegistryBuilder.<init>(PreconfiguredReactiveServiceRegistryBuilder.java:66)
    at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.rewireMetadataAndExtractServiceRegistry(FastBootHibernateReactivePersistenceProvider.java:177)
    at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.getEntityManagerFactoryBuilderOrNull(FastBootHibernateReactivePersistenceProvider.java:156)
    at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.createEntityManagerFactory(FastBootHibernateReactivePersistenceProvider.java:82)
    ... 86 more

Configuration

#need to limit memory taken by graalvm to create the native image , or it won't build
quarkus.native.native-image-xmx=4G

# Liquibase minimal config properties
quarkus.liquibase.migrate-at-start=true
quarkus.liquibase.default-schema-name=public
quarkus.liquibase.change-log=db/changeLog.xml

#Database
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=postgres
quarkus.datasource.password=postgres
#quarkus.datasource.jdbc=false
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/postgres

# Reactive config
quarkus.datasource.reactive.url=vertx-reactive:postgresql://localhost/postgres

#hibernate
quarkus.hibernate-orm.log.sql=true

Environment (please complete the following information):

Additional context

plugins {
    java
    id("io.quarkus")
}

repositories {
    mavenLocal()
    mavenCentral()
}

val quarkusPlatformGroupId: String by project
val quarkusPlatformArtifactId: String by project
val quarkusPlatformVersion: String by project

dependencies {
    implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
    implementation("io.quarkus:quarkus-resteasy-reactive-jackson")
    implementation("io.quarkus:quarkus-hibernate-reactive-panache")
    implementation("io.quarkus:quarkus-smallrye-openapi")
    implementation("io.quarkus:quarkus-smallrye-jwt")
    implementation("io.quarkus:quarkus-liquibase")
    implementation("io.quarkus:quarkus-mailer")
    implementation("io.quarkus:quarkus-reactive-pg-client")
    implementation("io.quarkus:quarkus-smallrye-metrics")
    implementation("io.quarkus:quarkus-arc")
    implementation("io.quarkus:quarkus-reactive-pg-client")
    testImplementation("io.quarkus:quarkus-jdbc-postgresql")
    testImplementation("io.quarkus:quarkus-junit5")
    testImplementation("io.rest-assured:rest-assured")
    testImplementation("org.testcontainers:postgresql:1.15.1")
}

group = "com.qu"
version = "1.0.0-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

tasks.withType<JavaCompile> {
    options.encoding = "UTF-8"
    options.compilerArgs.add("-parameters")
}
ghost commented 3 years ago

/cc @andrejpetras, @geoand, @gsmet

gsmet commented 3 years ago

Can you drop quarkus.hibernate-orm.log.sql=true from your config and see how it goes?

You can have a JDBC datasource, what you can't do at the moment is mixing Hibernate ORM classic and Hibernate Reactive.

There might be a bug somewhere though :).

BrainShit commented 3 years ago

I'm having the same issue with a similar configuration using maven and quarkus 1.12.0.Final

quarkus.datasource.username=user
quarkus.datasource.password=password
quarkus.liquibase.migrate-at-start=true
quarkus.datasource.db-kind=mariadb
quarkus.datasource.jdbc.url=jdbc:mysql://127.0.0.1:3306/mariadb
quarkus.datasource.reactive.url=mysql://127.0.0.1:3306/mariadb
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-resteasy-reactive-jsonb</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-jdbc-mariadb</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-reactive-mysql-client</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-hibernate-reactive-panache</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-quartz</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-smallrye-jwt</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-liquibase</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-smallrye-jwt-build</artifactId>
</dependency>

throwing this error message:

2021-02-28 22:04:04,986 INFO  [liq.changelog] (Quarkus Main Thread) Reading from jc_event.DATABASECHANGELOG
2021-02-28 22:04:05,033 INFO  [liq.lockservice] (Quarkus Main Thread) Successfully acquired change log lock
2021-02-28 22:04:05,165 INFO  [liq.changelog] (Quarkus Main Thread) Reading from jc_event.DATABASECHANGELOG
2021-02-28 22:04:05,194 INFO  [liq.lockservice] (Quarkus Main Thread) Successfully released change log lock
2021-02-28 22:04:05,261 INFO  [org.hib.rea.pro.imp.ReactiveIntegrator] (Quarkus Main Thread) HRX000001: Hibernate Reactive Preview
2021-02-28 21:57:11,724 ERROR [io.qua.run.Application] (Quarkus Main Thread) Failed to start application (with profile dev): java.lang.IllegalStateException: Booting an Hibernate Reactive serviceregistry on a non-reactive RecordedState!
    at io.quarkus.hibernate.reactive.runtime.boot.registry.PreconfiguredReactiveServiceRegistryBuilder.checkIsReactive(PreconfiguredReactiveServiceRegistryBuilder.java:76)
    at io.quarkus.hibernate.reactive.runtime.boot.registry.PreconfiguredReactiveServiceRegistryBuilder.<init>(PreconfiguredReactiveServiceRegistryBuilder.java:66)
    at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.rewireMetadataAndExtractServiceRegistry(FastBootHibernateReactivePersistenceProvider.java:177)
    at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.getEntityManagerFactoryBuilderOrNull(FastBootHibernateReactivePersistenceProvider.java:156)
    at io.quarkus.hibernate.reactive.runtime.FastBootHibernateReactivePersistenceProvider.createEntityManagerFactory(FastBootHibernateReactivePersistenceProvider.java:82)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:80)
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
    at io.quarkus.hibernate.orm.runtime.JPAConfig$LazyPersistenceUnit.get(JPAConfig.java:117)
    at io.quarkus.hibernate.orm.runtime.JPAConfig.startAll(JPAConfig.java:41)
    at io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder.startAllPersistenceUnits(HibernateOrmRecorder.java:88)
    at io.quarkus.deployment.steps.HibernateOrmProcessor$startPersistenceUnits951856026.deploy_0(HibernateOrmProcessor$startPersistenceUnits951856026.zig:74)
    at io.quarkus.deployment.steps.HibernateOrmProcessor$startPersistenceUnits951856026.deploy(HibernateOrmProcessor$startPersistenceUnits951856026.zig:40)
    at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:849)
    at io.quarkus.runtime.Application.start(Application.java:90)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:100)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
    at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at io.quarkus.runner.bootstrap.StartupActionImpl$3.run(StartupActionImpl.java:134)
    at java.base/java.lang.Thread.run(Thread.java:834)

my best guess would be that, since hibernate-reactive needs hibernate-orm that the classic orm creates it's connection first and only uses the jdbc one, as liquibase is running through just fine before

gsmet commented 3 years ago

@BrainShit could you create a small reproducer? That would be helpful.

BrainShit commented 3 years ago

https://github.com/BrainShit/liquibase-reactive-rep

Here's the reproducer, I stripped away a bunch of the extensions I've been using during my previous reply to thin that out a bit

famod commented 3 years ago

The root cause of this seems to be the same as in #10716

gmkumar2005 commented 3 years ago

Workaround I have used : Create a separate project eg : DB_Migration with blocking jdbc. Another option : Use liqibase docker image.

andrejpetras commented 3 years ago

I end up with my database migration solution which is using only the reactive drivers. https://github.com/lorislab/quarkus-barn I did look on the Flyway and Liquibase implementation, but they are strongly linked to the JDBC driver and I want to avoid having unnecessary dependencies in my projects.

KyriacosP commented 3 years ago

Do we have any updates? We are facing a similar issue.

We are getting an unsatisfied dependency error for org.hibernate.reactive.mutiny.Mutiny.Session

[1] Unsatisfied dependency for type org.hibernate.reactive.mutiny.Mutiny$Session and qualifiers [@Default]

KyriacosP commented 3 years ago

Current workaround is based on the workaround from @gwenneg on this issue #10716:


        @ConfigProperty(name = "custom.liquibase.migrate")
    boolean runMigration;
    @ConfigProperty(name = "quarkus.datasource.jdbc.url")
    String datasourceUrl;
    @ConfigProperty(name = "quarkus.datasource.username")
    String datasourceUsername;
    @ConfigProperty(name = "quarkus.datasource.password")
    String datasourcePassword;
        @ConfigProperty(name = "quarkus.liquibase.change-log")
    String changeLogLocation;

    public void runLiquibaseMigration(@Observes StartupEvent event) throws LiquibaseException {
        if(runMigration)
        {
            Liquibase liquibase = null;
            try {
                ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(Thread.currentThread().getContextClassLoader());
                DatabaseConnection conn = DatabaseFactory.getInstance().openConnection(datasourceUrl, datasourceUsername, datasourcePassword, null, resourceAccessor);
                liquibase = new Liquibase(changeLogLocation, resourceAccessor, conn);
                liquibase.update(new Contexts(), new LabelExpression());
            } catch (Exception e) {
                logger.error("Liquibase Migration Exception: " + ExceptionUtil.generateStackTrace(e));
            }
            finally {
                if(liquibase!=null)
                {
                    liquibase.close();
                }
            }
        }
    }

application.properties:


quarkus.datasource.db-kind=postgresql
quarkus.datasource.username={username}
quarkus.datasource.password={password}
quarkus.datasource.jdbc=false
quarkus.datasource.jdbc.url=jdbc:postgresql://{db-url}
quarkus.datasource.reactive.url=vertx-reactive:postgresql://{db-url}
quarkus.hibernate-orm.log.sql=true
quarkus.liquibase.change-log=db/changeLog.xml
custom.liquibase.migrate=true

pom.xml dependencies tag:

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-hibernate-reactive</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-reactive</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-reactive-pg-client</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-hibernate-validator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-liquibase</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-postgresql</artifactId>
    </dependency>
EnvyIT commented 2 years ago

Any progress on this issue? Feels pretty odd coming from Spring and experiencing such issues from the very beginning 😢

MuizMahdi commented 2 years ago

Any progress on this issue? Feels pretty odd coming from Spring and experiencing such issues from the very beginning cry

Same, I wasn't having this issue with Spring

geoand commented 2 years ago

To be completely fair, there is no Hibernate Reactive in Spring...

IIRC, @Sanne has mentioned elsewhere that this issue will be addressed, but likely after Hibernate 6 is out and integrated into Quarkus.

famod commented 2 years ago

IIRC, @Sanne has mentioned elsewhere that this issue will be addressed, but likely after Hibernate 6 is out and integrated into Quarkus.

https://github.com/quarkusio/quarkus/issues/10716#issuecomment-1022118053

MuizMahdi commented 2 years ago

@geoand Thanks for the response, by the way it is possible to integrate Hibernate Reactive with Spring Webflux

geoand commented 2 years ago

Sure, booting Hibernate Reactive on your own is of course possible.

But it's not integrated into Spring in any way, nor is there any integration with Flyway or Liquibase that could be used to claim that the same feature exists in Spring and not Quarkus.

My whole point is that Quarkus is at the bleeding edge of innovation and this highly requested feature will definitely land at some point, it's just a matter of priorities

MuizMahdi commented 2 years ago

Totally understandable 💯, thanks for the effort!

BlackSharkCZE commented 2 years ago

4 months later and this issue is still relevant. It is not possible using reactive approach in quarkus with Liquibase. Do you want use liquibase and reactive approche? Stay with Spring.

gwenneg commented 2 years ago

@BlackSharkCZE Several workarounds have been suggested or linked in this discussion. These workarounds make it possible to use Liquibase alongside with Hibernate Reactive in most projects.

Do you have a blocking problem that can't be solved with one of these? If so, don't hesitate to give us more details. The community might be able to help you fix it.

jostey commented 2 years ago

@BlackSharkCZE Several workarounds have been suggested or linked in this discussion. These workarounds make it possible to use Liquibase alongside with Hibernate Reactive in most projects.

Do you have a blocking problem that can't be solved with one of these? If so, don't hesitate to give us more details. The community might be able to help you fix it.

Hi, I configured liquibase with no reactive drivers of mysql but I think that could be very useful this properties: quarkus.liquibase.migrate-at-start quarkus.liquibase.validate-on-migrate quarkus.liquibase.clean-at-start

Thanks

stephan-strate commented 2 years ago

@BlackSharkCZE Several workarounds have been suggested or linked in this discussion. These workarounds make it possible to use Liquibase alongside with Hibernate Reactive in most projects.

Do you have a blocking problem that can't be solved with one of these? If so, don't hesitate to give us more details. The community might be able to help you fix it.

Are there any plans to introduce liquibase and flyway as reactive variant? I know the problems can be solved using a workaround, but reactive being the default now, a workaround feels a bit meh.. It would be so cool to get rid of the jdbc dependency, the manual configuration and the warning in the console 2022-07-20 10:03:52,811 WARN [io.quarkus.agroal.deployment.AgroalProcessor] (build-33) The Agroal dependency is present but no JDBC datasources have been defined..

gavinking commented 2 years ago

I don't understand this issue. It seems to be saying that:

But, on the other hand, it also seems to me like it's something completely reasonable and unsurprising...

...so, is there a strong reason I'm missing that we can't just close this issue?

gavinking commented 2 years ago

Are there any plans to introduce liquibase and flyway as reactive variant?

I would be very surprised if there were any such plans, since reactive doesn't seem to offer any advantages for schema management. But you would have to direct this question to the maintainers of these projects to be sure.

stephan-strate commented 2 years ago

I don't understand this issue. It seems to be saying that:

  • Liquibase is based around JDBC and so requires a JDBC config, but
  • HR does not use JDBC, and so requires a slightly different config, and
  • this is slightly uncomfortable.

But, on the other hand, it also seems to me like it's something completely reasonable and unsurprising...

...so, is there a strong reason I'm missing that we can't just close this issue?

I mean yeah, this is not ground breaking, but you will have to dig through GitHub issues, find this one, copy&paste the workaround from one of the answers and tweak the configuration. This is not what I would expect from Quarkus. Also, if there are no plans ever to have a reactive variant for liquibase and flyway. Maybe it could be made easier to configure.

Ideas:

gsmet commented 2 years ago

There's definitely something broken and I think the main culprit is that we should split Hibernate ORM/Hibernate Reactive differently and have:

This way, you could have a JDBC driver around without automatically triggering Hibernate ORM. That's the main problem you all have here.

We also might need separate config roots or a way to define if a PU is reactive or not.

But given @Sanne is currently working on integrating ORM 6 and that it will probably be a big change, this work would need to be done after ORM 6 has landed.

Sanne commented 2 years ago

Agreed. Sorry people, will have to be patient as the switch to Jakarta takes priority and some "projects" are better not run in parallel.

In the meantime I would suggest embrace strict separations of concerns: don't include Flyway in the same application as Hibernate Reactive.

I do appreciate that such a solution is not always very practical, but I do believe it's the better design as you wouldn't ship Flyway in production and won't need 100s of copies of flyway to run when scaling production up/down. This way you can benefit from both Flyway and Hibernate Reactive today, and using the robust and well tested existing processes.

gavinking commented 2 years ago

@gsmet oh I see, then it is simply that I didn't understand the issue. Thanks.

KyriacosP commented 2 years ago

Maybe a warning on the Liquibase and Flyway guides that they do not work with Hibernate Reactive by default will be a good idea.

lotogus commented 2 years ago

Hi, this is my solution...

build.gradle.kts

    //for application runtime   
    implementation("io.quarkus:quarkus-hibernate-reactive-panache")
    implementation("io.quarkus:quarkus-reactive-mysql-client")

    //only for liquibase migration
    implementation("io.quarkus:quarkus-jdbc-mysql")
    implementation("io.quarkus:quarkus-liquibase")

application.properties (I used default dev configs: https://quarkus.io/guides/databases-dev-services)

quarkus.devservices.enabled=true
quarkus.datasource.devservices.enabled=true

quarkus.datasource.db-kind=mysql
quarkus.datasource.jdbc=false

quarkus.hibernate-orm.database.generation=none

quarkus.liquibase.custom-migrate=true
quarkus.liquibase.migrate=false
quarkus.liquibase.change-log=db/changeLog.xml

a LiquibaseSetup.kt class

@ApplicationScoped
class LiquibaseSetup {

    @ConfigProperty(name = "quarkus.liquibase.custom-migrate")
    var runMigration = false

    @ConfigProperty(name = "quarkus.datasource.reactive.url")
    lateinit var datasourceUrl: String

    @ConfigProperty(name = "quarkus.datasource.username")
    lateinit var datasourceUsername: String

    @ConfigProperty(name = "quarkus.datasource.password")
    lateinit var datasourcePassword: String

    @ConfigProperty(name = "quarkus.liquibase.change-log")
    lateinit var changeLogLocation: String

    fun runLiquibaseMigration(@Observes event: StartupEvent) {
        if (runMigration) {
            var liquibase: Liquibase? = null
            try {
                Log.info("liquibase setup to $datasourceUrl")
                val resourceAccessor: ResourceAccessor = ClassLoaderResourceAccessor(Thread.currentThread().contextClassLoader)
                val conn: DatabaseConnection = DatabaseFactory.getInstance().openConnection(
                        datasourceUrl.replace("vertx-reactive", "jdbc"),
                        datasourceUsername,
                        datasourcePassword,
                        null,
                        resourceAccessor
                    )
                liquibase = Liquibase(changeLogLocation, resourceAccessor, conn)
                liquibase.update(Contexts(), LabelExpression())
            } catch (e: Exception) {
                Log.error("Liquibase Migration Exception: ", e)
            } finally {
                liquibase?.close()
            }
        }
    }
}
spieps commented 2 years ago

Hi, just wanted to confirm this is still an issue. Here is my solution for Reactive Panache + Liquibase. This will allow for databasechangelog and databasechangeloglock tables to be created in a separate schema as well.

@ApplicationScoped
public class LiquibaseSetup {

    private static final Logger LOGGER = LoggerFactory.getLogger(LiquibaseSetup.class);

    @ConfigProperty(name = "custom.liquibase.migrate")
    boolean runMigration;

    @ConfigProperty(name = "quarkus.datasource.jdbc.url")
    String datasourceUrl;

    @ConfigProperty(name = "quarkus.datasource.username")
    String datasourceUsername;

    @ConfigProperty(name = "quarkus.datasource.password")
    String datasourcePassword;

    @ConfigProperty(name = "quarkus.liquibase.change-log")
    String changeLogLocation;

    @ConfigProperty(name = "quarkus.liquibase.database-change-log-lock-table-name")
    String changeLogLockTableName;

    @ConfigProperty(name = "quarkus.liquibase.database-change-log-table-name")
    String changeLogTableName;

    @ConfigProperty(name = "quarkus.liquibase.default-schema-name")
    String defaultSchemaName;

    @ConfigProperty(name = "quarkus.liquibase.liquibase-schema-name")
    String liquibaseSchemaName;

    public void runLiquibaseMigration(@Observes StartupEvent event) throws LiquibaseException {
        if (runMigration) {
            Liquibase liquibase = null;
            try {
                ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(Thread.currentThread().getContextClassLoader());
                DatabaseConnection connection = DatabaseFactory.getInstance()
                        .openConnection(datasourceUrl, datasourceUsername, datasourcePassword, null, resourceAccessor);
                Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);

                database.setDatabaseChangeLogLockTableName(changeLogLockTableName);
                database.setDatabaseChangeLogTableName(changeLogTableName);
                database.setDefaultSchemaName(defaultSchemaName);
                database.setLiquibaseSchemaName(liquibaseSchemaName);

                liquibase = new Liquibase(changeLogLocation, resourceAccessor, database);
                liquibase.validate();
                liquibase.update(new Contexts(), new LabelExpression());
            } catch (Exception ex) {
                LOGGER.error("Liquibase Migration Exception Stack Trace: {}", ExceptionUtil.generateStackTrace(ex));
            } finally {
                if (liquibase != null) {
                    liquibase.close();
                }
            }
        }
    }
}

application.properties:

custom.liquibase.migrate=true
quarkus.liquibase.change-log=db/changeLog.xml
quarkus.liquibase.liquibase-schema-name=liquibase_custom
quarkus.liquibase.database-change-log-lock-table-name=databasechangeloglock
quarkus.liquibase.database-change-log-table-name=databasechangelog
quarkus.liquibase.default-schema-name=custom
quarkus.datasource.jdbc=false
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=user
quarkus.datasource.password=pass
quarkus.datasource.jdbc.url=jdbc:postgresql://{db-url}
quarkus.datasource.reactive.url=vertx-reactive:postgresql://{db-url}

pom.xml:

<dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-hibernate-reactive-panache</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-reactive-pg-client</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-liquibase</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-jdbc-postgresql</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-reactive</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
    </dependency>
   <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
   </dependency>
cthiebault commented 2 years ago

@spieps were you able to use your solution during tests with a Postgres container (created by Quarkus dev service)?
I can't make it work because of dynamic quarkus.datasource.jdbc.url :-(

arunk-r commented 2 years ago

disabling default jdbc will help the system to focus on reactive configuration and run liquibase changeset on system startup.

dependency data implementation("io.quarkus:quarkus-hibernate-reactive-panache") implementation("io.quarkus:quarkus-liquibase") implementation("io.quarkus:quarkus-reactive-pg-client") implementation("io.quarkus:quarkus-jdbc-postgresql")

configuration yml quarkus.liquibase.migrate-at-start=false quarkus.liquibase.change-log: db/changelog/master.xml quarkus.datasource.db-kind=postgresql quarkus.datasource.username=${DATASOURCE_USERNAME:user} quarkus.datasource.password=${DATASOURCE_PASSWORD:pwd} quarkus.datasource.jdbc= false quarkus.datasource.url=jdbc:postgresql://${DATASOURCE_HOST:localhost}:${DATASOURCE_PORT:5432}/${DATASOURCE_SCHEMA:database_schema} quarkus.datasource.validation-query-sql=SELECT 1 quarkus.datasource.reactive.url=vertx-reactive:postgresql://${DATASOURCE_HOST:localhost}:${DATASOURCE_PORT:5432}/${DATASOURCE_SCHEMA:database_schema}

Kotlin code

fun onStart(@Observes ev: StartupEvent) { logger.info("The application is starting with profile {}...", ProfileManager.getActiveProfile()) liquibaseUpgrade() }

private fun liquibaseUpgrade() { var liquibase: Liquibase? = null try { val resourceAccessor = ClassLoaderResourceAccessor(Thread.currentThread().contextClassLoader) val conn = DatabaseFactory.getInstance().openConnection(datasourceUrl, username, password, null, resourceAccessor) liquibase = Liquibase(changeLogLocation, resourceAccessor, conn) liquibase.update(Contexts(), LabelExpression()) } catch (e: Exception) { logger.error("Liquibase Migration Exception: " + e.message) } finally { liquibase?.close() } }

kavishkamk commented 1 year ago

Hi all. I am try to integrate a application with OpenTelemetry. For that I also use jdbc driver. Can some one tell me how can I configure with this jdbc.driver. When I change the quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/telemetry to quarkus.datasource.jdbc.url=jdbc:otel:postgresql://localhost:5432/telemetry, quarkus.datasource.jdbc.driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver it throw following error.

2023-02-17 13:20:29,593 INFO  [....inf.dat.LiquibaseRunner] [runMigration] (Quarkus Main Thread) Migrating DB jdbc:otel:postgresql://localhost:5432/sampledb
2023-02-17 13:20:29,595 ERROR [...inf.dat.LiquibaseRunner] [runMigration] (Quarkus Main Thread) Liquibase Migration Exception: liquibase.exception.DatabaseException: java.lang.RuntimeException: Driver class was not specified and could not be determined from the url (jdbc:otel:postgresql://localhost:5432/sampledb)
    at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:217)
    at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:176)
    at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:159)
    at ....infrastructure.data.LiquibaseRunner.runMigration(LiquibaseRunner.java:49)
    at ....infrastructure.data.LiquibaseRunner_Subclass.runMigration$$superforward1(Unknown Source)
    at ....infrastructure.data.LiquibaseRunner_Subclass$$function$$2.apply(Unknown Source)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:53)
    at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
    at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
    at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
    at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:40)
    at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
    at .....infrastructure.data.LiquibaseRunner_Subclass.runMigration(Unknown Source)
    at ....infrastructure.data.LiquibaseRunner.onApplicationStart(LiquibaseRunner.java:39)
    at .....infrastructure.data.LiquibaseRunner_Subclass.onApplicationStart$$superforward1(Unknown Source)
    at .....infrastructure.data.LiquibaseRunner_Subclass$$function$$1.apply(Unknown Source)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:53)
    at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
    at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
    at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
    at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
    at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:40)
    at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
    at ....infrastructure.data.LiquibaseRunner_Subclass.onApplicationStart(Unknown Source)
    at ..infrastructure....data.LiquibaseRunner_Observer_onApplicationStart_3e5ff3fa5ec96071cea0c7faf2b64ce71ea7016d.notify(Unknown Source)
    at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:323)
    at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:305)
    at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:73)
    at io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:130)
    at io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:99)
    at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(Unknown Source)
    at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(Unknown Source)
    at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
    at io.quarkus.runtime.Application.start(Application.java:101)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:108)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:67)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:41)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:120)
    at io.quarkus.runner.GeneratedMain.main(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:103)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.RuntimeException: Driver class was not specified and could not be determined from the url (jdbc:otel:postgresql://localhost:5432/sampledb)
    at liquibase.database.DatabaseFactory.findDriverClass(DatabaseFactory.java:262)
    at liquibase.database.DatabaseFactory.openConnection(DatabaseFactory.java:200)

Application.properties

## without distributed tracing with OpenTelemetry
#quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/telemetry

## with distributed tracing with OpenTelemetry
quarkus.datasource.jdbc.url=jdbc:otel:postgresql://localhost:5432/telemetry
quarkus.datasource.jdbc.driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver

quarkus.datasource.reactive.url=vertx-reactive:postgresql://localhost/telemetry
quarkus.datasource.db-kind=postgresql
#quarkus.datasource.jdbc=false

quarkus.datasource.password=dfdfdf
quarkus.datasource.username=dfdfdfd
quarkus.liquibase.change-log=db/changeLog.xml
quarkus.liquibase.migrate-at-start=true
quarkus.liquibase.default-schema-name=public

Liquirebase Runner class

package ai.govi.telemetry.infrastructure.data;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;

import io.quarkus.runtime.LaunchMode;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import io.quarkus.runtime.StartupEvent;
import io.quarkus.runtime.util.ExceptionUtil;
import liquibase.Contexts;
import liquibase.LabelExpression;
import liquibase.Liquibase;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.exception.LiquibaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import io.quarkus.logging.Log;

@ApplicationScoped
public class LiquibaseRunner {

    @ConfigProperty(name = "quarkus.datasource.jdbc.url")
    String datasourceUrl;

    @ConfigProperty(name = "quarkus.datasource.username")
    String datasourceUsername;

    @ConfigProperty(name = "quarkus.datasource.password")
    String datasourcePassword;

    @ConfigProperty(name = "quarkus.liquibase.change-log")
    String changeLogLocation;

    public void onApplicationStart(@Observes StartupEvent even) {
        LaunchMode mode = io.quarkus.runtime.LaunchMode.current();
        if(mode != LaunchMode.TEST) {
            this.runMigration();
        } else {
            Log.info("Skipping DB migrations in TEST mode.");
        }
    }
    public void runMigration() {
        Log.info("Migrating DB " + datasourceUrl);
        Liquibase liquibase = null;
        try {
            ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(Thread.currentThread().getContextClassLoader());
            DatabaseConnection conn = DatabaseFactory.getInstance().openConnection(datasourceUrl, datasourceUsername, datasourcePassword, null, resourceAccessor);

            liquibase = new Liquibase(changeLogLocation, resourceAccessor, conn);
            liquibase.update(new Contexts(), new LabelExpression());
        } catch (Exception e) {
            Log.error("Liquibase Migration Exception: " + ExceptionUtil.generateStackTrace(e));
        }
        finally {
            if(liquibase!=null)
            {
                try {
                    liquibase.close();
                } catch (LiquibaseException e) {
                    Log.info(e.getMessage());
                }
            }
        }
    }

}
deathwaiting commented 1 year ago

DatabaseFactory

Just as a hint that may help:

then in you LiquibaseRunner :

@ConfigProperty(name = "com.myapp.liquibase.datasource.jdbc.url")
    String datasourceUrl;
kavishkamk commented 1 year ago

Thank you very much. Also can you tell, how can I change the driver when I used this? When use this quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/telemetry it says java.sql.SQLException : Driver does not support the proviced URL: jdbc:postgresql://localhost:5432/telemetry

narayanan57 commented 1 year ago

I am getting following exception from Hibernate.Can anybody help ? java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors [error]: Build step io.quarkus.hibernate.orm.deployment.HibernateOrmProcessor#configurationDescriptorBuilding threw an exception: io.quarkus.runtime.configuration.ConfigurationException: Model classes are defined for the default persistence unit, but no default datasource was found. The default EntityManagerFactory will not be created. To solve this, configure the default datasource. Refer to https://quarkus.io/guides/datasource for guidance.

maven:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-reactive</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-liquibase</artifactId>
</dependency>
 <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-postgresql</artifactId>
 </dependency>

My properties:

quarkus.datasource.db-kind=postgresql 
quarkus.datasource.username=${DATABASE_USERNAME:}
quarkus.datasource.password=${DATABASE_PASSWORD:}
quarkus.datasource.jdbc=false
quarkus.datasource.jdbc.url=jdbc:postgresql://${DATABASE_SERVER:}/${DATABASE_NAME:}
quarkus.datasource.reactive=true
quarkus.datasource.reactive.url=postgresql://${DATABASE_SERVER:}/${DATABASE_NAME:}
quarkus.datasource."sample".reactive.url=postgresql://${DATABASE_SERVER:}/${DATABASE_NAME:}
quarkus.datasource."sample".db-kind=postgresql
quarkus.datasource."sample".username=${DATABASE_USERNAME:}
quarkus.datasource."sample".password=${DATABASE_PASSWORD:}
quarkus.datasource."sample1".reactive.url=postgresql://${DATABASE_SERVER:}/${DATABASE_NAME:}
quarkus.datasource."sample1".db-kind=postgresql
quarkus.datasource."sample1".username=${DATABASE_USERNAME:}
quarkus.datasource."sample1".password=${DATABASE_PASSWORD:}

#Liquibase properties
quarkus.liquibase.migrate-at-start=${LIQUIBASE_MIGRATE:false}
%test.quarkus.liquibase.migrate-at-start=false
quarkus.liquibase.change-log=db/changelog/db.changelog-master.xml
quarkus.liquibase.validate-on-migrate=${LIQUIBASE_VALIDATE:false}
quarkus.liquibase.default-schema-name=${APP_SCHEMA_NAME:public}

My LiquibaseRunner class

@ConfigProperty(name = "quarkus.datasource.jdbc.url")
    String datasourceUrl;

    @ConfigProperty(name = "quarkus.datasource.username")
    String datasourceUsername;

    @ConfigProperty(name = "quarkus.datasource.password")
    String datasourcePassword;

    @ConfigProperty(name = "quarkus.liquibase.change-log")
    String changeLogLocation;

    @ConfigProperty(name = "quarkus.liquibase.default-schema-name")
    String dataSourceSchemaName;

    public void onApplicationStart(@Observes StartupEvent even) {
        LaunchMode mode = LaunchMode.current();
        if (mode != LaunchMode.TEST) {
            this.runMigration();
        } else {
            Log.info("Skipping DB migrations in TEST mode.");
        }
    }

    public void runMigration() {
        Log.info("Migrating DB " + datasourceUrl);
        Liquibase liquibase = null;
        try {
            ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(Thread.currentThread().getContextClassLoader());
            DatabaseConnection conn = DatabaseFactory.getInstance().openConnection(datasourceUrl, datasourceUsername, datasourcePassword, null, resourceAccessor);
            Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn);
            database.setDefaultSchemaName(dataSourceSchemaName);
            liquibase = new Liquibase(changeLogLocation, resourceAccessor, database);
            liquibase.getDatabase().setDefaultSchemaName(dataSourceSchemaName);
            liquibase.update(new Contexts(), new LabelExpression());
        } catch (Exception e) {
            Log.error("Liquibase Migration Exception: " + ExceptionUtil.generateStackTrace(e));
        } finally {
            if (liquibase != null) {
                try {
                    liquibase.close();
                } catch (LiquibaseException e) {
                    Log.info(e.getMessage());
                }
            }
        }
    }
indalaterre commented 1 week ago

Hello guys, is there any plan for this issue? Thank you very much.