38

Is it possible to determine the native table name of an entity?

If a Table annotation is present it's easy:

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

But what about if no Table annotation is present?

Hibernate provides this information via the Configuration class:

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

Is there something similar in JPA?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
marabol
  • 1,247
  • 2
  • 15
  • 22
  • 1
    as far as i know this is indeed not part of the standard API, so you will have to rely on the actual implementation (hibernate, toplink, ...) to get what you want – Stefan De Boey Feb 26 '10 at 14:40

5 Answers5

23

This is the method I am using with EclipseLink (no mapping file):

/**
 * 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;
}
Daniel Szalay
  • 4,041
  • 12
  • 57
  • 103
  • 1
    Don't think it's a correct way, e.g. what the method would return for the entity with name SomeComplexName? – skwisgaar Jan 11 '18 at 11:08
  • @skwisgaar `SOMECOMPLEXNAME` in case there is no `@Table` annotation on the entity. Otherwise the `name` specified via the `@Table` annotation. – Daniel Szalay Jan 11 '18 at 13:42
  • 1
    afaik default behavior is to map camelcased entities to snakecased tables (e.g. SomeComplexName -> some_complex_name). I might be wrong though :) – skwisgaar Jan 11 '18 at 14:02
  • @skwisgaar I just tried it with Hibernate, the entity name got uppercased – Daniel Szalay Jan 11 '18 at 15:07
  • 1
    yes, same for me (mean, when I ran the code sample) but my tables have snakecased names so I ended up splitting it by uppercase letter and joining with underscore: `String tableName = String.join("_", entity.getClass().getSimpleName().split("(?=\\p{Upper})"));`. It's far from perfect but it's enough for my pretty simple case (I use it in tests). – skwisgaar Jan 11 '18 at 15:14
  • 1
    This doesn't work if `@Table` is used without a name and some configurable naming strategy that transforms entity name to database table names. – cdalxndr Jun 05 '20 at 15:14
  • Beware if you are using Spring Boot because it uses its [own naming strategy](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.data-access.configure-hibernate-naming-strategy) thereby overriding Hibernate's strategy. The result is that if no annotation is specified then the default table name is the class name converted to `snake_case`. In essence Spring Boot isn't fully compliant with the JPA spec in this respect .. but steers you towards using snake_case ... which I find is an acceptable override, even if not what the JPA spec says. – peterh May 22 '21 at 13:18
4

A colleague of mine found the following solution for a Spring Data JPA environment backed by Hibernate:

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.");
        }
    }
}
3

If no table annotation is present (and no ORM.xml) then in JPA the table name is formed based on the class name (see the JPA spec). Hence why exactly do you need an accessor method ?

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

DataNucleus
  • 15,497
  • 3
  • 32
  • 37
  • 2
    I wanted to avoid implementing the algorithm again. And I wanted to avoid parsing the XML mapping file, too. But I already thought, there would be no way to ask the JPA implementation for the real table names. Thanks a lot. – marabol Mar 01 '10 at 09:14
  • You can override the name using the orm.xml file so programatically re-doing the algorithm also needs to read in the correct orm file. – Καrτhικ Apr 18 '14 at 20:17
1

If you use @Table annotation, there is no problem, as you have shown. If you don't use that annotation, then table name is the same as the class name (JPA default).

The fun starts if you use mapping file, you need to parse it and retrive table name - this is not very difficult, but demands some work. If you are afraid of performance issues then you can parse mapping file(s) once and cache all tables names.

Piotr Kochański
  • 21,862
  • 7
  • 70
  • 77
0

Asking to the meta-model of the underlaying ORM is the most reliable: looking at the presence of the @Table is not enough, not only it can be overridden by XML configuration (e.g. orm.xml) but with JOINED strategy, the @Table may be on a super-class.

p3consulting
  • 2,721
  • 2
  • 12
  • 10