Hibernate подключается к DB2 при запуске приложения в WAS Liberty на CICS

Мы запускаем простое веб-приложение на WebSphere Liberty, которое использует Hibernate в качестве поставщика постоянных данных (включен в качестве библиотеки в файле WAR).

Когда приложение запускается, Hibernate инициализируется, и он открывает соединение с DB2 и выдает некоторые операторы SQL. Однако это не работает при работе в CICS и использовании источника данных драйвера JDBC типа 2. Следующие сообщения регистрируются (некоторые дополнительные разрывы строк для удобства чтения):

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 типа 2 только некоторые потоки могут открывать соединение DB2. Это были бы потоки приложений (те, которые обрабатывают запросы HTTP), а также обслуживание рабочих потоков. CICSExecutorService,

Текущее решение:

  1. Отключить поиск метаданных JDBC в JdbcEnvironmentInitiator установив hibernate.temp.use_jdbc_metadata_defaults собственность наfalse
  2. Завершение исполнения IdTableHelper#executeIdTableCreationStatements в Runnable и отправить его CICSExecutorService,

Считаете ли вы это решение достаточным и пригодным для производства? Или, может быть, вы используете другой подход?

Используемые версии:

  • Сервер транзакций CICS для z / OS 5.3.0
  • WebSphere Application Server 8.5.5.8
  • Hibernate 5.1.0

Обновление: просто чтобы уточнить, что после запуска нашего приложения оно может без проблем запрашивать DB2 (при обслуживании HTTP-запросов). Проблема связана только с запуском.

2 ответа

Решение

Следующее решение было протестировано, чтобы работать нормально.

Идея состоит в том, чтобы выполнить операторы SQL/DDL, используя CICSExecutorService#runAsCICS, Следующее расширение зарегистрировано через 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 метод принятия Callable, что может быть полезно для упрощения ветки CICS execute метод что-то вроде этого:

CICSExecutorService.runAsCICS(() -> { runnable.run(); return null; }).get();

Несколько альтернативных вариантов:

  1. Завершение только действия по установлению соединения (org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl#getConnection) не работал, так как соединение было закрыто уже при использовании в основном потоке.
  2. Завершение запуска всего приложения (org.springframework.web.context.ContextLoaderListener#contextInitialized) привело к проблемам с загрузкой классов.

Изменить: в конечном итоге пошел с пользовательским Hibernate's MultiTableBulkIdStrategy реализация, которая не запускает SQL/DDL при запуске ( см. страницу проекта на GitHub).

Поддержка CICS TS v5.3 для функции JPA в Liberty была недавно доступна в обновлении службы (июль 2016 г.). До этого обновления попытка запустить JPA в приложениях привела бы к проблемам, аналогичным тем, которые вы описали.

Хотя вы работаете в режиме гибернации и находитесь в потоке с поддержкой CICS, у него нет среды API (что позволит успешно выполнять вызов JDBC типа 2). Новая логика обнаружения была разработана специально (но не исключительно) для использования с драйвером DB2 JDBC типа 2 и JPA. Это обновление было добавлено в недавнем обновлении службы и может устранить проблемы, с которыми вы столкнулись.

Попробуйте подать заявку: http://www-01.ibm.com/support/docview.wss?crawler=1&uid=swg1PI58375

В описании говорится, что оно предназначено для поддержки "стандартного режима Liberty", но оно содержит и другие разработки, описанные выше.

Другие вопросы по тегам