Generating changelog programmatically: The application must supply JDBC connections

Hello, I have the following code to generate changelog programmatically:

      Database db = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
      
      String url = "hibernate:spring:cloud.service2,cloud.boot.starter.data"
      + "?dialect=org.hibernate.dialect.MySQL8Dialect"
      + "&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy"
      + "&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy";
      
      HibernateConnection hibernateConnection = new HibernateConnection(url, null);
      JdbcConnection jdbcConnection = new JdbcConnection(hibernateConnection);
      Database hdb = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(jdbcConnection);

      File changelog = new File(CHANGELOG_FOLDER + File.separator + changelogFileFileName);
      changelog.delete();
      CommandLineUtils.doDiffToChangeLog(changelog.getAbsolutePath(), hdb, db, new DiffOutputControl(false, false, false, null), null, null);

      return Optional.of(changelog);

however, during the building of HibernateSpringPakcageDatabase (hdb in the code above) when the execution get to buildMetadataFromPath() of HibernateEjb3Database it throw an error at building the EntityManagerFactory:

2021-07-23 10:41:06.023  WARN 28764 [           main] o.h.e.j.e.i.JdbcEnvironmentInitiator     * * * : HHH000342: Could not obtain connection to query metadata

java.lang.UnsupportedOperationException: The application must supply JDBC connections
	at org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl.getConnection(UserSuppliedConnectionProviderImpl.java:44) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:68) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:101) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:176) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:127) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1224) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1255) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
	at liquibase.ext.hibernate.database.HibernateEjb3Database.buildMetadataFromPath(HibernateEjb3Database.java:59) ~[liquibase-hibernate5-4.3.5.jar:na]
	at liquibase.ext.hibernate.database.HibernateDatabase.buildMetadata(HibernateDatabase.java:143) ~[liquibase-hibernate5-4.3.5.jar:na]
	at liquibase.ext.hibernate.database.HibernateDatabase.setConnection(HibernateDatabase.java:83) ~[liquibase-hibernate5-4.3.5.jar:na]
	at liquibase.database.DatabaseFactory.findCorrectDatabaseImplementation(DatabaseFactory.java:121) ~[liquibase-core-4.3.5.jar:na]

My understanding is that the HibernateSpringPackageDatabase is a meta database without real connection, however, at some point Hibernate is trying to get its connection. The root cause of this issue is that the buildMetadata() of HibernateDatabase class use the path from the url i specified to load the class but the path is the package, not a classname so it failed and thus fall back to loading metadata using connection.

My question has 2 parts:
1/My url match the template for HibernateSpringPackageDatabase which mean there are multiple Entity classes in the package, why the build metadata expects only 1 class? Am I wrong assuming that the changelog can be generated by diff 2 database with more than one entity/table?
2/Is there a documentation of how to use liquibase programmatically?

Hi @lnthai2002

Are you looking for Liquibase Java API documentation? Please have a look if this could help in some way.

Thanks,
Rakhi Agrawal

Thank you Rakhi, I saw the javadoc but they don’t help much. I am looking for some guide line, for example, to generate change log, I need 2 schemas to compare, one is the current state of the DB (current) and the other is the the state after the changes are applied (target); which class I should use to get the schema of the target?

umm, please bear with my questions. I’m a bit lost with your last comment.
Are you trying to generate a diff changelog here? OR Are you tring to generate a changelog of already created DB (in final state)?

Thank you Rakhi. The intention is to generate the diff changelog between the current database and the entity set. We are doing this because when new Entities are added to the code, we need to generate the diff changelog then verify that it does not drop anything already exist in the current DB before applying the changelog to the DB.

ohh I see. So may be you are looking for something like this here.

I hope this gives you some idea. The way explained on this link is not programmatically, but same should be implemented through liquibase API as well.

Thanks!
Rakhi Agarwal