Maven + Hibernate

Hi,


I am trying to use Maven plugin diff goal with my Hibernate mappings. I have been able to make it but I encountered several issues:

  • some goals are missing in the documentation http://www.liquibase.org/documentation/maven/index.html
  • the goal works with Liquibase 2.0.5 but fails with 3.0.2 with this message: [ERROR] Failed to execute goal org.liquibase:liquibase-maven-plugin:3.0.2:diff (default-cli) on project test_liquibase: Execution default-cli of goal org.liquibase:liquibase-maven-plugin:3.0.2:diff failed: An API incompatibility was encountered while executing org.liquibase:liquibase-maven-plugin:3.0.2:diff: java.lang.AbstractMethodError: null
  • I had to add a plugin dependency on org.liquibase.ext liquibase-hibernate 2.0.0 that looks like the official one but has not changed since 2010 and net.customware.liquibase liquibase-hibernate 2.1.0 that is a fork. Which one should I use ?

Did I miss something ?

I’ve not gotten a chance to work on the standard liquibase hibernate support for a while. It is on the list, but is quite a bit out of date, especially with liquibase 3.0. I’m hoping to get to it in the next month or so, but it will depend on priorities. You could try the customware one and see how that goes, but I haven’t looked at it yet.


Nathan

Thanks you very much Christian,

I was just working on something like this with HSQLDB. I have put the code in a junit test on my project so that when building the project it checks if the database structure is up to date, if not the test fails and displays the changesets that should be appended to my changelog file. This is exactly what I needed.


So is it worth releasing a new version of liquibase-hibernate extension or should we remove it and explain in the documentation how to do it the way you did? Nathan, Christian, what do you think?

I forked the standard liquibase hibernate. Now I have a running version that works with Liquibase 3.0.4 and the latest Hibernate 3 version. It supports hibernate mapping files, annotations and Spring configuration. I also added some unit tests and integration tests using hsqldb.

I still have an issue with case changes on column names when I compare a mapping with hsqldb (hsqldb transforms everything upper case and Liquibase diff is case sensitive on column names, I raised an issue for that in JIRA). 

Except from that, it is usable.

Should I create a pull request?

It’s actually pretty simple to create some kind of diff yourself.
The following code uses only JPA stuff but some hibernate specific properties.

String unitName = … //JPA unit name String changeLogPath = … //Path to the changelog master String driverClass = “org.h2.Driver”; String oldJdbcUrl = “jdbc:h2:mem:old”; String newJdbcUrl = “jdbc:h2:mem:new”; String user = “test”; String password = “test”; Database newDatabase = new H2Database(); newDatabase.setConnection(new JdbcConnection(DriverManager.getConnection(newJdbcUrl, user, password))); Database oldDatabase = new H2Database(); oldDatabase.setConnection(new JdbcConnection(DriverManager.getConnection(oldJdbcUrl, user, password))); Liquibase oldLiquibase = new Liquibase(changeLogPath, new ClassLoaderResourceAccessor(), oldDatabase); oldLiquibase.update(""); Liquibase newLiquibase = new Liquibase(null, null, newDatabase); newLiquibase.dropAll(); // Schema export to new database Map<String, Object> properties = new HashMap<String, Object>(); // Hibernate specific properties.put(“hibernate.hbm2ddl.auto”, “create”); properties.put(“hibernate.cache.region.factory_class”, null); properties.put(“hibernate.cache.use_query_cache”, false); properties.put(“hibernate.cache.use_second_level_cache”, false); properties.put(“hibernate.dialect”, “org.hibernate.dialect.H2Dialect”); properties.put(“javax.persistence.jtaDataSource”, null); properties.put(“javax.persistence.transactionType”, “RESOURCE_LOCAL”); properties.put(“javax.persistence.jdbc.driver”, driverClass); properties.put(“javax.persistence.jdbc.user”, user); properties.put(“javax.persistence.jdbc.password”, password); properties.put(“javax.persistence.jdbc.url”, newJdbcUrl); EntityManagerFactory emf = Persistence.createEntityManagerFactory(unitName, properties); // Diff DatabaseSnapshot oldSnapshot = SnapshotGeneratorFactory.getInstance().createSnapshot(oldDatabase.getDefaultSchema(), oldDatabase, new SnapshotControl(oldDatabase)); DatabaseSnapshot newSnapshot = SnapshotGeneratorFactory.getInstance().createSnapshot(newDatabase.getDefaultSchema(), newDatabase, new SnapshotControl(newDatabase)); CompareControl compareControl = new CompareControl( new CompareControl.SchemaComparison[] { new CompareControl.SchemaComparison(new CatalogAndSchema( newDatabase.getDefaultCatalogName(), newDatabase.getDefaultSchemaName()), new CatalogAndSchema( oldDatabase.getDefaultCatalogName(), oldDatabase.getDefaultSchemaName())) }, oldSnapshot.getSnapshotControl().getTypesToInclude()); DiffResult diffResult = oldLiquibase.diff(newDatabase, oldDatabase, compareControl); new DiffToChangeLog(diffResult, new DiffOutputControl(false, false, true)){ @Override public List generateChangeSets() { List changeSets = super.generateChangeSets(); List newChangeSets = new ArrayList; ChangeSet changeSet = new ChangeSet(generateId(), getChangeSetAuthor(), false, false, null, null, null, ObjectQuotingStrategy.QUOTE_ALL_OBJECTS, null);
                newChangeSets.add(changeSet);

for(ChangeSet set : changeSets) { for(Change change : set.getChanges()) {
                        boolean include = true;

                        if(change instanceof CreateIndexChange) { CreateIndexChange index = (CreateIndexChange) change; for(Change existingChange : changeSet.getChanges()) { if(existingChange instanceof AddUniqueConstraintChange) { AddUniqueConstraintChange unique = (AddUniqueConstraintChange) existingChange; boolean catalogEqual = unique.getCatalogName() == null ? index.getCatalogName() == null : unique.getCatalogName().equals(index.getCatalogName()); boolean schemaEqual = unique.getSchemaName() == null ? index.getSchemaName() == null : unique.getSchemaName().equals(index.getSchemaName()); if(catalogEqual && schemaEqual && unique.getTableName().equals(index.getTableName())) { StringBuilder sb = new StringBuilder(); for(ColumnConfig column : index.getColumns()) { sb.append(column.getName()).append(’,’); } sb.setLength(sb.length() - 1); if(unique.getColumnNames().equals(sb.toString())) { // TODO: instead of remove, add a precondition ifIndexDoesNotExist include = false; } break; } } } }

                        if(include) {
                            //create only a single changeset
                            changeSet.addChange(change);

                            //alternative, many changesets
                            //ChangeSet newChangeSet = new ChangeSet(generateId(), getChangeSetAuthor(), false, false, null, null, null, ObjectQuotingStrategy.QUOTE_ALL_OBJECTS, null);
                            //newChangeSet.addChange(change);
                            //newChangeSets.add(newChangeSet);
                        }
 } } return Arrays.asList(changeSet); } }.print(System.out); emf.close(); oldDatabase.close(); newDatabase.close();


If you have an update to the hibernate extension that would make it work with 3.0, I’d say definitely send a pull request and I’ll take a look at it and get a new version released. 


The case sensitivity of column comparisons should be controllable by the Database implementation. It could be a bug with the hsql database comparisons that should be fixed.


Christian, it looks like your strategy is to update an h2 database with hibernates auto-ddl functionality and then diff the two h2 databases, right? I think we should be able to use hibernates metadata APIs to directly “snapshot” the hiberate database model and do the same comparisons we do with any other database without the intermediate step.


Nathan



I just sent the pull request.

Let me know if you still see some things I can improve before you release a new version.


Note that I added some hibernate database specific comparison classes but I still think the default behaviour of Liquibase could be improved regarding case sensitivity: for example if you create 2 H2DB databases, one of which is case sensitive using the same change log, the diff should not contain any difference.


Thanks, I’ll take a look at the pull request.


Nathan