【问题标题】:Connection to Oracle via TNS is not working通过 TNS 连接到 Oracle 不起作用
【发布时间】:2017-05-16 10:16:52
【问题描述】:

我有一个 Spring Boot 应用程序,当它以经典方式连接到 Oracle 实例时可以顺利运行:

jdbc:oracle:thin:@<server name>:<port num>/<service name>

但是,当我通过存储在本地驱动器“C:\ORACLE\Client11g\network\admin”中的 tnsnames.ora 转到 tns 连接时,某些东西不起作用,我得到了这个异常:

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:267)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:231)
    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:240)
    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:858)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:885)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:373)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:362)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1642)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1579)
    ... 36 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:257)
    ... 52 more

10:37:32,201 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 381) MSC000001: Failed to start service jboss.undertow.deployment.default-server.default-host./my_service: org.jboss.msc.service.StartException in service jboss.undertow.deployment.default-server.default-host./my_service: java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [app/persistence/OracleConfiguration.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.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:85)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
    at org.jboss.threads.JBossThread.run(JBossThread.java:320)
Caused by: java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [app/persistence/OracleConfiguration.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 io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:236)
    at org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:100)
    at org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:82)
    ... 6 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [app/persistence/OracleConfiguration.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:1583)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:856)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    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:86)
    at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
    at io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:186)
    at io.undertow.servlet.core.DeploymentManagerImpl$1.call(DeploymentManagerImpl.java:171)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
    at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
    at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
    at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
    at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
    at io.undertow.servlet.api.LegacyThreadSetupActionWrapper$1.call(LegacyThreadSetupActionWrapper.java:44)
    at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:234)
    ... 8 more

以下是我的 application.properties + application-oracleprod.properties 文件。请记住,当我使用前面所说的 SID 连接时,这种方法有效。

application.properties:

spring.profiles.active=oracleprod

spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.jpa.hibernate.ddl-auto=validate
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
hibernate.generate_statistics=true

application-oracleprod.properties(这里的魔法应该发生,因为这个属性“oracle.net.tns_admin”设置了“tnsnames.ora”文件夹加上链接“@MY_SERVICE”,这显然存在于“tnsnames.properties”中。 ora" 文件,但我真的看不到 Oracle 在后台做了什么):

spring.datasource.driver-class-name=oracle.jdbc.OracleDriver

#/tnsnames.ora
oracle.net.tns_admin=C:/ORACLE/Client11g/network/admin

oracle.username=my_user
oracle.password=xyz
oracle.url=jdbc:oracle:thin:@MY_SERVICE

Configuration 类简洁明了:

@Configuration
@ConfigurationProperties(prefix="oracle" )
public class OracleConfiguration {

    @NotNull
    private String username;

    @NotNull
    private String password;

    @NotNull
    private String url;

    /*@Value("${oracle.net.tns_admin}")
    private String tns;*/

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    /*public void setTns(String tns) {
        this.tns = tns;
    }*/

    @Bean
    DataSource dataSource() throws SQLException {
        OracleDataSource dataSource = new OracleDataSource(); // we use oracle data source API but it could be mysql
        dataSource.setURL(this.url);
        dataSource.setUser(this.username);
        dataSource.setPassword(this.password);
        dataSource.setImplicitCachingEnabled(true);
        dataSource.setFastConnectionFailoverEnabled(true);
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        try {
            em.setDataSource(dataSource());
        } catch (SQLException e) {
            e.printStackTrace();
        }

        em.setPackagesToScan(new String[] { "app.persistence.entity" }); // put the name of entity classes package 

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);

        //em.setJpaProperties(additionalProperties()); // this properties are set by "application-oracle.properties" file

        return em;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {

        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }

总而言之,我遵循了 Oracle 文档中建议的方法并结合了 Spring Boot 配置文件/类,但没有运气。

我也尝试过不同的路径格式:

C:/ORACLE/Client11g/network/admin/tnsnames.ora
C:\ORACLE\Client11g\network\admin
C:\\ORACLE\\Client11g\\network\\admin

我认为我做得很好,因为有了 SID 连接,一切都很完美,但可能缺少一些隐藏的东西。

更新

事实证明 db url 不正确,至少对于 TNS 连接是不正确的。问题是我们有多个主机,而不仅仅是一个主机:

    MY_SERVICE =
  (DESCRIPTION_LIST =
    (LOAD_BALANCE = off)
    (FAILOVER = on)
    (DESCRIPTION =
      (CONNECT_TIMEOUT = 5)
      (TRANSPORT_CONNECT_TIMEOUT = 3)
      (RETRY_COUNT = 3)
      (ADDRESS_LIST =
        (LOAD_BALANCE = on)
        (ADDRESS = (PROTOCOL = TCP)(HOST = <ip_1> )(PORT = 1621))
        (ADDRESS = (PROTOCOL = TCP)(HOST = <ip_2> )(PORT = 1621))
      )
      (CONNECT_DATA =
        (SERVICE_NAME = <my_service_name>)
      )
    )
    (DESCRIPTION =
      (CONNECT_TIMEOUT = 5)
      (TRANSPORT_CONNECT_TIMEOUT = 3)
      (RETRY_COUNT = 3)
      (ADDRESS_LIST =
        (LOAD_BALANCE = on)
        (ADDRESS = (PROTOCOL = TCP)(HOST = <ip_3> )(PORT = 1621))
        (ADDRESS = (PROTOCOL = TCP)(HOST = <ip_4> )(PORT = 1621))
      )
      (CONNECT_DATA =
        (SERVICE_NAME = <my_service_name> )
      )
    )
  )

我得到了:

 javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
    Caused by: java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
    Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
    Caused by: org.hibernate.exception.JDBCConnectionException: Unable to open JDBC Connection for DDL execution
    Caused by: java.sql.SQLRecoverableException: Errore di I/O: Unknown host specified 
    Caused by: oracle.net.ns.NetException: Unknown host specified "

再次,我有点困惑如何告诉 Spring 如何读取此 TNS 配置。

有什么想法吗?

【问题讨论】:

  • 你在这里放了很多代码,任何人都很难第一次理解你的问题然后回答它。请在提问时具体准确。在发布问题之前请查看How to AskMinimal, Complete, and Verifiable example
  • 您正在将 spring.jpa.database-platform 设置为 Oracle 方言,您是否也尝试过设置 hibernate-property?(按照异常中的要求)
  • 所以你在application.properties 中设置属性用于自动配置。接下来创建您自己的配置,禁用自动配置,并发现属性未应用很奇怪。简而言之,您的application.properties 中的任何属性都不会被使用/应用。问题出现了,为什么你甚至有这个配置类?你真的不需要它,因为 Spring Boot 可以为你配置所有这些,开箱即用。
  • @GeekyNinja 省略该代码不会显示完整且可验证的示例
  • @Turo 我不明白你指的是哪个属性。你能解释得更好吗?

标签: oracle spring-boot tnsnames tns


【解决方案1】:

在您的课程中,您基本上禁用了自动配置,因此大部分 application.properties 无用。

您的OracleConfiguration 中没有任何内容不是(或可以)自动为您配置的。

注意:我假设有两件事,首先您使用 HikariCP 作为连接池,其次您使用 Spring Boot 1.4.x。

所以对于初学者来说,删除OracleConfiguration 类并相应地修复application.properties

# DataSource
spring.datasource.type=com.zaxxer.hikari.HikariDataSource

#JPA   
spring.jpa.hibernate.ddl-auto=validate

#Hibernate
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.generate_statistics=true

您不需要spring.datasource.driver-class-name,因为它是从 URL 派生的。

在您的application-oracle.properties(或您使用的任何配置文件名称)中设置以下内容

# DataSource
spring.datasource.username=my_user
spring.datasource.password=xyz
spring.datasource.url=jdbc:oracle:thin:@MY_SERVICE

spring.datasource.hikari.dataSourceClassName=oracle.jdbc.pool.OracleDataSource
spring.datasource.hikari.dataSourceProperties.implicitCachingEnabled=true
spring.datasource.hikari.dataSourceProperties.fastConnectionFailoverEnabled=true

#JPA   
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect

这会配置 oracle 特定的 JDBC 属性。

需要spring.datasource.hikari.dataSourceProperties 来设置OracleDataSource 中的附加属性。

这应该允许 Spring Boot 自动创建 DataSource 和适当的 EntityManagerFactory,而您根本不需要创建自定义配置类。

更新(没有 Hikari 和普通 OracleDataSource 的配置)

如果您想使用普通的OracleDataSource 而不是连接池,您可以从默认的application.properties 中删除spring.datasource.type,并将以下内容添加到application-oracle.properties

# DataSource
spring.datasource.type=oracle.jdbc.pool.OracleDataSource
spring.datasource.username=my_user
spring.datasource.password=xyz
spring.datasource.url=jdbc:oracle:thin:@MY_SERVICE

这与您手动配置的结果或多或少相同。

更新 #2

由于您想使用 TNS,您还需要设置一个名为 oracle.net.tns_admin 的系统属性。 (见this question)。

所以在你的应用初始化器中(如果你的应用服务器上没有设置)你需要手动设置它。

private static void determineAndSetTnsHome() {
    String tnsAdmin = System.getenv("TNS_ADMIN");
    if (tnsAdmin == null) {
        String oracleHome = System.getenv("ORACLE_HOME");
        if (oracleHome == null) {
            return; //failed to find any useful env variables            
        }
        tnsAdmin = oracleHome + File.separatorChar + "network" + File.separatorChar + "admin";
    }
    System.setProperty("oracle.net.tns_admin", tnsAdmin);
}

从您的mainonStartupconfigure 方法调用此方法。这假设您在环境中设置了 ORACLE_HOMETNS_ADMIN 变量。

【讨论】:

  • 谢谢。它实际上适用于 SID 连接。但是我仍然面临 TNS 连接的问题。此时我想知道配置类的用途。是不是不依赖.properties文件手动配置数据源(在代码中指定连接条件)?
  • 你不需要配置类。 (您在评论中的问题是另一个问题)。我非常怀疑,使用这个配置文件,你会得到同样的异常(如果你已经删除了配置类)。
  • 是的,例外情况不同。我已经更新了主要问题。 TNS 连接的问题仍然存在,但我当然误解了数据源配置。
  • 你拯救了我的一天。我想知道为什么在 .properties 文件中添加属性“oracle.net.tns_admin”不起作用。它肯定会更清洁和高效。我将进一步研究一种方法来做到这一点,而无需在我的 Spring Boot“Application.java”中添加额外的代码。原因是我想要两个 oracle 配置,以便根据开发/生产环境从一个切换到另一个。
  • 只需为此使用配置文件并为application-[profile].properties 添加两个属性文件。简单地将其添加到属性文件中是行不通的,因为 Oracle 驱动程序显然是从系统环境中读取该属性的。 Oracle 驱动程序现在(也不应该)关于 spring。您可以创建一个小配置,将特定属性添加为系统属性(而不是您现在拥有的)。
【解决方案2】:

实际上是蹩脚的方法, 但效果很好

把它放在你的 Main 类中。

    System.setProperty("oracle.net.tns_admin",
            "<Path of tnsnames.ora>");

在你的属性文件中使用它

spring.datasource.url=jdbc:oracle:thin:@ORCLPDB

我还在挖掘更多。会在这里更新

【讨论】:

    【解决方案3】:

    出于某种原因,对我来说只有工作:

    spring.datasource.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)(HOST=TODO)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=TODO)))
    

    而不是spring.datasource.url=jdbc:oracle:thin:@MY_SERVICE.

    另外,我正在调用用户 M. Deinum 在接受的答案中建议的方法 determineAndSetTnsHome():

    public static void main(String[] args) {
            SpringApplication.run(EvaApplication.class, args);
            determineAndSetTnsHome();
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-17
      • 1970-01-01
      • 2013-11-01
      • 1970-01-01
      • 2023-01-04
      • 1970-01-01
      相关资源
      最近更新 更多