【问题标题】:JDBC connection lost during task in Tomcat ServerTomcat 服务器中的任务期间 JDBC 连接丢失
【发布时间】:2018-04-26 15:07:59
【问题描述】:

我的结构:

  • 身份验证服务器,使用 Keycloak
  • 一个 Tomcat 8 服务器
  • 一个应用程序(称为 A),为所有其他 WebApps 提供公共服务,位于 tomcat 8 服务器上
  • 其他网络应用
  • MySQL 服务器数据库,具有不同的 w/r 架构。
  • 可读的 SQL Server

我所有的 webApp 都是在 Spring Boot 中使用 Java 8 开发的。

案例:
在其中一个指定的应用程序(称为 B)中,我调用了 A 的 API。 此 API 打开与 SQL Server 的连接,用于读取条目并将读取条目的值发送给 B

我用 AB 在我的本地机器上运行,以及 A 官方和 B 本地测试它.

在测试中我没有任何问题,API 工作正常。

但是当我从官方调用这个 API 到官方时,几秒钟后我的日志中出现了一些错误,并且我失去了与数据库的所有连接:

Forwarding to error page from request [/mycontroller/request] due to exception 
[org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5ec9a879 has been closed already]


java.lang.IllegalStateException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5ec9a879 has been closed already

或:

EntityManager is closed

出现此错误后,我只能重新启动 Tomcat 服务器以恢复与数据库的连接。

这是我的代码,称为 API:

 @Override
            public void apiCalled() {
            try {
                for(Entity c : entityRepository.findEntity()) {
                    String ftt = keycloakRestTemplate.getForEntity(URI.create(mux+"/api/apiCalled?num="+c.getNumber()), String.class).getBody();
                    if(ftt == null) {
                        continue;
                    }
                    FttDto f = new ObjectMapper().readValue(ftt, FttDto.class);

                    c.setNumberF(f.getNumberF());
                    c.setDateF(convertDate(f.getDateF()));
                    commessaRepository.save(c);
                }
            } catch (RestClientException | IOException e) {
                LOG.error("Api Service Get error: {}", e.getMessage());
            }
        }

API 代码:

@PersistenceContext(unitName="persistenceUnitI24")
private EntityManager emI24;

public FttDto findByNumber(String number) {
    Session session = emI24.unwrap(Session.class);
    FttI24 fttListI24 = (FttI24) session.createQuery("select f from FttRg r join r.idFttI24 f join r.nota n where n.nota like '%"+number+"%'")
     .setCacheMode(CacheMode.IGNORE)
     .uniqueResult();

    if(fttListI24 == null) {
        return null;
    }

    FttDto ftt = new FttDto();
    ftt.setNumberF(fttListI24.getNumberF());
    ftt.setDateF(fttListI24.getDateF());
    return ftt;
}

有什么想法吗?

编辑

这是用于我的数据库连接的 server.xml:

        <Resource 
          name="jdbc/schemaA"
          auth="Container" 
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          type="javax.sql.DataSource"
          initialSize="2"
          maxActive="4"
          maxIdle="2" 
          minIdle="1" 
          username="user" 
          password="password" 
          driverClassName="net.sourceforge.jtds.jdbc.Driver" 
          url="jdbc:jtds:sqlserver://sqlServer_ip/schemaA" 
          testOnBorrow="true"
          testWhileIdle="true"
          validationQuery="select 1"
          validationInterval="300000"/>

    <Resource 
          name="jdbc/schemaB"
          auth="Container" 
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          type="javax.sql.DataSource" 
          initialSize="4"
          maxActive="8"
          maxWait="10000"
          maxIdle="8"
          minIdle="4"
          username="userB"
          password="passB" 
          driverClassName="com.mysql.jdbc.Driver" 
          url="jdbc:mysql://mysqlserver_ip/schemaB?zeroDateTimeBehavior=convertToNull" 
          testOnBorrow="true" 
          testWhileIdle="true" 
          validationQuery="select 1"
          validationInterval="300000" />

在 spring 中添加 DataSource Config。

在 WebApp B上:

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
@Bean(name = "dataSource")
@ConfigurationProperties(prefix="spring.datasource")
@Primary    
public DataSource dataSource() throws NamingException {

    if(Arrays.asList(environment.getActiveProfiles()).contains("dev")) {
        return new BasicDataSource();
    }else {
        Context ctxConfig = new InitialContext();
        return (DataSource) ctxConfig.lookup("java:comp/env/jdbc/schemaB");
    }
}

@Bean
@Primary
public JpaTransactionManager transactionManager() throws NamingException {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());

    return transactionManager;
}

@Bean
public JpaVendorAdapter jpaVendorAdapter(){

    HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();

    jpaVendorAdapter.setDatabase(Database.MYSQL);
    jpaVendorAdapter.setGenerateDdl(true);
    jpaVendorAdapter.setShowSql(false);
    jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQLInnoDBDialect");

    return jpaVendorAdapter;
}

@Bean
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();

    entityManagerFactoryBean.setDataSource(dataSource());
    entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
    entityManagerFactoryBean.setPackagesToScan("domain.mydomain");
    entityManagerFactoryBean.setPersistenceUnitName("persistenceUnit");

return entityManagerFactoryBean;
}
}

在 WebApp A

 @Configuration
public class DataSourceI24Config {

    @Autowired
    private Environment environment;

    @Bean(name = "dataSourceI24")
    @ConfigurationProperties(prefix = "spring.datasource.i24")
    public DataSource dataSourceI24() throws NamingException {
            Context ctxConfig = new InitialContext();
            return (DataSource) ctxConfig.lookup("java:comp/env/jdbc/schemaA");

    }

    @Bean(name="transactionManagerI24")
    public JpaTransactionManager transactionManagerI24() throws NamingException {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryI24().getObject());
        return transactionManager;
    }

    @Bean(name="entityManagerI24")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryI24() throws NamingException {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();

        entityManagerFactoryBean.setDataSource(dataSourceI24());
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPackagesToScan("appA.i24.domain");
        entityManagerFactoryBean.setPersistenceUnitName("persistenceUnitI24");

       [...]

        return entityManagerFactoryBean;
    }
}

【问题讨论】:

  • 提到AnnotationConfigEmbeddedWebApplicationContext 已关闭的错误表明您(或至少:某事)正在关闭/停止弹簧上下文。这是一个比数据库连接问题更广泛的问题(可能这些只是弹簧上下文被关闭的症状(结果))。你真的需要发一个minimal reproducible example

标签: java spring-boot scheduler tomcat8 entitymanager


【解决方案1】:

我在使用 h2 数据库时遇到了同样的问题。解决方案是在多连接模式下使用 db(多于一个可能的连接,连接池)。

【讨论】:

  • 如果你没有在多线程应用程序中使用连接池,那你就是自找麻烦。使用连接池将大大提高应用程序的性能和可靠性。
  • @Jackkobec 我添加了 server.xml 配置以连接到我的数据库,看看是否可以帮助你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-22
  • 1970-01-01
  • 1970-01-01
  • 2020-12-05
  • 2012-11-15
  • 1970-01-01
  • 2020-07-11
相关资源
最近更新 更多