在运行时动态添加实体类

use*_*154 32 java jpa

我有这个要求在运行时向持久性单元添加实体类,而不是在persistence.xml中指定所有类.有人能帮我一样吗?

我知道Hibernate有自己的相同机制:

AnnotationConfiguration.addAnnotatedClass(Class)等 - 您还可以以*.hbm.xml编程方式添加hibernate config()文件.

要求是,如果不重新启动应用服务器,我应该能够继续动态地将实体类/它们的配置(映射)文件添加到持久性单元.

但是,在运行时以编程方式将实体类/配置文件添加到持久性单元的解决方案不应该特定于JPA实现.

Ste*_*han 31

JPA尚未提供此功能.您可以查看以下三个选项:

编辑:

如果JPA提供程序是Hibernate,那么从Hibernate 4.0开始,可以直接将实体传递给这个JPA提供程序,而无需persistence.xml文件中声明它们.Hibernate将动态处理实体.

编辑:

以下是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>
Run Code Online (Sandbox Code Playgroud)

参考

  • 您能否告诉我如何配置Hibernate接受实体而不声明它们?看来这不是默认行为. (3认同)

pan*_*adb 7

我迟到了,但我认为这会让一些人头疼.我为纯JPA实现了类路径扫描(不需要弹簧等),如果需要的话也可以与guice-persist集成.

这是你需要做的.

首先,更改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>
Run Code Online (Sandbox Code Playgroud)

为了使提供者得到认可,您必须使其可被发现.JPA发现使用服务加载机制,所以我们添加:

/src/main/resources/META-INF/services/javax.persistence.spi.PersistenceProvider
Run Code Online (Sandbox Code Playgroud)

这个文件只有一行:

my.custom.package.HibernateDynamicPersistenceProvider
Run Code Online (Sandbox Code Playgroud)

最后添加您自己的提供程序并将其基于HibernateProvider(我基于它,因为我想使用hibernate):

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我不得不覆盖2种方法,首先:

protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder(
            PersistenceUnitDescriptor persistenceUnitDescriptor, Map integration, ClassLoader providedClassLoader)
Run Code Online (Sandbox Code Playgroud)

这是拦截方法.我添加了一个自定义属性"CUSTOM_CLASSES",它应该被称为"CUSTOM_PACKAGES",它将列出所有需要扫描的软件包.在这一点上,我有点懒,我会跳过实际的类路径扫描,但你可以自己做 - 这是非常直接的.然后你可以打电话

tmp.addClasses("class1", "class2");
Run Code Online (Sandbox Code Playgroud)

哪些类是您发现的类.

我们重写的第二种方法是:

protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilderOrNull(String persistenceUnitName, Map properties, ClassLoader providedClassLoader)
Run Code Online (Sandbox Code Playgroud)

这是因为我们扩展的提供程序是硬编码的,只允许hibernate类创建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;
}
Run Code Online (Sandbox Code Playgroud)

这扩展了正常的hibernate检查,以使我的自定义提供程序有效.

Wola,我们已经完成了,你现在已经使用JPA进行了hibernate启用的类路径扫描.