如何在运行时检索 JPA 中实体的映射表名称?

Posted

技术标签:

【中文标题】如何在运行时检索 JPA 中实体的映射表名称?【英文标题】:How to retrieve mapping table name for an entity in JPA at runtime? 【发布时间】:2011-01-21 11:23:10 【问题描述】:

是否可以确定实体的本机表名?

如果存在Table 注释,这很容易:

entityClass.getAnnotation(Table.class).name()

但是如果没有Table 注释呢?

Hibernate 通过 Configuration 类提供此信息:

configuration.getClassMapping(entityClass.getSimpleName()).getTable().getName()

JPA 中有类似的东西吗?

【问题讨论】:

据我所知,这确实不是标准 API 的一部分,因此您将不得不依赖实际实现(hibernate、toplink、...)来获得您想要的 【参考方案1】:

我的一位同事为 Hibernate 支持的 Spring Data JPA 环境找到了以下解决方案:

import org.hibernate.internal.SessionImpl;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;

@Service
public class EntityClassToTableNameMapper 

    @Transactional
    public String[] getTableNames(EntityManager em, Class entityClass) 

        Object entityExample;
        try 
            entityExample = entityClass.newInstance();
         catch (ReflectiveOperationException e) 
            throw new RuntimeException(e);
        

        SessionImpl session = em.unwrap(SessionImpl.class);

        EntityPersister persister = session.getEntityPersister(null, entityExample);

        if (persister instanceof AbstractEntityPersister) 
            AbstractEntityPersister persisterImpl = (AbstractEntityPersister) persister;

            String tableName = persisterImpl.getTableName();

            String rootTableName = persisterImpl.getRootTableName();

            return new String[] rootTableName, tableName;

         else 
            throw new RuntimeException("Unexpected persister type; a subtype of AbstractEntityPersister expected.");
        
    

【讨论】:

【参考方案2】:

询问底层 ORM 的元模型是最可靠的:仅查看 @Table 的存在是不够的,它不仅可以被 XML 配置(例如 orm.xml)覆盖,而且可以使用 JOINED 策略, @Table 可能在超类中。

【讨论】:

这更像是一条评论。【参考方案3】:

这是我在 EclipseLink 中使用的方法(无映射文件):

/**
 * Returns the table name for a given entity type in the @link EntityManager.
 * @param em
 * @param entityClass
 * @return
 */
public static <T> String getTableName(EntityManager em, Class<T> entityClass) 
    /*
     * Check if the specified class is present in the metamodel.
     * Throws IllegalArgumentException if not.
     */
    Metamodel meta = em.getMetamodel();
    EntityType<T> entityType = meta.entity(entityClass);

    //Check whether @Table annotation is present on the class.
    Table t = entityClass.getAnnotation(Table.class);

    String tableName = (t == null)
                        ? entityType.getName().toUpperCase()
                        : t.name();
    return tableName;

【讨论】:

不要认为这是正确的方法,例如该方法将为名称为 SomeComplexName 的实体返回什么? @skwisgaar SOMECOMPLEXNAME 以防实体上没有 @Table 注释。否则,通过 @Table 注释指定的 name afaik 默认行为是将驼峰实体映射到蛇形表(例如 SomeComplexName -> some_complex_name)。不过我可能错了:) 是的,对我来说也一样(意思是,当我运行代码示例时)但我的表有蛇形名称,所以我最终用大写字母分割它并用下划线连接:String tableName = String.join("_", entity.getClass().getSimpleName().split("(?=\\pUpper)"));。它远非完美,但对于我非常简单的案例来说已经足够了(我在测试中使用它)。 如果@Table 没有名称和一些可配置的命名策略将实体名称转换为数据库表名称,这将不起作用。【参考方案4】:

如果您使用@Table 注释,则没有问题,如您所示。如果不使用该注解,则表名与类名相同(JPA 默认)。

如果你使用映射文件,乐趣就开始了,你需要解析它并检索表名——这不是很困难,但需要一些工作。如果您担心性能问题,那么您可以解析一次映射文件并缓存所有表名。

【讨论】:

【参考方案5】:

如果没有表注释(也没有 ORM.xml),那么在 JPA 中,表名是根据类名形成的(参见 JPA 规范)。因此,您究竟为什么需要访问器方法?

见http://www.datanucleus.org/products/accessplatform_2_0/jpa/orm/datastore_identifiers.html

【讨论】:

我想避免再次实现该算法。我也想避免解析 XML 映射文件。但我已经想到,没有办法向 JPA 实现询问真实的表名。非常感谢。 您可以使用 orm.xml 文件覆盖名称,因此以编程方式重新执行算法也需要读取正确的 orm 文件。

以上是关于如何在运行时检索 JPA 中实体的映射表名称?的主要内容,如果未能解决你的问题,请参考以下文章

JPA,如何使用同一个类(实体)来映射不同的表?

在 JPA 中映射为实体的关系表

如何从 JPA 注释的实体类生成 JPA 映射文件?

在 Spring Data JPA 中,如何在运行时添加实体?

如何在 JPA 中映射名称为保留字的实体字段

Java JPA双向ManyToOne映射不起作用