hibernate-reactive icon indicating copy to clipboard operation
hibernate-reactive copied to clipboard

V2.0 Alpha does not use Classloader Service from Service Registry

Open milgner opened this issue 2 years ago • 1 comments

With v1 this works fine through the registered classloader service, but with v2 it throws an exception when building the session factory:

        val configuration = Configuration()
        configuration
            .setProperty(
                Settings.JAKARTA_PERSISTENCE_PROVIDER,
                "org.hibernate.reactive.provider.ReactivePersistenceProvider",
            )
            .setProperty(Settings.DIALECT, "org.hibernate.dialect.PostgreSQL10Dialect")
            .setProperty(Settings.URL, databaseConfig.url)
            .setProperty(Settings.USER, databaseConfig.username)
            .setProperty(Settings.PASS, databaseConfig.password)
            .setProperty(Settings.SHOW_SQL, pluginManager.isDevelopment.toString())
            .setProperty(Settings.FORMAT_SQL, pluginManager.isDevelopment.toString())

        val classloaders = mutableSetOf<ClassLoader>()
        for (extension in pluginManager.getExtensions(HibernateConfiguration::class.java)) {
            logger.info("Processing extension $extension")
            for (clz in extension.annotatedClasses()) {
                logger.info("Adding class $clz")
                classloaders.add(clz.classLoader)
                configuration.addAnnotatedClass(clz)
            }
        }

        val serviceRegistry = hibernateServiceRegistry(configuration, classloaders)
        val jpaSessionFactory = configuration.buildSessionFactory(serviceRegistry)
        logger.info("JPA SessionFactory initialised")
        return jpaSessionFactory.unwrap(Mutiny.SessionFactory::class.java)
private fun hibernateServiceRegistry(
        configuration: Configuration,
        classloaders: Set<ClassLoader>,
    ): ServiceRegistry {
        // since every plugin has its own classloader and Hibernate needs access to classes inside
        // the plugins, the classloader service needs manual composition based on all upstream
        // classloaders
        val classLoaderService = ClassLoaderServiceImpl(classloaders, TcclLookupPrecedence.BEFORE)
        val builder =
            ReactiveServiceRegistryBuilder()
                .addService(ClassLoaderService::class.java, classLoaderService)
                // see
                // https://hibernate.org/reactive/documentation/1.0/reference/html_single/#_vert_x_instance_service
                // it avoids Hibernate Reactive having to instantiate its own Vert.x
                .addService(
                    VertxInstance::class.java,
                    VertxInstance { vertx },
                )
                .applySettings(configuration.properties)
        return builder.build()
    }
Exception in thread "main" org.hibernate.MappingException: entity class not found: com.marcusilgner.plugin1.models.Model1
	at org.hibernate.mapping.PersistentClass.getMappedClass(PersistentClass.java:174)
	at org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId(BinderHelper.java:868)
	at org.hibernate.boot.model.internal.PropertyBinder.bindBasic(PropertyBinder.java:993)
	at org.hibernate.boot.model.internal.PropertyBinder.bindProperty(PropertyBinder.java:867)
	at org.hibernate.boot.model.internal.PropertyBinder.buildProperty(PropertyBinder.java:765)
	at org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations(PropertyBinder.java:686)
	at org.hibernate.boot.model.internal.EntityBinder.processIdPropertiesIfNotAlready(EntityBinder.java:961)
	at org.hibernate.boot.model.internal.EntityBinder.handleIdentifier(EntityBinder.java:302)
	at org.hibernate.boot.model.internal.EntityBinder.bindEntityClass(EntityBinder.java:228)
	at org.hibernate.boot.model.internal.AnnotationBinder.bindClass(AnnotationBinder.java:417)
	at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:255)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:271)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:314)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:121)
	at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:442)
	at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:93)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:863)
	at com.marcusilgner.Boot.buildJpaSessionFactory(Boot.kt:155)

Full reproducer at https://github.com/milgner/hibernate-reactive-2-classloader-repro/

milgner avatar Mar 21 '23 21:03 milgner

I recently re-factored some things in the project to use the latest regular Hibernate and the corresponding classloader registration changed as follows:

     private fun hibernateServiceRegistry(
         configuration: Configuration,
-        classloaders: Set<ClassLoader>,
+        classloaders: Collection<ClassLoader>,
     ): ServiceRegistry {
         // since every plugin has its own classloader and Hibernate needs access to classes inside
         // the plugins, the classloader service needs manual composition based on all upstream
         // classloaders
-        val classLoaderService =
-            ClassLoaderServiceImpl(classloaders, TcclLookupPrecedence.BEFORE)
-        val builder =
-            ReactiveServiceRegistryBuilder()
-                .addService(ClassLoaderService::class.java, classLoaderService)
-                .applySettings(configuration.properties)
-        return builder.build()
+
+        val bootstrapRegistryBuilder = BootstrapServiceRegistryBuilder()
+        classloaders.forEach(bootstrapRegistryBuilder::applyClassLoader)
+        val standardRegistryBuilder = StandardServiceRegistryBuilder(bootstrapRegistryBuilder.build())
+        return standardRegistryBuilder.applySettings(configuration.properties).build()
     }

Basically I had to use a custom bootstrap registry builder, add all the classloaders and then use the resulting bootstrap registry to create the standard service registry.

Edit: shortened code snippet for clarity / brevity

milgner avatar Jul 07 '23 11:07 milgner