Получать соединение с базой данных в чистом СПД установки
У нас есть приложение JPA (с использованием hibernate), и нам нужно передать вызов устаревшему инструменту отчетности, который нуждается в подключении к базе данных JDBC в качестве параметра. Есть ли простой способ получить доступ к подключению JDBC hibernate имеет настройки?
12 ответов:
где вы хотите получить это соединение неясно. Одной из возможностей было бы получить его из базового 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.
на
hibernatedocs здесь,подключение()
не рекомендуется. (планируется удаление в 4.икс.) Замена зависит от потребности; для делать сразу пользу вещества JDBC doWork (org.зимовать.интерфейс jdbc.Работа. )..
использовать вместо того, чтобы работать с 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 } });
Если вы используете JAVA EE 5.0, лучший способ сделать это-использовать аннотацию @Resource для вставки источника данных в атрибут класса (например, EJB) для хранения ресурса источника данных (например, источника данных Oracle) для устаревшего средства создания отчетов следующим образом:
@Resource(mappedName="jdbc:/OracleDefaultDS") DataSource datasource;позже вы можете получить соединение и передать его в устаревший инструмент отчетов следующим образом:
Connection conn = dataSource.getConnection();
Если вы используете EclipseLink: Вы должны быть в транзакции JPA для доступа к соединению
entityManager.getTransaction().begin(); java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class); ... entityManager.getTransaction().commit();
поскольку код, предложенный @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();
слово чисто не соответствует слову hibernate.
я делюсь своими кодами.
скажем, мы можем определить метод для использования
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)); }
С Hibernate 4 / 5:
asyncronous:
Session session = entityManager.unwrap(Session.class); session.doWork(connection -> doSomeStuffWith(connection));синхронных:
Session session = entityManager.unwrap(Session.class); Connection cnn = session.doReturningWork(c -> c);или один:
Connection cnn = em.unwrap(Session.class).doReturningWork(c -> c);
Hibernate использует ConnectionProvider внутренне для получения соединений. Из спящего режима javadoc:
интерфейс ConnectionProvider не предназначен для доступа к приложению. Вместо этого он используется внутри Hibernate для получения соединений.
более элегантный способ решения этой проблемы - создать пул соединений с базой данных самостоятельно и передать соединения в спящий режим и ваш устаревший инструмент оттуда.
я столкнулся с этой проблемой сегодня, и это был трюк, который я сделал, который работал для меня:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("DAOMANAGER"); EntityManagerem = emf.createEntityManager(); org.hibernate.Session session = ((EntityManagerImpl) em).getSession(); java.sql.Connection connectionObj = session.connection();хотя и не лучший способ, но делает свою работу.
Я использую старую версию 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)и проблема исчезла.
вот фрагмент кода, который работает с Hibernate 4 ответа
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; } }
Ниже приведен код, который работал для меня. Мы используем реализацию 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; } }
Comments