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


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

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

5 69

5 ответов:

Есть ли способ инициализировать 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-source и 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);

Да, вы можете без использования любого 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;
}

Мне удалось создать 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
}

Вот решение без пружины. Константы берутся из 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;
        }
    };
}

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

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

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

Где PersistenceUnitInfo реализуется классом Spring-specificMutablePersistenceUnitInfo .

Проверьтеэту статью для хорошей демонстрации того, как вы можете достичь этой цели с помощью Hibernate.