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


82

Чи існує спосіб ініціалізації EntityManagerбез визначеної одиниці збереження? Чи можете ви надати всі необхідні властивості для створення менеджера сутності? Мені потрібно створити EntityManagerз вказаних користувачем значень під час виконання. Оновлення persistence.xmlта перекомпіляція не є можливим.

Будь-яка ідея про те, як це зробити, більше ніж вітається!

Відповіді:


57

Чи існує спосіб ініціалізації EntityManagerбез визначеної одиниці збереження?

Ви повинні визначити принаймні одну одиницю стійкості в persistence.xmlдескрипторі розгортання.

Чи можете ви надати всі необхідні властивості для створення Entitymanager?

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

У середовищах Java EE елементи jta-data-sourceand non-jta-data-sourceвикористовуються для вказівки глобального імені JNDI JTA та / або джерела даних, що не є 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 config 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?
Загроза

це простий об'єкт 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і ви переслідуєте NullPointerExceptionцілий день. > :-(

Потім він працював із таким кодом:

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 info, Map map) для завантаження EntityManagerFactoryбез використання persistence.xml.

Однак дратує те, що вам доводиться реалізовувати PersistenceUnitInfoінтерфейс, тому вам краще використовувати Spring або Hibernate, які обидва підтримують завантаження JPA без persistence.xmlфайлу:

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

Де PersistenceUnitInfo реалізований специфічним для Spring класом MutablePersistenceUnitInfo .

Ознайомтесь із цією статтею, щоб отримати гарну демонстрацію того, як ви можете досягти цієї мети за допомогою Hibernate.


Використання MutablePersistenceUnitInfoне працює, оскільки деякі методи викидають UnsupportedOperationException . Також згадана стаття трохи застаріла: getPersistenceUnitRootUrlне може повернути null, інакше Hibernate не сканує шлях до класу (Hibernate 5.2.8).
Брайс,

1
Я трохи помилився, стаття щодо цього не застаріла, оскільки код передає список сутностей і не використовує сканування пакетів. Однак для автоматичного сканування сутності необхідно застосувати getPersistenceUnitRootUrl або getJarFileUrls. Пізніший засіяно на stackoverflow.com/a/42372648/48136
Brice

0

DataNucleus JPA, який я використовую, також має спосіб зробити це у своїх документах . Не потрібно весни або потворної реалізації 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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.