Programming LiquiBase

Hello,

For a project of mine, I want to use Liquibase to generate database modifications in real time. Hibernate’s DDL capabilities just don’t cut the mustard.

But I have not found anywhere about how to do. Here is the code I came up with so far:

  1.         final HashMap<String, String> settings = new HashMap<String, String>();
  2.         settings.put(“hibernate.connection.driver_class”, “org.hsqldb.jdbcDriver”);
  3.         settings.put(“hibernate.connection.url”, “jdbc:hsqldb:mem:testdb”);
  4.         settings.put(“hibernate.connection.username”, “sa”);
  5.         settings.put(“hibernate.connection.password”, “”);
  6.         settings.put(“hibernate.dialect”, “org.hibernate.dialect.HSQLDialect”);
  7.         final Properties props = new Properties();
  8.         props.putAll(settings);
  9.         /*
  10.          * Create the configuration properties, and start by setting properties
  11.          * necessary to tell which JDBC driver to use, which user/password,
  12.          * etc.
  13.          */
  14.         final Configuration cfg = new Configuration();
  15.         cfg.addProperties(props);
  16.         final SessionFactory sf = cfg.buildSessionFactory();
  17.         final Session s = sf.openSession();
  18.         final Connection c = s.connection();
  19.         final ConstraintsConfig constraint = new ConstraintsConfig();
  20.         constraint.setPrimaryKey(true);
  21.         final ColumnConfig id = new ColumnConfig();
  22.         id.setAutoIncrement(false);
  23.         id.setName(“id”);
  24.         id.setType(“UUID”);
  25.         id.setConstraints(constraint);
  26.         final ColumnConfig name = new ColumnConfig();
  27.         name.setType(“VARCHAR”);
  28.         name.setName(“name”);
  29.         final CreateTableChange change = new CreateTableChange();
  30.         change.addColumn(id);
  31.         change.addColumn(name);
  32.         change.setTableName(“test”);
  33.         final DatabaseConnection dbc = new HsqlConnection©;
  34.         final Database db = new HsqlDatabase();
  35.         db.setConnection(dbc);
  36.         db.setAutoCommit(false);
  37.         db.executeStatements(change, null, null);

Well, this doesn’t work. The last line throws NPE.

I am at a loss. The thing is, I do NOT want to use an XML file to generate the changes. As I want to generate changes in real time, I’ll spawn Change objects in order to… Well, what? How do I operate from there?

Any help appreciated!

Have you taken a look at the liquibase hibernate integration code? (http://liquibase.jira.com/source/browse/CONTRIB/trunk/hibernate/trunk)


It uses hibernate’s hbm2ddl to generate liquibase changesets, but it does still use hbm2ddl’s database/config comparison logic.


Is your code doing somethign similar? Or am I missing what you are looking to do?


Nathan

OK, well, I have had some sort of success. I’ve had to create an XML file with the described changeset and the org.liquibase.Liquibase objet did the trick.

But I really wish it were easier to generate a DDL change set without having to go through an XML file… Anyway, here is the code that I came up with:

  1. package eel.kitchen.dynamicdb;

    import liquibase.Liquibase;
    import liquibase.database.Database;
    import liquibase.database.DatabaseFactory;
    import liquibase.database.jvm.HsqlConnection;
    import liquibase.exception.DatabaseException;
    import liquibase.exception.LiquibaseException;
    import liquibase.resource.ClassLoaderResourceAccessor;
    import liquibase.resource.ResourceAccessor;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;

    import java.sql.Connection;
    import java.util.HashMap;
    import java.util.Properties;

    public class LiquiBaseDemo
    {
        public static void main(final String… args)
            throws LiquibaseException
        {
            final ClassLoader myself = LiquiBaseDemo.class.getClassLoader();
            final ResourceAccessor ra = new ClassLoaderResourceAccessor(myself);
            final HashMap<String, String> settings = new HashMap<String, String>();

            settings.put(“hibernate.connection.driver_class”, “org.hsqldb.jdbcDriver”);
            settings.put(“hibernate.connection.url”, “jdbc:hsqldb:mem:testdb”);
            settings.put(“hibernate.connection.username”, “sa”);
            settings.put(“hibernate.connection.password”, “”);
            settings.put(“hibernate.dialect”, “org.hibernate.dialect.HSQLDialect”);
            settings.put(“hibernate.show_sql”, “true”);
            final Properties props = new Properties();
            props.putAll(settings);

            final Configuration cfg = new Configuration();
            cfg.addProperties(props);

            final SessionFactory sf = cfg.buildSessionFactory();
            final Session s = sf.openSession();
            final Connection c = s.connection();

            final Database db = DatabaseFactory.getInstance()
                .findCorrectDatabaseImplementation(new HsqlConnection©);

            final Liquibase lb = new Liquibase(“liquibase.xml”, ra, db);

            lb.update(null);
            System.out.println(“Meh”);
        }
    }
Probably not ideal. Comments welcome.


No, not in detail. It doesn’t really do what I want from what I’ve seen so far. I do mean real time: generating changelogs, mappings etc on the fly. And I want to use Liquibase for DDL. Hibernate comes “after the fact” in my case.

While I could get Liquibase to work by generating a ChangeLog XML file, I really wish it were easier to generate a ChangeLog object (or is that another one) without generating an XML file.

Have a look at XMLChangeLogSAXHandler to see how it builds up the changelog.  You would need to do the same thing.

Best,
Laird

The workflow I had considered for the hibernate/liquibase integration is to look at the hibernate mapping and database and build an XML file so that the the XML file can be inspected to ensure that the generated changes are actually what is expected. For example, it is easy for a column rename to be interpreted as a column drop and column add but you don’t want to actually do that or you will lose data.


Nathan

OK, well, I’ve had a more detailed look, and I am deeply depressed.

LiquiBase pretty much always expects to work with a physical file, everywhere. And I don’t want to use one.

DatabaseChangeLog expects one, ResourceAccessor expects one, ChangeLogSAXHandler expects one. I want none.

OK well, it’s just a case of extending/implementing each and every object until I can at last pass at least a Dom4J document as the root of the changelog (yes, I also happen to hate Sun’s XML APIs…).

That’s not my plan. I separate the DDL operations in two: the ones which can be executed without affecting current DML sessions (creating tables, adding columns etc) and the ones which do affect current DML sessions (removing tables, adding constraints, dropping columns etc). I plan to handle these using specially crafted sessions which are schema-aware (I plan not to use POJOs at all for DML - they are not suited for the task).

Liquibase is the best solution I found for DDL, I just wish it were easier to use without filesystem backings :confused:

The normal usage of liquibase is to have physical files, but I would think you should be able to use the DatabaseChangeLog object without one. With 2.0, I attempted to refactor the codebase so you have various changelog parsers who convert from the physical file to the in-memory DatabaseChangeLog object. From that point, there all logic is taken from the changelog object, not from the original file. In your case, you should be able to build up the DatabaseChangeLog object yourself without using ChangeLogSAXHandler at all or any files at all.


You will need to assign a logical file name to your databasechangelog object, but that can be completely arbitrary and not map to an actual file.


ResourceAccessor would only be applicable if you did an sqlFile call, but you would only be doing that if you are creating physical sql files.


Nathan