Home > OS >  How to get ServiceRegistry instance from Spring Boot application?
How to get ServiceRegistry instance from Spring Boot application?

Time:03-08

I am trying to use a CommandLineRunner to access info on the underlying Hibernate database so I can eventually dump a schema file. I need to get access to the service registry instance to do that.

I tried to see if I can get it from the AutoWired EntityManagerFactory via this code:

package test;

import javax.persistence.EntityManagerFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.ServiceRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class AutoWiredTest implements CommandLineRunner {
  @Autowired
  private EntityManagerFactory emf;
  
  @Override
  public void run(String... args) 
  throws Exception {
    SessionFactoryImplementor sessionFactory = emf.unwrap(SessionFactoryImplementor.class);
    ServiceRegistry serviceRegistry = sessionFactory.getServiceRegistry();
    if( serviceRegistry == null )
      throw new Exception("Service registry is null");
    MetadataSources metadataSources = new MetadataSources(serviceRegistry);
    metadataSources.buildMetadata();
 }

The application gives me this error:

java.lang.IllegalStateException: Failed to execute CommandLineRunner
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:761) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.4.jar:2.6.4]
        at test.AutoWiredTest.main(AutoWiredTest.java:31) ~[classes/:na]
Caused by: org.hibernate.HibernateException: Unexpected type of ServiceRegistry [org.hibernate.service.internal.SessionFactoryServiceRegistryImpl] encountered in attempt to build MetadataBuilder
        at org.hibernate.boot.internal.MetadataBuilderImpl.getStandardServiceRegistry(MetadataBuilderImpl.java:113) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
        at org.hibernate.boot.internal.MetadataBuilderImpl.<init>(MetadataBuilderImpl.java:93) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
        at org.hibernate.boot.MetadataSources.getMetadataBuilder(MetadataSources.java:146) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
        at org.hibernate.boot.MetadataSources.buildMetadata(MetadataSources.java:202) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
        at test.AutoWiredTest.run(AutoWiredTest.java:26) ~[classes/:na]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:777) ~[spring-boot-2.6.4.jar:2.6.4]
        ... 5 common frames omitted

Next, I tried to create a builder with this code:

package test;

import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BuilderTest implements CommandLineRunner {
  @Override
  public void run(String... args) 
  throws Exception {
    new StandardServiceRegistryBuilder().configure().build();
  }
  
  public static void main(String[] args) 
  throws Exception {
    SpringApplication.run(test.BuilderTest.class, args);
  }
}

Which resulted in this error:

java.lang.IllegalStateException: Failed to execute CommandLineRunner
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:761) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.4.jar:2.6.4]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.4.jar:2.6.4]
        at test.BuilderTest.main(BuilderTest.java:18) ~[classes/:na]
Caused by: org.hibernate.internal.util.config.ConfigurationException: Could not locate cfg.xml resource [hibernate.cfg.xml]
        at org.hibernate.boot.cfgxml.internal.ConfigLoader.loadConfigXmlResource(ConfigLoader.java:53) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
        at org.hibernate.boot.registry.StandardServiceRegistryBuilder.configure(StandardServiceRegistryBuilder.java:254) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
        at org.hibernate.boot.registry.StandardServiceRegistryBuilder.configure(StandardServiceRegistryBuilder.java:243) ~[hibernate-core-5.6.5.Final.jar:5.6.5.Final]
        at test.BuilderTest.run(BuilderTest.java:13) ~[classes/:na]
        at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:777) ~[spring-boot-2.6.4.jar:2.6.4]
        ... 5 common frames omitted

It is looking for a cfg.xml file, but I have already defined my database configuration in the application.properties file:

hibernate.current_session_context_class=thread
hibernate.format_sql=false
hibernate.show_sql=false
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/marketing
spring.datasource.username=marketing
spring.datasource.password=[PASS]
spring.logging.level.root=ERROR
spring.logging.level.org.hibernate=INFO
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB103Dialect

Any ideas what is going wrong?

CodePudding user response:

Seems like Spring just doesn't register Hibernate ServiceRegistry as a bean. I looked through the sources and discover that its instance is created inside the SessionFactoryImpl constructor.

You can try to apply this hack.

@SpringBootApplication
public class AutoWiredTest implements CommandLineRunner {
  @Autowired
  private EntityManagerFactory emf;
  
  @Override
  public void run(String... args) 
  throws Exception {
    SessionFactoryImplementor sessionFactory = emf.unwrap(SessionFactoryImplementor.class);
    ServiceRegistry serviceRegistry = sessionFactory.getServiceRegistry();
  }
  
  public static void main(String[] args) 
  throws Exception {
    SpringApplication.run(AutoWiredTest.class, args);
  }
}
  • Related