Получить объект 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;
}
}