Как программно внедрить контекст постоянства в другой источник данных - PullRequest
6 голосов
/ 24 февраля 2011

В стандартном EJB 3 при внедрении диспетчера сущностей единица персистентности (которая относится к источнику данных) жестко закодирована в аннотацию: (или, альтернативно, файл XML)

@PersistenceContext(unitName = "myunit")
private EntityManager entityManager;

Есть ли способ использовать диспетчер сущностейно выбрать источник данных по имени во время выполнения?

Ответы [ 4 ]

5 голосов
/ 16 февраля 2012

Используя EclipseLink, вы можете установить источник данных, настроенный на вашем сервере приложений.

import org.eclipse.persistence.config.PersistenceUnitProperties;
...


....
Map props = new HashMap();  
props.put(PersistenceUnitProperties.JTA_DATASOURCE, "dataSource");  
EntityManagerFactory  emf = Persistence.createEntityManagerFactory("UNIT_NAME", props);
EntityManager em = emf.createEntityManager();

PU_NAME относится к имени, используемому в файле persistence.xml
dataSource относится к имени, используемому в приложениисервер для ресурса jdbc как "jdbc / sample"

2 голосов
/ 07 ноября 2013

Это возможно! Я сделал это, и он работает под JBoss AS и WebSphere.

Я использую пользовательский поставщик сохраняемости, который расширяет org.hibernate.ejb.HibernatePersistence (вам нужно изменить private static final поле , чтобы установить имя поставщика сохраняемости на org.hibernate.ejb3.Ejb3Configuration.IMPLEMENTATION_NAME: это своего рода черная магия, но работает). Убедитесь, что в ваших единицах постоянства persistence.xml пользовательский поставщик установлен в теге <provider>, а ваш пользовательский поставщик зарегистрирован в META-INF/services/javax.persistence.spi.PersistenceProvider.

Мой провайдер переопределяет метод createContainerEntityManagerFactory(PersistenceUnitInfo,Map), называемый контейнером Java EE, как таковой (для источника данных JTA, но было бы легко сделать это и для источника данных не JTA):

@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {

    // load the DataSource
    String newDataSourceName = ...; // any name you want
    DataSource ds = (DataSource)(new InitialContext().lookup(newDataSourceName));

    // modify the datasource
    try {
        try {
            // JBoss implementation (any maybe other Java EE vendors except IBM WebSphere)
            Method m = info.getClass().getDeclaredMethod("setJtaDataSource", DataSource.class);
            m.setAccessible(true);
            m.invoke(info, ds);

        } catch (NoSuchMethodException e) {
            // method does not exist (WebSphere?) => try the WebSphere way

            // set the datasource name
            Method m = info.getClass().getDeclaredMethod("setJtaDataSource", String.class);
            m.setAccessible(true);
            m.invoke(info, newDataSourceName);

            // do the lookup
            Method m2 = info.getClass().getDeclaredMethod("lookupJtaDataSource", String.class);
            m2.setAccessible(true);
            m2.invoke(info);
        }
    } catch (Throwable e) {
        throw new RuntimeException("could not change DataSource for "+info.getClass().getName());
    }

    // delegate the EMF creation
    return new HibernatePersistence().createContainerEntityManaferFactory(info, map);
}

createEntityManagerFactory(String,Map) также переопределяет, но намного проще:

@Override
public EntityManagerFactory createEntityManagerFactory(String persistenceUnitInfo, Map map) {

    // change the datasource name
    String newDataSourceName = ...; // any name you want
    if (map==null) map = new HashMap();  
    map.put(HibernatePersistence.JTA_DATASOURCE, newDataSourceName);  

    // delegate the EMF creation
    return new HibernatePersistence().createEntityManaferFactory(persistenceUnitInfo, map);
}

Обратите внимание, что я написал здесь только основной код. На самом деле, мой поставщик персистентности имеет много других функций:

  • проверьте, что источник данных запущен и работает
  • установить менеджер транзакций для JBoss или WebSphere
  • кэшировать EMF для уменьшения использования памяти
  • перенастроить кэш плана запросов Hibernate для меньшего использования памяти
  • зарегистрировать bean-компонент JMX (чтобы несколько EAR могли получить одно и то же имя единицы сохраняемости)
2 голосов
/ 12 марта 2011
  • Настройка необходимых источников данных и постоянных единиц в файле persistence.xml.
<persistence-unit name="UNIT_NAME" transaction-type="JTA">
      <provider>PERSISTENCE_PROVIDER</provider>
          <jta-data-source>java:DATA_SOURCE_NAME</jta-data-source>
</persistence-unit>

 -- other units  

Теперь во время выполнения вы можете построить сущность-менеджер для требуемого модуля персистентности. Создайте отдельные единицы постоянства для каждого источника данных.

//---
EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName);
EntityManager em = emf.createEntityManager();
//---
  • В противном случае вы также можете построить фабрику, предоставив карту свойств, таких как db-url, userName и т. Д.
createEntityManagerFactory(persistenceUnitName,propertiesMap);

Это создаст и вернет EntityManagerFactory для именованной единицы персистентности с использованием заданных свойств. Поэтому вы можете соответствующим образом изменить свойства во время выполнения.

1 голос
/ 10 декабря 2012

Я хочу указать, что использование

Persistence.createEntityManagerFactory(persistenceUnitName)

, рекомендованного в ответе Наяна, классифицируется в спецификации JPA (JSR 317) следующим образом (сноска в «9.2 Начальная загрузка в средах Java SE»):

"Использование этих API начальной загрузки Java SE может поддерживаться в контейнерах Java EE, однако поддержка такого использования не требуется."

Так что это не такстандартное решение для EJB.В любом случае, я могу подтвердить, что это работает в EclipseLink.

Примечание. Мне еще не разрешено публиковать это как комментарий.

...