hibernategrailsgrails-4hibernate-5

How can I add custom db objects during schema generation with Grails 4.0.5 (Hibernate 5) without the need for migration plugin?


I need to add a couple functions and views that are part of the system schema design. This should be part of the session start up (especially in development and test) because of the dbCreate create-drop settings on the data source so that the initial dev & test environments are initially set up consistently & pristine.

It does not make sense to use the database migration plugin because these are predefined db objects that are part of the whole system design and do not belong in a change set unless they themselves are changing. It's equivalent to requiring us to add the domain table generation sql in a change set. It makes sense if it's changing (that's what a change set is for!), but if it's just being initially created, it's conceptually the wrong place to put it.

Further we can't use the migration plugin because our production QA process does not allow for that approach (it is an external process not tied to the project source so it doesn't make sense to have the change sets maintained in multiple projects).

Hibernate generates it's schema automatically from the grails domain objects, we have the sql for the views (dependent on the tables from the domain objects) as well as the sql for a few functions used within those views, and we just need to determine where to best have GORM / Hibernate execute said sql in order to generate the appropriate db objects when the schema is generated.

In Grails 2.5.5 I used a custom GrailsAnnotationConfiguration to hook in to the schema generation process. This is no longer an option with the use of Hibernate 5 in grails 4+.

What is the best way to tie in to the Hibernate Schema generation process in order to add custom db objects like functions and views without relying on the database migration plugin?


Solution

  • Currently (Grails 4.05) the answer seems to be either use database migration plugin (which is inappropriate for me as outlined in the original question) or use a custom HibernateMappingContextConfiguration in order to inject your own hibernate 'AuxiliaryDatabaseObject' into the session factory via overriding the buildSessionFactory() method. These objects are documented in the Hibernate documentation, and the config is mentioned in the GORM documentation.

    All attempts at specifying mappings for auxiliary database objects failed. I could not get Gorm / Grails to pay attention to the hibernate configuration and mapping files. It's unclear or if this is intentional or a bug. Regardless, I needed a different approach.

    Fortunately HibernateMappingContextConfiguration has support for directly adding an AuxiliaryDatabaseObject through which you can effectively inject any SQL required to create or drop the auxiliary db objects including functions, views, and even arbitrary sql.

    This works for me, and has the advantage that any code injected in this manner carries through (naturally) to the ddl generated when using schema-export, which is necessary when management of the DB (including change set management) is external, with the exported ddl being the produced artifact.

    Additionally, this lets you keep your dev environment using 'create-drop' for the datasource (so it always starts up from a pristine schema).

    for example:

    MyHibernateMappingContextConfiguration extends HibernateMappingContextConfiguration {
      @Override
      SessionFactory buildSessionFactory() throws HibernateException {
        //...
        //create and add AuxiliaryDatabaseObject instances as needed
        super.addAuxiliaryDatabaseObject(myAuxiliaryDatabaseObject)
        //...
        return super.buildSessionFactory() 
      }
    }
    

    and then just configure hibernate to use your custom context config via application.yml:

    hibernate:
        configClass: path.to.MyHibernateMappingContextConfiguration