我们在WebSphere Liberty上运行一个简单的webapp,它使用Hibernate作为持久性提供程序(作为WAR文件中的库包含在内)。
当应用程序启动时,Hibernate被初始化,它将打开与DB2的连接并发出一些SQL语句。但是,在CICS上运行并使用JDBC Type 2 Driver DataSource时,这会失败。记录以下消息(为了便于阅读,还有一些额外的换行符):
WARN org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator -
HHH000342: Could not obtain connection to query metadata : [jcc][50053][12310][4.19.56]
T2zOS exception: [jcc][T2zos]T2zosCicsApi.checkApiStatus:
Thread is not CICS-DB2 compatible: CICS_REGION_BUT_API_DISALLOWED ERRORCODE=-4228, SQLSTATE=null
...
ERROR org.hibernate.hql.spi.id.IdTableHelper - Unable obtain JDBC Connection
com.ibm.db2.jcc.am.SqlException: [jcc][50053][12310][4.19.56] T2zOS exception: [jcc][T2zos]T2zosCicsApi.checkApiStatus:
Thread is not CICS-DB2 compatible: CICS_REGION_BUT_API_DISALLOWED ERRORCODE=-4228, SQLSTATE=null
at com.ibm.db2.jcc.am.kd.a(Unknown Source) ~[db2jcc4.jar:?]
...
at com.ibm.db2.jcc.t2zos.T2zosConnection.a(Unknown Source) ~[db2jcc4.jar:?]
...
at com.ibm.db2.jcc.DB2SimpleDataSource.getConnection(Unknown Source) ~[db2jcc4.jar:?]
at com.ibm.cics.wlp.jdbc.internal.CICSDataSource.getConnection(CICSDataSource.java:176) ~[?:?]
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) ~[our-app.war:5.1.0.Final]
at org.hibernate.internal.SessionFactoryImpl$3.obtainConnection(SessionFactoryImpl.java:643) ~[our-app.war:5.1.0.Final]
at org.hibernate.hql.spi.id.IdTableHelper.executeIdTableCreationStatements(IdTableHelper.java:67) [our-app.war:5.1.0.Final]
at org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy.finishPreparation(GlobalTemporaryTableBulkIdStrategy.java:125) [our-app.war:5.1.0.Final]
at org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy.finishPreparation(GlobalTemporaryTableBulkIdStrategy.java:42) [our-app.war:5.1.0.Final]
at org.hibernate.hql.spi.id.AbstractMultiTableBulkIdStrategyImpl.prepare(AbstractMultiTableBulkIdStrategyImpl.java:88) [our-app.war:5.1.0.Final]
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:451) [our-app.war:5.1.0.Final]
我目前的理解是,当在CICS上运行并使用JDBC Type 2驱动程序时,只有一些线程能够打开DB2连接。这将是应用程序线程(处理HTTP请求的线程)以及服务CICSExecutorService
的工作线程。
目前的解决方案是:
JdbcEnvironmentInitiator
中禁用JDBC元数据查找
将hibernate.temp.use_jdbc_metadata_defaults
属性设置为
false
IdTableHelper#executeIdTableCreationStatements
中包含Runnable
的执行情况并将其提交至CICSExecutorService
。您认为此解决方案是否足够且适合生产?或者你可能会使用一些不同的方法?
使用的版本:
更新:只是为了澄清一下,一旦我们的应用程序启动,它就可以毫无问题地查询DB2(在处理HTTP请求时)。问题只与启动有关。
答案 0 :(得分:0)
最近,在服务更新(2016年7月)中提供了对Liberty中JPA功能的CICS TS v5.3支持。在更新之前,尝试在应用程序中运行JPA会导致与您描述的问题非常相似的问题。
虽然您正在运行hibernate并且您在启用CICS的线程上,但它没有API环境(这将允许类型2 JDBC调用成功)。新的检测逻辑是专门(但不是唯一)开发的,用于DB2 JDBC type 2驱动程序和JPA。此更新是在最近的服务更新中提供的,可能会解决您遇到的问题。
尝试申请: http://www-01.ibm.com/support/docview.wss?crawler=1&uid=swg1PI58375
说明书是针对“标准模式Liberty”支持,但它包含上述其他发展。
答案 1 :(得分:0)
以下解决方案已经过测试,可以正常使用。
我们的想法是使用CICSExecutorService#runAsCICS
执行SQL / DDL语句。以下扩展名通过hibernate.hql.bulk_id_strategy
属性注册。
package org.hibernate.hql.spi.id.global;
import java.util.concurrent.*;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.springframework.util.ClassUtils;
import com.ibm.cics.server.*;
public class CicsAwareGlobalTemporaryTableBulkIdStrategy extends GlobalTemporaryTableBulkIdStrategy {
@Override
protected void finishPreparation(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess, MetadataImplementor metadata, PreparationContextImpl context) {
execute(() -> super.finishPreparation(jdbcServices, connectionAccess, metadata, context));
}
@Override
public void release(JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess) {
execute(() -> super.release(jdbcServices, connectionAccess));
}
private void execute(Runnable runnable) {
if (isCics() && IsCICS.getApiStatus() == IsCICS.CICS_REGION_BUT_API_DISALLOWED) {
RunnableFuture<Void> task = new FutureTask<>(runnable, null);
CICSExecutorService.runAsCICS(task);
try {
task.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException("Failed to execute in a CICS API-enabled thread. " + e.getMessage(), e);
}
} else {
runnable.run();
}
}
private boolean isCics() {
return ClassUtils.isPresent("com.ibm.cics.server.CICSExecutorService", null);
}
}
请注意,较新的JCICS API版本具有接受runAsCics
的{{1}}方法的重叠,这可能有助于将Callable
方法的CICS分支简化为以下内容:
execute
尝试了一些替代方案:
CICSExecutorService.runAsCICS(() -> { runnable.run(); return null; }).get();
)的包装不起作用,因为在主线程中使用连接时连接已经关闭。org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl#getConnection
)导致了类加载问题。 编辑最终使用了自定义Hibernate的org.springframework.web.context.ContextLoaderListener#contextInitialized
实现,该实现在启动时不运行任何SQL / DDL(see project page on GitHub)。