Подтвердить что ты не робот

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

У меня есть это требование, чтобы добавлять классы сущностей в блок персистентности во время выполнения, а не указывать их все в файле persistence.xml. Может ли кто-нибудь помочь мне с тем же?

Я знаю, что у Hibernate есть свой собственный механизм, использующий то же самое:

AnnotationConfiguration.addAnnotatedClass(Class) и т.д. Вы также можете программно добавлять файлы конфигурации hibernate config (*.hbm.xml).

Требование состоит в том, что без перезапуска сервера приложений я должен иметь возможность динамически добавлять классы объектов/их файлы конфигурации (сопоставления) в блок сохранения.

Но решение программно добавлять сущности-классы/файлы конфигурации во время выполнения в блок персистентности не должно быть специфичным для реализации JPA.

4b9b3361

Ответ 1

JPA еще не предлагает эту функцию. Вот три варианта, которые вы можете проверить:

EDIT:

Если поставщик JPA является Hibernate, с Hibernate 4.0, он может передавать напрямую сущности этому поставщику JPA без объявления в файле persistence.xml. Спящий режим будет обрабатывать объекты на лету.

EDIT:

Вот пример конфигурации JPA 2.1 + Hibernate 4.3.7.Final без объявления каких-либо объектов:

META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
    version="2.1">

    <persistence-unit name="my-persistence-unit"
        transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <!-- Database Properties -->
            <property name="javax.persistence.jdbc.url"
                value="jdbc:postgresql://localhost:5432/my-database" />
            <property name="javax.persistence.jdbc.user" value="login" />
            <property name="javax.persistence.jdbc.password" value="password" />

            <!-- Hibernate Properties -->
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="hibernate.default_schema" value="public" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.format_sql" value="true" />

            <!-- Connection Pool -->
            <property name="hibernate.c3p0.min_size" value="5" />
            <property name="hibernate.c3p0.max_size" value="20" />
            <property name="hibernate.c3p0.timeout" value="500" />
            <property name="hibernate.c3p0.max_statements" value="50" />
            <property name="hibernate.c3p0.idle_test_period" value="2000" />
        </properties>
    </persistence-unit>

</persistence>

Ссылки

Ответ 2

Я опаздываю на вечеринку, но я думаю, что это спасет некоторых людей от головной боли. Я реализовал сканирование классов для чистой JPA (без spring и т.д.), Которая интегрируется с, например, при необходимости, при необходимости.

Вот что вам нужно сделать.

Сначала измените файл persistence.xml и добавьте собственную реализацию, например:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
         http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
         version="2.1">

<persistence-unit name="my.persistence.unit" transaction-type="RESOURCE_LOCAL">

    <provider>my.custom.package.HibernateDynamicPersistenceProvider</provider>

    <exclude-unlisted-classes>true</exclude-unlisted-classes>

    <properties>
        <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
        <property name="hibernate.max_fetch_depth" value="30" />
        <property name="hibernate.hbm2ddl.auto" value="update" />
        <property name="hibernate.show_sql" value="true" />
    </properties>
</persistence-unit>

Для того, чтобы Провайдеры были узнаны, вам нужно будет сделать это доступным. JPA обнаруживает использование механизма загрузки службы, поэтому мы добавляем:

/src/main/resources/META-INF/services/javax.persistence.spi.PersistenceProvider

Этот файл имеет ровно одну строку:

my.custom.package.HibernateDynamicPersistenceProvider

Наконец, добавьте своего собственного провайдера и основайте его на HibernateProvider (я основываю его на этом, так как я хочу использовать спящий режим):

public class HibernateDynamicPersistenceProvider extends HibernatePersistenceProvider implements PersistenceProvider {

    private static final Logger log = Logger.getLogger(HibernateDynamicPersistenceProvider.class);

    public static final String CUSTOM_CLASSES = "CUSTOM_CLASSES";

    @Override
    protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
            PersistenceUnitDescriptor persistenceUnitDescriptor, Map integration, ClassLoader providedClassLoader) {

        if(persistenceUnitDescriptor instanceof ParsedPersistenceXmlDescriptor) {
            ParsedPersistenceXmlDescriptor tmp = (ParsedPersistenceXmlDescriptor) persistenceUnitDescriptor;
            Object object = integration.get("CUSTOM_CLASSES");
        }

        return super.getEntityManagerFactoryBuilder(persistenceUnitDescriptor, integration, providedClassLoader);
    }

    protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties, ClassLoader providedClassLoader) {
        log.debug( String.format("Attempting to obtain correct EntityManagerFactoryBuilder for persistenceUnitName : %s", persistenceUnitName ));

        final Map integration = wrap( properties );
        final List<ParsedPersistenceXmlDescriptor> units;
        try {
            units = PersistenceXmlParser.locatePersistenceUnits( integration );
        }
        catch (Exception e) {
            log.debug( "Unable to locate persistence units", e );
            throw new PersistenceException( "Unable to locate persistence units", e );
        }

        log.debug( String.format("Located and parsed %s persistence units; checking each", units.size() ));

        if ( persistenceUnitName == null && units.size() > 1 ) {
            // no persistence-unit name to look for was given and we found multiple persistence-units
            throw new PersistenceException( "No name provided and multiple persistence units found" );
        }

        for ( ParsedPersistenceXmlDescriptor persistenceUnit : units ) {
            log.debug( String.format(
                    "Checking persistence-unit [name=%s, explicit-provider=%s] against incoming persistence unit name [%s]",
                    persistenceUnit.getName(),
                    persistenceUnit.getProviderClassName(),
                    persistenceUnitName
            ));

            final boolean matches = persistenceUnitName == null || persistenceUnit.getName().equals( persistenceUnitName );
            if ( !matches ) {
                log.debug( "Excluding from consideration due to name mis-match" );
                continue;
            }

            // See if we (Hibernate) are the persistence provider

            String extractRequestedProviderName = ProviderChecker.extractRequestedProviderName(persistenceUnit, integration);

            if ( ! ProviderChecker.isProvider( persistenceUnit, properties ) && !(this.getClass().getName().equals(extractRequestedProviderName))) {
                log.debug( "Excluding from consideration due to provider mis-match" );
                continue;
            }

            return getEntityManagerFactoryBuilder( persistenceUnit, integration, providedClassLoader );
        }

        log.debug( "Found no matching persistence units" );
        return null;
    }
}

Мне пришлось перезаписать 2 метода, сначала:

protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
            PersistenceUnitDescriptor persistenceUnitDescriptor, Map integration, ClassLoader providedClassLoader)

Это метод перехвата. Я добавил настраиваемое свойство "CUSTOM_CLASSES", которое действительно должно называться "CUSTOM_PACKAGES", в котором будут перечислены все пакеты, которые необходимо отсканировать. На данный момент я немного ленив, и я пропущу фактическое сканирование классов, но вы можете сделать это сами - это довольно прямолинейно. Затем вы можете вызвать

tmp.addClasses("class1", "class2");

Если классы - это те, которые вы обнаружили.

Второй метод, который мы переопределяем:

protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties, ClassLoader providedClassLoader)

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

String extractRequestedProviderName = ProviderChecker.extractRequestedProviderName(persistenceUnit, integration);

if ( ! ProviderChecker.isProvider( persistenceUnit, properties ) && !(this.getClass().getName().equals(extractRequestedProviderName))) {
        log.debug( "Excluding from consideration due to provider mis-match" );
        continue;
}

Это расширяет обычную проверку спящего режима, также чтобы мой пользовательский поставщик был действительным.

Вола, мы закончили, теперь у вас есть hibernate enabled classpath scan с JPA.