Создать JPA EntityManager без файла конфигурации persistence.xml

82

Есть ли способ инициализировать EntityManagerбез определения единицы сохранения? Можете ли вы предоставить все необходимые свойства для создания менеджера сущностей? Мне нужно создать EntityManagerиз указанных пользователем значений во время выполнения. Обновлениеpersistence.xml и перекомпиляция невозможны.

Любая идея о том, как это сделать, более чем приветствуется!

javydreamercsw
источник

Ответы:

57

Есть ли способ инициализировать EntityManagerбез определения единицы сохранения?

В дескрипторе развертывания следует определить как минимум одну единицу сохранения состояния persistence.xml.

Можете ли вы предоставить все необходимые свойства для создания Entitymanager?

  • Атрибут имени является обязательным. Остальные атрибуты и элементы необязательны. (Спецификация JPA). Итак, это должен быть более или менее ваш минимальный persistence.xmlфайл:
<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        SOME_PROPERTIES
    </persistence-unit>
</persistence>

В среде Java EE, что jta-data-sourceи non-jta-data-sourceэлементы используются для указания глобального имени JNDI в ССТ и / или источник данных , не JTA , который будет использоваться поставщиком сохранения состояния .

Итак, если ваш целевой сервер приложений поддерживает JTA (JBoss, Websphere, GlassFish), ваш persistence.xmlвид выглядит так:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <!--GLOBAL_JNDI_GOES_HERE-->
        <jta-data-source>jdbc/myDS</jta-data-source>
    </persistence-unit>
</persistence>

Если ваш целевой сервер приложений не поддерживает JTA (Tomcat), ваш persistence.xmlвид выглядит так:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <!--GLOBAL_JNDI_GOES_HERE-->
        <non-jta-data-source>jdbc/myDS</non-jta-data-source>
    </persistence-unit>
</persistence>

Если ваш источник данных не привязан к глобальному JNDI (например, вне контейнера Java EE), вы обычно определяете свойства провайдера JPA, драйвера, URL, пользователя и пароля. Но имя свойства зависит от поставщика JPA. Итак, для Hibernate в качестве поставщика JPA ваш persistence.xmlфайл будет выглядеть так:

<persistence>
    <persistence-unit name="[REQUIRED_PERSISTENCE_UNIT_NAME_GOES_HERE]">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>br.com.persistence.SomeClass</class>
        <properties>
            <property name="hibernate.connection.driver_class" value="org.apache.derby.jdbc.ClientDriver"/>
            <property name="hibernate.connection.url" value="jdbc:derby://localhost:1527/EmpServDB;create=true"/>
            <property name="hibernate.connection.username" value="APP"/>
            <property name="hibernate.connection.password" value="APP"/>
        </properties>
    </persistence-unit>
</persistence>

Атрибут типа транзакции

Как правило, в средах Java EE тип транзакции RESOURCE_LOCALпредполагает, что будет предоставлен источник данных, не относящийся к JTA. В среде Java EE, если этот элемент не указан, по умолчанию используется JTA. В среде Java SE, если этот элемент не указан, RESOURCE_LOCALможно принять значение по умолчанию .

  • Чтобы гарантировать переносимость приложения Java SE, необходимо явно перечислить классы управляемой сохраняемости, которые включены в блок сохраняемости (спецификация JPA)

Мне нужно создать EntityManagerиз указанных пользователем значений во время выполнения

Так что используйте это:

Map addedOrOverridenProperties = new HashMap();

// Let's suppose we are using Hibernate as JPA provider
addedOrOverridenProperties.put("hibernate.show_sql", true);

Persistence.createEntityManagerFactory(<PERSISTENCE_UNIT_NAME_GOES_HERE>, addedOrOverridenProperties);
Артур Рональд
источник
Привет, я попробовал ваше решение, но возникли проблемы, не могли бы вы проверить мой вопрос: stackoverflow.com/questions/3935394/…
stacker
Но ... вопрос был в том, как создать JPA EntityManager без persistence.xml. Это хороший ответ, но он все еще использует persistence.xml, верно?
Джошуа Дэвис
В среде JavaEE при создании EntityManagerFactory они управляются EJB / JPA?
Энтони Виней
29

Да, вы можете без использования какого-либо xml-файла, используя spring, подобный этому, внутри класса @Configuration (или его эквивалентную конфигурацию spring xml):

@Bean
public LocalContainerEntityManagerFactoryBean emf(){
    properties.put("javax.persistence.jdbc.driver", dbDriverClassName);
    properties.put("javax.persistence.jdbc.url", dbConnectionURL);
    properties.put("javax.persistence.jdbc.user", dbUser); //if needed

    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setPersistenceProviderClass(org.eclipse.persistence.jpa.PersistenceProvider.class); //If your using eclipse or change it to whatever you're using
    emf.setPackagesToScan("com.yourpkg"); //The packages to search for Entities, line required to avoid looking into the persistence.xml
    emf.setPersistenceUnitName(SysConstants.SysConfigPU);
    emf.setJpaPropertyMap(properties);
    emf.setLoadTimeWeaver(new ReflectiveLoadTimeWeaver()); //required unless you know what your doing
    return emf;
}
Фрэнк Орельяна
источник
Что это за объект properties?
ThreaT
это простой объект java.util.Properties
Фрэнк Орельяна
20

Вот решение без Spring. Константы берутся из org.hibernate.cfg.AvailableSettings:

entityManagerFactory = new HibernatePersistenceProvider().createContainerEntityManagerFactory(
            archiverPersistenceUnitInfo(),
            ImmutableMap.<String, Object>builder()
                    .put(JPA_JDBC_DRIVER, JDBC_DRIVER)
                    .put(JPA_JDBC_URL, JDBC_URL)
                    .put(DIALECT, Oracle12cDialect.class)
                    .put(HBM2DDL_AUTO, CREATE)
                    .put(SHOW_SQL, false)
                    .put(QUERY_STARTUP_CHECKING, false)
                    .put(GENERATE_STATISTICS, false)
                    .put(USE_REFLECTION_OPTIMIZER, false)
                    .put(USE_SECOND_LEVEL_CACHE, false)
                    .put(USE_QUERY_CACHE, false)
                    .put(USE_STRUCTURED_CACHE, false)
                    .put(STATEMENT_BATCH_SIZE, 20)
                    .build());

entityManager = entityManagerFactory.createEntityManager();

И печально известный PersistenceUnitInfo

private static PersistenceUnitInfo archiverPersistenceUnitInfo() {
    return new PersistenceUnitInfo() {
        @Override
        public String getPersistenceUnitName() {
            return "ApplicationPersistenceUnit";
        }

        @Override
        public String getPersistenceProviderClassName() {
            return "org.hibernate.jpa.HibernatePersistenceProvider";
        }

        @Override
        public PersistenceUnitTransactionType getTransactionType() {
            return PersistenceUnitTransactionType.RESOURCE_LOCAL;
        }

        @Override
        public DataSource getJtaDataSource() {
            return null;
        }

        @Override
        public DataSource getNonJtaDataSource() {
            return null;
        }

        @Override
        public List<String> getMappingFileNames() {
            return Collections.emptyList();
        }

        @Override
        public List<URL> getJarFileUrls() {
            try {
                return Collections.list(this.getClass()
                                            .getClassLoader()
                                            .getResources(""));
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        @Override
        public URL getPersistenceUnitRootUrl() {
            return null;
        }

        @Override
        public List<String> getManagedClassNames() {
            return Collections.emptyList();
        }

        @Override
        public boolean excludeUnlistedClasses() {
            return false;
        }

        @Override
        public SharedCacheMode getSharedCacheMode() {
            return null;
        }

        @Override
        public ValidationMode getValidationMode() {
            return null;
        }

        @Override
        public Properties getProperties() {
            return new Properties();
        }

        @Override
        public String getPersistenceXMLSchemaVersion() {
            return null;
        }

        @Override
        public ClassLoader getClassLoader() {
            return null;
        }

        @Override
        public void addTransformer(ClassTransformer transformer) {

        }

        @Override
        public ClassLoader getNewTempClassLoader() {
            return null;
        }
    };
}
Брайс
источник
3
Это очень помогло мне, так как помогло мне избежать накладных расходов на использование arquillian в некоторых тестовых случаях!
cljk
18

Я смог создать с EntityManagerпомощью Hibernate и PostgreSQL исключительно с использованием кода Java (с конфигурацией Spring) следующее:

@Bean
public DataSource dataSource() {
    final PGSimpleDataSource dataSource = new PGSimpleDataSource();

    dataSource.setDatabaseName( "mytestdb" );
    dataSource.setUser( "myuser" );
    dataSource.setPassword("mypass");

    return dataSource;
}

@Bean
public Properties hibernateProperties(){
    final Properties properties = new Properties();

    properties.put( "hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect" );
    properties.put( "hibernate.connection.driver_class", "org.postgresql.Driver" );
    properties.put( "hibernate.hbm2ddl.auto", "create-drop" );

    return properties;
}

@Bean
public EntityManagerFactory entityManagerFactory( DataSource dataSource, Properties hibernateProperties ){
    final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource( dataSource );
    em.setPackagesToScan( "net.initech.domain" );
    em.setJpaVendorAdapter( new HibernateJpaVendorAdapter() );
    em.setJpaProperties( hibernateProperties );
    em.setPersistenceUnitName( "mytestdomain" );
    em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
    em.afterPropertiesSet();

    return em.getObject();
}

Обращение к LocalContainerEntityManagerFactoryBean.afterPropertiesSet()очень важно, поскольку в противном случае фабрика никогда не будет построена, а затем getObject()вернется, nullи вы будете гоняться за NullPointerExceptions весь день. > :-(

Затем он работал со следующим кодом:

PageEntry pe = new PageEntry();
pe.setLinkName( "Google" );
pe.setLinkDestination( new URL( "http://www.google.com" ) );

EntityTransaction entTrans = entityManager.getTransaction();
entTrans.begin();
entityManager.persist( pe );
entTrans.commit();

Где была моя сущность:

@Entity
@Table(name = "page_entries")
public class PageEntry {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String linkName;
    private URL linkDestination;

    // gets & setters omitted
}
Санки
источник
2
Хорошая альтернатива Hibernate.
javydreamercsw
8

С обычным JPA, предполагая, что у вас есть PersistenceProviderреализация (например, Hibernate), вы можете использовать метод PersistenceProvider # createContainerEntityManagerFactory (информация PersistenceUnitInfo, карта карты) для начальной загрузки EntityManagerFactoryбез необходимости использования persistence.xml.

Однако раздражает, что вам нужно реализовать PersistenceUnitInfoинтерфейс, поэтому вам лучше использовать Spring или Hibernate, которые поддерживают загрузку JPA без persistence.xmlфайла:

this.nativeEntityManagerFactory = provider.createContainerEntityManagerFactory(
    this.persistenceUnitInfo, 
    getJpaPropertyMap()
);

Где PersistenceUnitInfo реализуется классом MutablePersistenceUnitInfo, специфичным для Spring .

Влад Михалча
источник
Использование MutablePersistenceUnitInfoне работает, поскольку некоторые методы вызывают исключение UnsupportedOperationException . Также упомянутая статья немного устарела: getPersistenceUnitRootUrlне может вернуть null, иначе Hibernate не сканирует путь к классам (Hibernate 5.2.8).
Брайс
1
Я немного ошибся, статья в этом отношении не устарела, так как код передает список сущностей и не использует сканирование пакетов. Однако для автоматического сканирования объекта необходимо реализовать либо getPersistenceUnitRootUrl или getJarFileUrls. Последний посеян в stackoverflow.com/a/42372648/48136
Брайс
0

DataNucleus JPA, который я использую, также имеет способ сделать это в своей документации . Нет необходимости в Spring или уродливой реализации PersistenceUnitInfo.

Просто сделайте следующее

import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.api.jpa.JPAEntityManagerFactory;

PersistenceUnitMetaData pumd = new PersistenceUnitMetaData("dynamic-unit", "RESOURCE_LOCAL", null);
pumd.addClassName("mydomain.test.A");
pumd.setExcludeUnlistedClasses();
pumd.addProperty("javax.persistence.jdbc.url", "jdbc:h2:mem:nucleus");
pumd.addProperty("javax.persistence.jdbc.user", "sa");
pumd.addProperty("javax.persistence.jdbc.password", "");
pumd.addProperty("datanucleus.schema.autoCreateAll", "true");

EntityManagerFactory emf = new JPAEntityManagerFactory(pumd, null);

источник
0

Вы также можете получить EntityManager с помощью аннотации PersistenceContext или Autowired, но имейте в виду, что это не будет потокобезопасным.

@PersistenceContext
private EntityManager entityManager;
Анкит Шарма
источник