programing

순수 JPA 설정에서 데이터베이스 연결 얻기

nasanasas 2020. 12. 6. 21:38
반응형

순수 JPA 설정에서 데이터베이스 연결 얻기


JPA 애플리케이션 (최대 절전 모드 사용)이 있으며 JDBC 데이터베이스 연결이 필요한 레거시보고 도구에 대한 호출을 매개 변수로 전달해야합니다. 최대 절전 모드가 설정된 JDBC 연결에 액세스하는 간단한 방법이 있습니까?


그 연결을 원하는 위치가 명확하지 않습니다. 하나의 가능성은에 Session의해 사용되는 기본 Hibernate에서 가져 오는 것입니다 EntityManager. JPA 1.0에서는 다음과 같은 작업을 수행해야합니다.

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

(가) 있습니다 getDelegate()이식 할 수 없습니다,이 방법의 결과는 구현 고유의 것입니다 : 위의 코드를 GlassFish에 당신이 그것을 적응해야 할 것, 제이 보스에서 작동 - 한 번 봐 가지고 () EntityManager.getDelegate을 사용하는 동안주의를 .

JPA 2.0에서는 상황이 조금 더 나아졌으며 다음을 수행 할 수 있습니다.

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

컨테이너 내부에서 실행중인 경우 구성된 DataSource.


당으로 hibernate문서 여기 ,

연결 연결 ()

더 이상 사용되지 않습니다 . (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
    }
});

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();

최대 절전 모드 4/5 :

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

단어 순수는 단어에 일치하지 않는 최대 절전 모드 .

내 코드를 공유하고 있습니다.

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");
}

최대 절전 모드

기본적으로 다음 코드로 수행해야합니다.

// 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는 연결을 얻기 위해 내부적으로 ConnectionProvider를 사용합니다. 최대 절전 모드 javadoc에서 :

ConnectionProvider 인터페이스는 애플리케이션에 노출되지 않습니다. 대신 연결을 얻기 위해 Hibernate에서 내부적으로 사용됩니다.

The more elegant way of solving this would be to create a database connection pool yourself and hand connections to hibernate and your legacy tool from there.


I ran into this problem today and this was the trick I did, which worked for me:

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

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

Though not the best way but does the job.


Below is the code that worked for me. We use jpa 1.0, Apache openjpa implementation.

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;

        }

}

I'm using a old version of Hibernate (3.3.0) with a newest version of OpenEJB (4.6.0). My solution was:

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);
}

I had an error after that:

Commit can not be set while enrolled in a transaction

Because this code above was inside a EJB Controller (you can't commit inside a transaction). I annotated the method with @TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) and the problem was gone.


Here is a code snippet that works with Hibernate 4 based on Dominik's answer

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;
    }

}

참고URL : https://stackoverflow.com/questions/3493495/getting-database-connection-in-pure-jpa-setup

반응형