配置使用JNDI的Spring Boot多个数据源。

huangapple go评论72阅读模式
英文:

Configuring multiple data sources with Spring Boot using JNDI

问题

以下是您提供的内容的翻译:


我目前正在使用Spring Boot构建一个Web应用程序,目前只有一个数据源。现在我想使用JNDI添加第二个数据源。为此,我为每个数据源创建了一个配置类,该类应该使用JNDI获取属性。不幸的是,我一直收到一个错误,告诉我无法创建reportingEntityManager。在嵌入第二个数据源之前,一切都运行正常。

我尝试了以下解决方案:https://stackoverflow.com/questions/41941147/configuring-two-datasources-by-jndi-lookup-with-springboot + https://stackoverflow.com/questions/32114558/nouniquebeandefinitionexception-no-qualifying-bean-of-type-javax-persistence-e,并尝试了几个教程,但都无法解决。

我已经尝试了两天来解决这个问题,我已经没有更多的想法可以尝试了。如果有人有想法,或者至少可以帮助我理解问题出在哪里,那将会非常好,谢谢!

第一个数据源的配置类

@Configuration
@EnableConfigurationProperties
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = {"com.project.support.database.research.repositories"},
        entityManagerFactoryRef = "researchEntityManagerFactory",
        transactionManagerRef = "researchTransactionManager")
@PropertySource("classpath:application.properties")
public class ResearchDbConfig {

    @Primary
    @Bean(name = "researchDataSourceProperties")
    @ConfigurationProperties("research.spring")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean(name = "researchEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("researchDataSource") DataSource researchDataSource) {
        return builder.dataSource(researchDataSource)
                .packages("com.project.support.database.research.entities")
                .persistenceUnit("research")
                .build();
    }

    @Primary
    @Bean(name = "researchTransactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("researchEntityManagerFactory") EntityManagerFactory researchEntityManagerFactory) {
        return new JpaTransactionManager(researchEntityManagerFactory);
    }
}

第二个数据源的配置类

@EntityScan("com.project.support.database.reporting.entities")
@Configuration
@EnableConfigurationProperties
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "reportingEntityManagerFactory",
        transactionManagerRef = "reportingTransactionManager",
        basePackages = {"com.project.support.database.reporting.repositories"})
@PropertySource("classpath:application.properties")
public class ReportingDbConfig {

    @Bean
    public DataSource secondaryDataSource() {
        JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        return dataSourceLookup.getDataSource(jndiSecondary().getJndiName());
    }

    @Bean(name = "reportingEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("reportingDataSource") DataSource reportingDataSource) {
        return builder.dataSource(reportingDataSource)
                .packages("com.project.support.database.reporting.entities")
                .persistenceUnit("reporting")
                .build();
    }

    @Bean(name = "reportingTransactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("reportingEntityManagerFactory") EntityManagerFactory reportingEntityManagerFactory) {
        return new JpaTransactionManager(reportingEntityManagerFactory);
    }
}

application.properties

research.spring.datasource.jndi-name=jdbc/research
reporting.spring.datasource.jndi-name=jdbc/reporting

错误信息

26-Aug-2020 08:05:23.580 SEVERE [RMI TCP Connection(16)-192.168.178.46] org.apache.tomcat.util.modeler.BaseModelMBean.invoke 引发异常 [manageApp]
 java.lang.IllegalStateException: Error starting child
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:716)
    ... 更多的堆栈信息 ...

请注意,这只是提供给您的原文的翻译。如果您有任何其他问题或需要进一步的帮助,请随时问我。

英文:

I´m currently building a web app with Spring Boot and until now I have one datasource. Now I want to add a second one using JNDI. To do so I created a configuration class for each datasource that is supposed to fetch the properties using JNDI. Unfortunately I keep getting an error telling me that the reportingEntityManager cannot be created. Before embedding the second datasource everything worked fine.

I tried the solutions suggested in
https://stackoverflow.com/questions/41941147/configuring-two-datasources-by-jndi-lookup-with-springboot +
https://stackoverflow.com/questions/32114558/nouniquebeandefinitionexception-no-qualifying-bean-of-type-javax-persistence-e and tried several tutorials but it won´t work.

I´ve been trying to solve this problem for two days now and I´m running out of ideas on what else to try. Would be great if someone has an idea or can at least help me understanding whats wrong, thanks!

Config class for the first datasource

@EnableConfigurationProperties
@EnableTransactionManagement
@EnableJpaRepositories(
		basePackages = {"com.project.support.database.research.repositories"},
		entityManagerFactoryRef = "researchEntityManagerFactory",
		transactionManagerRef = "researchTransactionManager")
@PropertySource("classpath:application.properties")
public class ResearchDbConfig {

	@Primary
	@Bean(name = researchDataSourceProperties")
	@ConfigurationProperties("research.spring")
	public DataSourceProperties dataSourceProperties() {
		return new DataSourceProperties();
	}

	@Primary
	@Bean(name = "researchEntityManagerFactory")
	public LocalContainerEntityManagerFactoryBean entityManagerFactory(
			EntityManagerFactoryBuilder builder,
			@Qualifier("researchDataSource") DataSource researchDataSource) {
		return builder.dataSource(researchDataSource)
				.packages("com.project.support.database.research.entities")
				.persistenceUnit("research")
				.build();
	}

	@Primary
	@Bean(name = "researchTransactionManager")
	public PlatformTransactionManager transactionManager(
			@Qualifier("researchEntityManagerFactory") EntityManagerFactory researchEntityManagerFactory) {
		return new JpaTransactionManager(researchEntityManagerFactory);
	}

}

Config class for the second datasource

@Configuration
@EnableConfigurationProperties
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "reportingEntityManagerFactory",
        transactionManagerRef = "reportingTransactionManager",
        basePackages = {"com.project.support.database.reporting.repositories"})
@PropertySource("classpath:application.properties")

public class ReportingDbConfig {

    @Bean
    public DataSource secondaryDataSource() {
        JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
        return dataSourceLookup.getDataSource(jndiSecondary().getJndiName());
    }

    @Bean(name = "reportingEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("reportingDataSource") DataSource reportingDataSource) {
        return builder.dataSource(reportingDataSource)
                .packages("com.project.support.database.reporting.entities")
                .persistenceUnit("reporting")
                .build();
    }

    @Bean(name = "reportingTransactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("reportingEntityManagerFactory") EntityManagerFactory reportingEntityManagerFactory) {
        return new JpaTransactionManager(reportingEntityManagerFactory);
    }
}

application.properties

research.spring.datasource.jndi-name=jdbc/research
reporting.spring.datasource.jndi-name=jdbc/reporting

Error Message

26-Aug-2020 08:05:23.580 SEVERE [RMI TCP Connection(16)-192.168.178.46] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method [manageApp]
java.lang.IllegalStateException: Error starting child
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:716)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1729)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:289)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:457)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:406)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:289)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1401)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/support]]
at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:441)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:713)
... 41 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reportingEntityManager' defined in class path resource [com/project/support/configuration/PersistenceReportingEntityAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1630)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1082)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:857)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:123)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:666)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:353)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:300)
at org.springframework.boot.web.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:151)
at org.springframework.boot.web.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:131)
at org.springframework.boot.web.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:91)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5139)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
... 42 more
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:271)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:233)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210)
at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:51)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:94)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:242)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.handleTypes(MetadataBuildingProcess.java:352)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:111)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:861)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:888)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:360)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:384)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:371)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:336)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1688)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1626)
... 61 more
Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:100)
at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:54)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:137)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:88)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:259)
... 78 more
26-Aug-2020 08:05:23.581 SEVERE [RMI TCP Connection(16)-192.168.178.46] org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method [createStandardContext]
javax.management.RuntimeOperationsException: Exception invoking method [manageApp]
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:298)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:457)
at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:406)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:289)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1468)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:76)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1309)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1401)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:829)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Error starting child
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:716)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:690)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
at org.apache.catalina.startup.HostConfig.manageApp(HostConfig.java:1729)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:289)
... 33 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/support]]
at org.apache.catalina.util.LifecycleBase.handleSubClassException(LifecycleBase.java:441)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:198)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:713)
... 41 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'reportingEntityManager' defined in class path resource [com/project/support/configuration/PersistenceReportingEntityAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1630)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1082)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:857)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:123)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:666)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:353)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:300)
at org.springframework.boot.web.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:151)
at org.springframework.boot.web.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:131)
at org.springframework.boot.web.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:91)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5139)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
... 42 more
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:271)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:233)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210)
at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:51)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:94)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:242)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.handleTypes(MetadataBuildingProcess.java:352)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:111)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:861)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:888)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:360)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:384)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:371)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:336)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1688)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1626)
... 61 more
Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:100)
at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:54)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:137)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:88)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:259)
... 78 more

答案1

得分: 1

这个回答总结了我对与此处陈述的大致相同问题的调查结果。我选择了最近的相关问题,因为事情可能已经发生了变化,行为可能会有所不同。

我自己的设置是使用Spring Boot 2.4.12,我的项目被编译为WAR文件,并部署到一个外部Tomcat上,Tomcat的类路径中已经包含了HikariCP和Oracle库,并且Oracle连接在Tomcat的server.xml中被定义为全局资源。

关于原始问题的堆栈跟踪中的最终内部异常:

Caused by: org.hibernate.HibernateException: 在未设置'hibernate.dialect'时,无法将访问DialectResolutionInfo设置为null

我有一个印象是配置根本没有被正确读取。在我的情况下,这个消息也出现了,但那时我已经找到了一个有效的解决方案,所以我没有彻底调查它。据我所理解,Spring Boot 很可能会尝试使用默认值初始化配置中未显式设置的其余数据源属性,而这些默认值可能不匹配。

在我的情况下,application.properties 中唯一与数据源和JNDI相关的内容是:

spring.datasource.jndi-name = java:/comp/env/jdbc/MyOracleHikariDS

在我特定的情况下,我在使用的次要库中隐藏了第二个数据源,它指向一个嵌入式的H2实例,用作缓存。因此,类路径中有一个指向H2库的引用,而 Spring Boot 似乎以某种方式将其选作了默认数据源。因此,我不得不通过显式的 @Primary 声明来强制主数据源指向我的真正主数据源,后者需要通过JNDI来定义。

我认为我通过多次 M. Deinum 的评论和对其他问题的回答找到了答案,当我只想将特定数据源定义为 @Primary 时,实际上不需要提供 TransactionManagerEntityManagerFactoryBean 的实现。在我这个案例中,问题是关于这个数据源的创建方式,一旦我将其创建交给自己来手动处理。

根据我的经验,当我使用以下代码返回我的数据源时出现了问题:

@Bean
@Primary
@ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}

起初,我以为在 Spring 尝试初始化数据源作为其上下文初始化的一部分时,Tomcat 还没有初始化自己的JNDI资源 - 因此我尝试了@Lazy、代理等,但最终情况并非如此。

我个人的结论(尚未确认)是 @ConfigurationProperties 字面地获取属性并覆盖了从JNDI查找获得的属性。唯一让我成功的方式是以下内容:

@Configuration
@EnableJpaRepositories(basePackages = "com.myproject.repositories.primary",
entityManagerFactoryRef = "primaryEMF",
transactionManagerRef = "primaryTM")
public class JpaPrimaryConfig {
@Value("${spring.datasource.jndi-name}")
String jndiName;
@Bean(destroyMethod = "")
@Primary
public DataSource dataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource ds = dataSourceLookup.getDataSource(jndiName);
return ds;
}
// 我们必须将不同的实体绑定到不同的数据源 - 我们可以通过实体管理器来实现
@Primary
@Bean(name = "primaryEMF")
public LocalContainerEntityManagerFactoryBean primaryEMF(EntityManagerFactoryBuilder builder) {
return builder.dataSource(dataSource()).packages(DummyEntity.class).build();
}
@Primary
@Bean(name = "primaryTM")
public PlatformTransactionManager primaryTM(final @Qualifier("primaryEMF") LocalContainerEntityManagerFactoryBean emf) {
return new JpaTransactionManager(emf.getObject());
}
}

请注意,上述内容仅为示例;在我的工作项目中,我不需要使用 @EnableJpaRepositories,因为我需要的第二个数据源不使用 Spring 特定的存储库对象。

另一个有用的检查项,除了增加 HikariCP 的日志级别(logging.level.com.zaxxer=DEBUG),还包括确保您的Servlet容器确实已经定义了这些JNDI资源。可以通过 jconsole 来实现对本地 Tomcat 的检查。此外,启动 Tomcat 时不部署任何应用程序,以确保它不会对连接池的初始创建报错,也是很有用的。

英文:

This answer summarizes my own findings regarding approximately the same problem that is stated here. I have picked the most recent relevant question, because things might have changed and behave differently now.

My own setup was Spring Boot 2.4.12, with my project compiled as WAR artifact and deployed to an external Tomcat with HikariCP and Oracle libraries already in its classpath, and Oracle connection defined as global resource in Tomcat's server.xml.

About the final inner exception from the stacktrace of the OP:

Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set

I have an impression that the configuration was not read correctly at all. In my case, this message appeared too, but at that time I had already found a working solution - so I didn't investigate it thoroughly. As I understand it, it is very possibly that Spring Boot tries to initialize with default values the rest of the data source properties that are not set explicitly in the configuration, and these default values do not match.

In my case, the only datasource and JNDI-related thing in application.properties was

spring.datasource.jndi-name = java:/comp/env/jdbc/MyOracleHikariDS

In my specific scenario, there was a second data source hidden deep inside secondary libraries that I was using, that pointed to an embedded H2 instance for use as a cache. So there was a reference to H2 library in the classpath, which Spring Boot somehow seemed to pick up as the default datasource. So I had to force (via explicit @Primary declaration) the primary datasource to point to my real primary datasource, which needed to be defined via JNDI.

I think I found out, with the help of multiple M. Deinum comments and answers to other questions, that it was not required to also provide implementations of TransactionManager and EntityManagerFactoryBean, when I only wanted to define a specific data source as @Primary. In my case the problem was how that data source was created, once I took its creation upon myself, doing it manually.

In my experience, something went wrong, when I returned my datasource using the following code:

@Bean
@Primary
@ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}

At first I thought that at the time when Spring tries to initialize the data source as part of its context initialization, Tomcat hasn't yet initialized its own JNDI resource - so I tried to play with @Lazy and proxies and what not, but this wasn't finally the case.

My private conclusion (have NOT confirmed this) is that @ConfigurationProperties takes the properties literally and overwrites the properties we get from JNDI lookup. The only thing I got working is the following:

@Configuration
@EnableJpaRepositories(basePackages = "com.myproject.repositories.primary",
entityManagerFactoryRef = "primaryEMF",
transactionManagerRef = "primaryTM")
public class JpaPrimaryConfig {
@Value("${spring.datasource.jndi-name}")
String jndiName;
@Bean(destroyMethod = "")
@Primary
public DataSource dataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource ds = dataSourceLookup.getDataSource(jndiName);
return ds;
}
// we have to bind different entities to different data sources - we can do this via entity managers
@Primary
@Bean(name = "primaryEMF")
public LocalContainerEntityManagerFactoryBean primaryEMF(EntityManagerFactoryBuilder builder) {
return builder.dataSource(dataSource()).packages(DummyEntity.class).build();
}
@Primary
@Bean(name = "primaryTM")
public PlatformTransactionManager primaryTM(final @Qualifier("primaryEMF") LocalContainerEntityManagerFactoryBean emf) {
return new JpaTransactionManager(emf.getObject());
}
}

Note that the above is an example; in my working project I didn't have to use @EnableJpaRepositories as the second data source I needed didn't use Spring-specific repository objects.

Another useful thing to check, apart from upping HikariCP log level (logging.level.com.zaxxer=DEBUG), is to make sure your servlet container really has those JNDI resources defined. This can be done via jconsole for a local Tomcat. It's also useful to start Tomcat without any applications deployed to see that it doesn't complain about initial creation of connection pools.

huangapple
  • 本文由 发表于 2020年8月26日 14:40:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63591870.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定