【发布时间】: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。
我用 A 和 B 在我的本地机器上运行,以及 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