在运行时动态添加实体类

时间:2011-12-09 16:57:25

标签: java jpa

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

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

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

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

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

2 个答案:

答案 0 :(得分: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>

<强>参考

答案 1 :(得分:7)

我迟到了,但我认为这会让一些人头疼。我实现了纯粹的JPA(不需要弹簧等)的类路径扫描,它与例如如果需要,也要坚持不懈。

这是您需要做的事情。

首先,更改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(我基于它,因为我想使用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;
    }
}

我必须覆盖2种方法,首先:

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

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

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

哪些类是您发现的类。

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

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

这是因为我们扩展的提供程序是硬编码的,只允许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;
}

这扩展了正常的休眠检查以使我的自定义提供程序有效。

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