Получить объект JDBC-соединения из компонента без состояния

В сеансе без гражданства EntityManager вводится, но я хотел бы получить Connection объект для вызова процедуры БД. Есть ли какое-то решение этого?

7 ответов

Решение

Это будет специфический код JPA-провайдера. Обычно это делается путем вызова unwrap() на EntityManager учебный класс.

Если вы используете EclipseLink, будет полезен следующий код (из вики EclipseLink) (в случае, если вы используете EntityManager, управляемый приложением):

JPA 2.0

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

JPA 1.0

entityManager.getTransaction().begin();
UnitOfWork unitOfWork = (UnitOfWork)((JpaEntityManager)entityManager.getDelegate()).getActiveSession();
unitOfWork.beginEarlyTransaction();
Accessor accessor = unitOfWork.getAccessor();
accessor.incrementCallCount(unitOfWork.getParent());
accessor.decrementCallCount();
java.sql.Connection connection = accessor.getConnection();
...
entityManager.getTransaction().commit();

Обратите внимание, что решение, предоставленное для JPA 2.0, завершится с ошибкой для Hibernate 3.6.5 с PersistenceException содержащий сообщение

Hibernate не может развернуть интерфейс java.sql.Connection

Используйте код, предоставленный Skaffman, чтобы заставить его работать против Hibernate (проверено на работу в соответствии с 3.6.5 даже для контекстов персистентности, управляемых контейнером).

Тем не менее, вики EclipseLink указывает на один полезный бит информации - если вы используете управляемые JTA источники данных, вы должны вводить его, используя @Resource аннотирование или извлечение его с помощью поиска JNDI. Пока вам нужно выполнить транзакционную работу с базой данных, неважно, получаете ли вы новое соединение из источника данных или существующее; в любом случае большинство пулов соединений будут обеспечивать такое же соединение, которое связано с текущим потоком (т. е. тем, которое уже используется менеджером сущностей). Таким образом, вы бы избегали развертывания менеджера сущностей таким способом, а также выполняли бы транзакционную деятельность с базой данных; помните, что кеш контекстного хранения и кэш второго уровня могут не синхронизироваться, если вы сделаете это.

В Hibernate решение, опубликованное skaffman, привело к следующему сообщению об ошибке:

Hibernate не может развернуть класс org.hsqldb.Session

Я заставил его работать, используя SessionImpl, а не Session:

Connection connection = entityManager().unwrap(SessionImpl.class).connection();

Пример решения проблемы с использованием Session.doWork() выглядит следующим образом:

private void executeNative(final String query) {
    Session session = entityManager.unwrap(Session.class);
    session.doWork(new Work() {

        @Override
        public void execute(Connection connection) throws SQLException {
            Statement s = null;
            try {
                s = connection.createStatement();
                s.executeUpdate(query);
            } 
            finally {
                if (s != null) {
                    s.close();
                }
            }
        }

    });
}

Кажется, что сам JPA API не предлагает этого, что неудивительно, но если вы хотите связать свой код с конкретной реализацией, то вы можете использовать что-то вроде этого (Hibernate):

Session hibernateSession = entityManager.unwrap(Session.class);
Connection jdbcConnection = hibernateSession.connection(); 

Обратите внимание, что Session.connection() устарела для удаления в Hibernate 4. Рассмотрите возможность использования Session.doWork() вместо.

Вы должны взять базовый делегат, используя entitymanager.getDelegate() или же entitymanager.unwrap(что является лучшим способом), приведите его к конкретной реализации (в Hibernate это называется Session). Тогда вы можете позвонить connection() метод. Имейте в виду, что это устарело, используйте Work класс вместо. Узнайте больше здесь.

В JPA2.0, если необходимо, JDBC является номинантом DTO или объектом для более сложного запроса. Иногда JPA не все...

Я надеюсь, что это поможет вам:

Statement statement = null;
EntityManager em = null;
em = emf.createEntityManager();
EntityTransaction et = em.getTransaction();

if(!et.isActive()) {
    et.begin();
}

java.sql.Connection connection = em.unwrap(java.sql.Connection.class);

    String qquerry="SELE ...
    try {   
        statement = connection.createStatement();
        ResultSet rs =  statement.executeQuery(qquerry);                                                              

        if (!rs.next()) {
            return null;
        }
        else{
            wwwwas=rs.getString(4);                                 
        }         
        statement.close();
        } 
    catch (SQLException e) {
        System.out.println("\n b-03:"+e);
        throw new RuntimeException(e.getMessage(), e);
    } 
    finally {
        try {
            //  em.getTransaction().commit();
            if(connection != null )
            connection.close();
        } 
        catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
       }
    }

Это работает потрясающе, и вы можете использовать объект подключения в другом месте, если это необходимо

SessionImpl sessionImpl = (SessionImpl) session;
Connection conn = sessionImpl.connection();

куда session это имя объекта Hibernate Session

Ниже приведен код, который работал для меня. Мы используем 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;

    }

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