Подтвердить что ты не робот

Получение соединения с базой данных в чистой настройке JPA

У нас есть приложение JPA (с использованием hibernate), и нам нужно передать вызов устаревшему инструменту отчетности, которому необходимо подключение базы данных JDBC в качестве параметра. Есть ли простой способ получить доступ к соединению JDBC, установленному в спящем режиме?

4b9b3361

Ответ 1

Если вы хотите получить это соединение, неясно. Одной из возможностей было бы получить его из базового Hibernate Session, используемого EntityManager. С JPA 1.0 вам нужно будет сделать что-то вроде этого:

Session session = (Session)em.getDelegate();
Connection conn = session.connection();

Обратите внимание, что getDelegate() не является переносимым, результат этого метода специфичен для реализации: приведенный выше код работает в JBoss, для GlassFish вам придется его адаптировать - посмотрите Будьте осторожны при использовании EntityManager.getDelegate().

В JPA 2.0 все немного лучше, и вы можете сделать следующее:

Connection conn = em.unwrap(Session.class).connection();

Если вы работаете внутри контейнера, вы также можете выполнить поиск по настроенному DataSource.

Ответ 2

В соответствии с hibernate docs здесь,

Подключение()

Устаревшие. (планируется удалить в 4.x). Замена зависит от необходимости; для использования JDBC doWork (org.hibernate.jdbc.Work)...

Вместо этого используйте Hibernate Work API:

Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {

    @Override
    public void execute(Connection connection) throws SQLException {
        // do whatever you need to do with the connection
    }
});

Ответ 3

Если вы используете JAVA EE 5.0, лучший способ сделать это - использовать аннотацию @Resource для вставки источника данных в атрибут класса (например, EJB) для хранения ресурса источника данных (например, Oracle datasource) для устаревшего средства отчетности, таким образом:

@Resource(mappedName="jdbc:/OracleDefaultDS") DataSource datasource;

Позже вы можете получить соединение и передать его в устаревший инструмент отчетности таким образом:

Connection conn = dataSource.getConnection();

Ответ 4

если вы используете EclipseLink: Вы должны быть в транзакции JPA для доступа к соединению

entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
...
entityManager.getTransaction().commit();

Ответ 5

Так как код, предложенный @Pascal, устарел, как упоминалось в @Jacob, я нашел этот другой способ, который работает для меня.

import org.hibernate.classic.Session;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.engine.SessionFactoryImplementor;

Session session = (Session) em.getDelegate();
SessionFactoryImplementor sfi = (SessionFactoryImplementor) session.getSessionFactory();
ConnectionProvider cp = sfi.getConnectionProvider();
Connection connection = cp.getConnection();

Ответ 6

Спящий режим 4/5:

Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> doSomeStuffWith(connection));

Ответ 7

Слово "чистый" не соответствует слову "спящий".

Я делюсь своими кодами.

Допустим, мы можем определить метод использования Connection, полученный из EntityManager.

static <R> applyConnection(final EntityManager manager,
                           final Function<Connection, R> function) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (function == null) {
        throw new NullPointerException("function is null");
    }

    // we gonna fill here up

    throw new RuntimeException("failed to work with a connection");
}

EclipseLink

Это довольно просто, как описано в ссылке выше.

  • Обратите внимание, что EntityManager должен быть присоединен к Transaction, иначе метод unwrap вернет null. (Не очень хороший ход.)
  • Я не уверен, что ответственность за закрытие соединения.
// --------------------------------------------------------- EclipseLink
try {
    final Connection connection = manager.unwrap(Connection.class);
    if (connection != null) { // manage is not in any transaction
        return function.apply(connection);
    }
} catch (final PersistenceException pe) {
    logger.log(FINE, pe, () -> "failed to unwrap as a connection");
}

Hibernate

В основном это должно быть сделано с помощью следующих кодов.

// using vendor specific APIs
final Session session = (Session) manager.unwrap(Session.class);
//return session.doReturningWork<R>(function::apply);
return session.doReturningWork(new ReturningWork<R>() {
    @Override public R execute(final Connection connection) {
        return function.apply(connection);
    }
});

Что ж, нам (по крайней мере, мне) может не потребоваться никаких зависимостей от поставщика. Прокси приходит на помощь.

try {
    // See? You shouldn't fire me, ass hole!!!
    final Class<?> sessionClass
            = Class.forName("org.hibernate.Session");
    final Object session = manager.unwrap(sessionClass);
    final Class<?> returningWorkClass
            = Class.forName("org.hibernate.jdbc.ReturningWork");
    final Method executeMethod
            = returningWorkClass.getMethod("execute", Connection.class);
    final Object workProxy = Proxy.newProxyInstance(
            lookup().lookupClass().getClassLoader(),
            new Class[]{returningWorkClass},
            (proxy, method, args) -> {
                if (method.equals(executeMethod)) {
                    final Connection connection = (Connection) args[0];
                    return function.apply(connection);
                }
                return null;
            });
    final Method doReturningWorkMethod = sessionClass.getMethod(
            "doReturningWork", returningWorkClass);
    return (R) doReturningWorkMethod.invoke(session, workProxy);
} catch (final ReflectiveOperationException roe) {
    logger.log(Level.FINE, roe, () -> "failed to work with hibernate");
}

OpenJPA

Я не уверен, что OpenJPA уже использует способ с использованием unwrap(Connection.class), но это можно сделать способом, описанным в одной из приведенных выше ссылок.

Не ясно ответственность за закрытие соединения. Документ (одна из приведенных выше ссылок) явно говорит, но я плохо разбираюсь в английском.

try {
    final Class<?> k = Class.forName(
            "org.apache.openjpa.persistence.OpenJPAEntityManager");
    if (k.isInstance(manager)) {
        final Method m = k.getMethod("getConnection");
        try {
            try (Connection c = (Connection) m.invoke(manager)) {
                return function.apply(c);
            }
        } catch (final SQLException sqle) {
            logger.log(FINE, sqle, () -> "failed to work with openjpa");
        }
    }
} catch (final ReflectiveOperationException roe) {
    logger.log(Level.FINE, roe, () -> "failed to work with openjpa");
}

ПРИЛОЖЕНИЕ

static <U, R> R applyConnection(
        final EntityManager manager,
        final BiFunction<Connection, U, R> function, final U u) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (function == null) {
        throw new NullPointerException("function is null");
    }
    return applyConnection(manager, t -> function.apply(t, u));
}

static void acceptConnection(
        final EntityManager manager, final Consumer<Connection> consumer) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (consumer == null) {
        throw new NullPointerException("consumer is null");
    }
    applyConnection(
            manager,
            t -> {
                consumer.accept(t);
                return null;
            }
    );
}

static <U> void acceptConnection(
        final EntityManager manager,
        final BiConsumer<Connection, U> consumer, final U u) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (consumer == null) {
        throw new NullPointerException("consumer is null");
    }
    acceptConnection(manager, t -> consumer.accept(t, u));
}

Ответ 8

Hibernate использует ConnectionProvider для получения соединений. Из спящего режима javadoc:

Интерфейс ConnectionProvider не предназначен для работы с приложением. Вместо этого он используется внутри Hibernate для получения соединений.

Более элегантным способом решения этой проблемы было бы создать пул соединений с базой данных и вручную подключиться к спящему и вашему устаревшему инструменту.

Ответ 9

Я столкнулся с этой проблемой сегодня, и это был трюк, который я сделал, который работал у меня:

   EntityManagerFactory emf = Persistence.createEntityManagerFactory("DAOMANAGER");
   EntityManagerem = emf.createEntityManager();

   org.hibernate.Session session = ((EntityManagerImpl) em).getSession();
   java.sql.Connection connectionObj = session.connection();

Хотя это не лучший способ, но выполняет эту работу.

Ответ 10

Ниже приведен код, который работал у меня. Мы используем jpa 1.0, реализацию Apache openjpa.

import java.sql.Connection;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;

public final class MsSqlDaoFactory {


       public static final Connection getConnection(final EntityManager entityManager) {
              OpenJPAEntityManager openJPAEntityManager = OpenJPAPersistence.cast(entityManager);
              Connection connection = (Connection) openJPAEntityManager.getConnection();
              return connection;

        }

}

Ответ 11

Я использую старую версию Hibernate (3.3.0) с новейшей версией OpenEJB (4.6.0). Мое решение было:

EntityManagerImpl entityManager = (EntityManagerImpl)em.getDelegate();
Session session = entityManager.getSession();
Connection connection = session.connection();
Statement statement = null;
try {
    statement = connection.createStatement();
    statement.execute(sql);
    connection.commit();
} catch (SQLException e) {
    throw new RuntimeException(e);
}

После этого у меня была ошибка:

Commit can not be set while enrolled in a transaction

Потому что этот код был внутри контроллера EJB (вы не можете commit внутри транзакции). Я аннотировал метод с помощью @TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED), и проблема исчезла.

Ответ 12

Вот фрагмент кода, который работает с Hibernate 4 на основе ответа Dominik

Connection getConnection() {
    Session session = entityManager.unwrap(Session.class);
    MyWork myWork = new MyWork();
    session.doWork(myWork);
    return myWork.getConnection();
}

private static class MyWork implements Work {

    Connection conn;

    @Override
    public void execute(Connection arg0) throws SQLException {
        this.conn = arg0;
    }

    Connection getConnection() {
        return conn;
    }

}