1

I'm trying to upgrade from Hibernate 4.3.11 to 5.4.x with Spring-Orm 4.3.29. As part of the upgrade, I need to migrate existing custom logic that is dependent on NamedSQLQueries retrieved from Hibernate Configuration class. This custom logic mutates the NamedSQLQueries and updates back to the original Map before building the sessionFactory. In the new version (5.x) - I modified the bootstrapping as per the new documentation and able to build the metadata but not able to mutate the namedNativeQueries (per documentation metadata is immutable). Is there a way to mutate the metadata before building the sessionFactory.

Current code -

public class CustomFactoryBean extends LocalSessionFactoryBean {
...

    @Override
    protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder localSessionFactoryBuilder) {
      localSessionFactoryBuilder.buildMappings(); //deprecated in 5.x
      mutateNamedSQLQueries(localSessionFactoryBuilder.getNamedSQLQueries()); //mutates NamedSQLQueries and updates back to the Map (Map<String, NamedSQLQueryDefinition>) AND localSessionFactoryBuilder.getNamedSQLQueries() returns nothing in 5.x (deprecated)
      localSessionFactoryBuilder.buildSessionFactory();
    }

}

New Code -

    @Override
    protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder localSessionFactoryBuilder) {

            final BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
                    .enableAutoClose()
                    .applyIntegrator(MetadataExtractorIntegrator.INSTANCE) //MetadataExtractorIntegrator class is based on the 2nd link below
                    .build();
            
            final StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry)
                    .applySettings(localSessionFactoryBuilder.getProperties())
                    .build();
            
            MetadataSources metadataSources = new MetadataSources(serviceRegistry);
            metadataSources.addAnnotatedClass(TestEntity.class);
            
            Metadata metadata = metadataSources.buildMetadata();
            
            List<NamedSQLQueryDefinition> namedSQLQueryDefinitions = new ArrayList<>(metadata.getNamedNativeQueryDefinitions());
            mutateNamedNativeQueries(namedSQLQueryDefinitions); //not able to mutate namedSQLQueryDefinitions since metadata is immutable

            return metadata.buildSessionFactory();

    }

Per this link - https://hibernate.atlassian.net/browse/HHH-12089, looks like the right way to mutate metadata before building it is to use MetadataContributor but I don't see an example or any mention in the official documentation where and how to plugin the custom MetadataContributor.

References - Building the sessionFactory in Hibernate 5 https://docs.jboss.org/hibernate/stable/orm/userguide/html_single/Hibernate_User_Guide.html#bootstrap-native-SessionFactory

To read the metadata once the sessionFactory is built https://vladmihalcea.com/how-to-get-access-to-database-table-metadata-with-hibernate-5/

Any help on this would be much appreciated.

Vicky
  • 11
  • 2

1 Answers1

0

In a MetadataContributor you can remove entries from InFlightMetadataCollector#getNamedQueryDefinitions and re-add them after doing your changes.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
  • I created CustomContributor as below but I dont see way to add the contributor in the bootstrapping process. public class CustomMetadataContributor implements MetadataContributor { @Override public void contribute(InFlightMetadataCollector metadataCollector, IndexView jandexIndex) { Collection namedNativeQueryDefinitions = metadataCollector.getNamedNativeQueryDefinitions(); mutateNamedSQLQueries(namedNativeQueryDefinitions); } private void mutateNamedSQLQueries(Collection namedNativeQueryDefinitions) { //mutate here } } – Vicky Jan 14 '21 at 14:14
  • Iterate named native query, and remove the element you want to replace. After that, call the addNamedNativeQuery method – Christian Beikov Jan 14 '21 at 14:17
  • I've iterated and modified the required element and adding it back as a new element using addNamedNativeQuery but this logic is not getting invoked in the bootstrapping process. Do I need to add this CustomContributor to StandardServiceRegistry or MetadataSources or something else for this logic to get invoked? – Vicky Jan 14 '21 at 15:10
  • code -- NamedSQLQueryDefinition namedNativeQueryDefinition = metadataCollector.getNamedNativeQueryDefinition("TestEntity.namedNativeQuery");namedNativeQueryDefinition.getQueryString().replace("TEST", "TEST_TEMP");metadataCollector.addNamedNativeQuery(new NamedSQLQueryDefinitionBuilder().setName(namedNativeQueryDefinition.getName()).setFetchSize(namedNativeQueryDefinition.getFetchSize()).createNamedQueryDefinition()); – Vicky Jan 14 '21 at 15:10
  • Hi Chris, The override method in custom contributor gets invoked after adding the Custom Contributor class name in file org.hibernate.boot.spi.MetadataContributor under resources/META-INF/services directory but the problem is, none of Spring Beans are available in this Custom Contributor which is making my mutation process difficult. Am I missing injecting the contributor at wrong place? Can you please suggest? – Vicky Jan 15 '21 at 17:53
  • This class is supposed to be instantiated through the Service loader mechanism, so that's the best it gets. I think for newer Hibernate versions you can use a BeanContainer to access Spring context information during bootstratp: http://www.matez.de/index.php/2019/04/05/connecting-spring-and-hibernate-though-beancontainer/ – Christian Beikov Jan 18 '21 at 08:17