使用 JPA 2.0 以编程方式加载实体类?

Posted

技术标签:

【中文标题】使用 JPA 2.0 以编程方式加载实体类?【英文标题】:Programmatically loading Entity classes with JPA 2.0? 【发布时间】:2011-02-19 18:50:41 【问题描述】:

使用 Hibernate,您可以将 Entity 类加载为:

sessionFactory = new AnnotationConfiguration()
                    .addPackage("test.animals")
                    .addAnnotatedClass(Flight.class)
                    .addAnnotatedClass(Sky.class)
                    .addAnnotatedClass(Person.class)
                    .addAnnotatedClass(Dog.class);

有没有办法以符合 JPA 2.0 的方式做同样的事情 - 以编程方式加载您的实体类?

这个问题的原因是因为我想动态地加载我的Entity 类,因此不一定以编程方式。

【问题讨论】:

【参考方案1】:

在 Spring 的帮助下,我以符合 JPA 的方式做到了这一点。

我的“persistence.xml”看起来是空的,<persistence-unit> 元素中没有列出任何实体。

然后我编写了一个实现 PersistenceUnitPostProcessor 的类,如下所示:

import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.reflections.Reflections;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;

public class ReflectionsPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor 

    private String reflectionsRoot;
    private Logger log = LoggerFactory.getLogger(ReflectionsPersistenceUnitPostProcessor.class);

    @Override
    public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) 
            Reflections r = new Reflections(this.reflectionsRoot, new TypeAnnotationsScanner());
            Set<String> entityClasses = r.getStore().getTypesAnnotatedWith(Entity.class.getName());
            Set<String> mappedSuperClasses = r.getStore().getTypesAnnotatedWith(MappedSuperclass.class.getName());

            for (String clzz : mappedSuperClasses)
            
                    pui.addManagedClassName(clzz);
            


            for (String clzz : entityClasses)
            
                    pui.addManagedClassName(clzz);
            

    

    public String getReflectionsRoot() 
            return reflectionsRoot;
    

    public void setReflectionsRoot(String reflectionsRoot) 
            this.reflectionsRoot = reflectionsRoot;
    

然后我像这样调整了我的 spring 上下文 xml:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="jpaVendorAdapter">
                    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                            <property name="showSql" value="false" />
                            <property name="generateDdl" value="true" />
                            <property name="databasePlatform" value="org.hibernate.dialect.mysql5InnoDBDialect" />
                    </bean>
            </property>
            <property name="persistenceUnitName" value="GenericPersistenceUnit"/>
            <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
            <property name="persistenceUnitPostProcessors">
                    <list>
                            <bean class="com.austinmichael.core.repository.ReflectionsPersistenceUnitPostProcessor">
                                    <property name="reflectionsRoot" value="com.austinmichael"/>
                            </bean>
                    </list>
            </property>
    </bean>

注意 ReflectionsPersistenceUnitPostProcessor 在 persistenceUnitPostProcessors 设置中的注册。

就是这样。类路径上带有 JPA 实体或 MappedSuperclass 注释的每个类都将添加到类路径中。我必须为反射提供要扫描的包名称的前缀,这就是为什么 com.austinmichael 存在的原因。如果您的实体不共享公共包名称前缀,您可以注册第二个 ReflectionsPersistenceUnitPostProcessor 并使用不同的包名称前缀。

但是,现在这与 JPAVendor 无关。

【讨论】:

是否可以使用此方法以编程方式设置 persistence.xml 中的 databaseName 和其他属性?【参考方案2】:

有没有办法以符合 JPA 2.0 的方式做同样的事情 - 以编程方式加载您的实体类?

不,JPA 不支持此功能,因此您必须以提供者特定的方式执行此操作。 James Sutherland 在this thread 中描述了 EclipseLink 的流程,如下所示:

您可以从EntityManagerFactoryImpl (getServerSession()) 访问 EclipseLink ServerSession,并使用其'addDescriptor(ClassDescriptor)addDescriptors() API 添加 EclipseLink ClassDescriptor。您需要自己直接构建 ClassDescriptor 元数据对象(或使用 Mapping Workbench 来创建它们),因为从 JPA 注释或 orm.xml 加载会更加困难。

还可以查看more recent thread 以获取更多代码示例(API 看起来有点冗长)。

参考文献

Re: [eclipselink-users] Close to get it. Just a little help Re: JPA: adding entities to EntityManagerFactory programmatically

【讨论】:

这不是违背了 JPA 的全部目的吗?它为您提供了很多控制权,但代价是必须手动完成所有事情。还是我看错了? @Dennetik 不知何故,是的。但我不认为 EclipseLink 有像 Hibernate 那样简洁的东西。如果你想要懒惰的 ToOne,我什至不确定在哪里放置编织步骤。换句话说,我不完全确定你想做什么,但我觉得 JPA 可能不合适。 Hibernate 有什么东西吗?【参考方案3】:

我不认为有这样的选项 - JPA 支持扫描实体的类路径或在 persistence.xml 中明确列出实体类。由于您使用休眠作为持久性提供程序,但是您始终可以使用休眠代码。看看HibernateEntityManagerHibernateEntityManagerFactory 类。您可以将实体管理器工厂和实体管理器转换为它们并执行通常的休眠操作。

【讨论】:

好吧,老实说,我实际上使用的是 EclipseLink-2.0.2。我将研究类路径扫描,因为它听起来很有希望。您是否知道这是否适用于使用 Javassist 等库的字节码修改(在创建 EntityManager 之前)?

以上是关于使用 JPA 2.0 以编程方式加载实体类?的主要内容,如果未能解决你的问题,请参考以下文章

使用 JPA 和 Spring 加载选定的 Hibernate 实体

JPA:查询以根据实体类中定义的外键值获取结果?

JPA实体类中的注解

从 JPA/Hibernate 中的视图加载实体

从 JPA 注释的实体类自动生成数据模式

如何使用类中的 setters 方法将数据插入 JPA 实体表?