Hibernate 5 ImplicitNamingStrategy
Asked Answered
C

3

5

I want to use hibernate 5.x. With hibernate 5.x there are new interfaces for ImplicitNamingStrategy and PhysicalNamingStrategy.

For a property name of an entity User I want to have a column name user_name in my database. I do not want to annotate every property with @Column(name="..."). I tried to write a custom ImplicitNamingStrategy but there is no way to get the name of the owning entity.

public class MyNamingStrategy extends ImplicitNamingStrategyComponentPathImpl
{
    @Override
    public Identifier determineBasicColumnName ( ImplicitBasicColumnNameSource source )
    {
        // How to get the name of the owning entity?
        String owningEntityName = "howdoigetthis"; 
        Identifier basicColumnName = super.determineBasicColumnName(source);
        Identifier identifier = Identifier.toIdentifier(owningEntityName +"_" + basicColumnName.toString());
        return identifier;
    }
}

Is there a way to prefix every column with the table (or entity) name by just using a NamingStrategy?

Corwun answered 13/10, 2015 at 20:23 Comment(2)
This post seems to match to your needs : #32166194.Godfrey
@AndréBlaszczyk This post you mentioned doesn't match my needs. It just alters the column names, but doesn't prefix them with a table name.Corwun
S
4

Here's a solution if you don't mind a bit of dirty reflection:

@Override
public Identifier determineBasicColumnName(final ImplicitBasicColumnNameSource source)
{
    // Get 'this$0' field and make it accessible
    Field ejb3ColumnField = null;
    final Field[] sourceFields = source.getClass().getDeclaredFields();
    for (final Field sourceField : sourceFields) {
        if (sourceField.getName().equals("this$0")) {
            ejb3ColumnField = sourceField;
        }
    }
    ejb3ColumnField.setAccessible(true);

    // Get actual field object
    String owningEntityName;
    Ejb3Column ejb3Column;
    try {
        ejb3Column = (Ejb3Column) ejb3ColumnField.get(source);
    } catch (
        IllegalArgumentException
        | IllegalAccessException e) {
        throw new RuntimeException(e);  // (Or deal with this appropriately, e.g. log it.)
    }

    // The property holder path holds the owning entity's fully qualified name
    final String owningEntityFullyQualifiedName = ejb3Column.getPropertyHolder().getPath();

    // The entity name is after the last dot in the fully qualified name
    final String[] owningEntityTokens = owningEntityFullyQualifiedName.split("\\.");
    owningEntityName = owningEntityTokens[owningEntityTokens.length - 1];

    final Identifier basicColumnName = super.determineBasicColumnName(source);
    return Identifier.toIdentifier(owningEntityName + "_" + basicColumnName.toString());
}
Sluggish answered 22/10, 2015 at 8:54 Comment(0)
C
4

This is my final solution based on Steve Chambers answer. This solutions even takes into account an explicit naming with annotations and fixes the join column name. But Steve should get all the merits for his answer

public class JlotImplicitNamingStrategy extends ImplicitNamingStrategyComponentPathImpl
{
    @Override
    public Identifier determineJoinColumnName ( ImplicitJoinColumnNameSource source )
    {
        String name = source.getReferencedColumnName().toString();
        Identifier identifier = toIdentifier(name, source.getBuildingContext());
        return identifier;
    }

    @Override
    public Identifier determineBasicColumnName ( ImplicitBasicColumnNameSource source )
    {
        try
        {
            Field ejb3ColumnField = source.getClass().getDeclaredField("this$0");
            ejb3ColumnField.setAccessible(true);
            Ejb3Column ejb3Column = (Ejb3Column) ejb3ColumnField.get(source);

            // explicit naming oder implicit
            String tableName = ejb3Column.getPropertyHolder().getTable().getName();
            final Identifier basicColumnName = super.determineBasicColumnName(source);
            String columnName = tableName + "_" + basicColumnName.toString();
            return Identifier.toIdentifier(columnName);
        }
        catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e)
        {
            throw new RuntimeException(e);
        }
    }
}
Corwun answered 25/10, 2015 at 9:8 Comment(1)
it is not calling when loader the project, when should be calling this class?Deandreadeane
N
2

I've implemented both interfaces that gives 99.99% result as hibernate 4:

public class ImprovedNamingStrategy implements PhysicalNamingStrategy
{

@Override
public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnv)
{
    return convert(identifier);
}

@Override
public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnv)
{
    return convert(identifier);
}

@Override
public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnv)
{
    return convert(identifier);
}

@Override
public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnv)
{
    return convert(identifier);
}

@Override
public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnv)
{
    return convert(identifier);
}

/**
 * Converts table name.
 *
 * @param identifier the identifier.
 * @return the identifier.
 */
private Identifier convert(Identifier identifier)
{
    if (identifier == null || StringUtils.isBlank(identifier.getText()))
    {
        return identifier;
    }

    final StringBuilder buf = new StringBuilder(identifier.getText().replace('.', '_'));
    for (int i = 1; i < buf.length() - 1; i++)
    {
        if (
            Character.isLowerCase(buf.charAt(i - 1)) &&
            Character.isUpperCase(buf.charAt(i)) &&
            Character.isLowerCase(buf.charAt(i + 1))
            )
        {
            buf.insert(i++, '_');
        }
    }
    String newName = Strings.toLowerCase(buf.toString());
    return Identifier.toIdentifier(newName);
}
}

and

public class ImprovedImplicitNamingStrategy implements ImplicitNamingStrategy
{
/**
 * The INSTANCE.
 */
public static final ImprovedImplicitNamingStrategy INSTANCE = new ImprovedImplicitNamingStrategy();

/**
 * Constructor.
 */
public ImprovedImplicitNamingStrategy()
{
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determinePrimaryTableName(ImplicitEntityNameSource source)
{
    if (source == null)
    {
        // should never happen, but to be defensive...
        throw new HibernateException("Entity naming information was not provided.");
    }

    String tableName = transformEntityName(source.getEntityNaming());

    if (tableName == null)
    {
        // todo : add info to error message - but how to know what to write since we failed to interpret the naming source
        throw new HibernateException("Could not determine primary table name for entity");
    }
    return toIdentifier(tableName, source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param entityNaming the source.
 * @return the identifier.
 */
protected String transformEntityName(EntityNaming entityNaming)
{
    // prefer the JPA entity name, if specified...
    if (StringHelper.isNotEmpty(entityNaming.getJpaEntityName()))
    {
        return entityNaming.getJpaEntityName();
    }
    else
    {
        // otherwise, use the Hibernate entity name
        return StringHelper.unqualify(entityNaming.getEntityName());
    }
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineJoinTableName(ImplicitJoinTableNameSource source)
{
    final String ownerPortion = source.getOwningPhysicalTableName();
    final String ownedPortion;
    if (source.getAssociationOwningAttributePath() != null)
    {
        ownedPortion = transformAttributePath(source.getAssociationOwningAttributePath());
    }
    else
    {
        ownedPortion = source.getNonOwningPhysicalTableName();
    }

    return toIdentifier(ownerPortion + "_" + ownedPortion, source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineCollectionTableName(ImplicitCollectionTableNameSource source)
{
    final String owningEntity = transformEntityName(source.getOwningEntityNaming());
    final String name = transformAttributePath(source.getOwningAttributePath());
    final String entityName;
    if (!Strings.isNullOrEmpty(owningEntity))
    {
        entityName = owningEntity + "_" + name;
    }
    else
    {
        entityName = name;
    }
    return toIdentifier(entityName, source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineIdentifierColumnName(ImplicitIdentifierColumnNameSource source)
{
    return toIdentifier(transformAttributePath(source.getIdentifierAttributePath()), source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineDiscriminatorColumnName(ImplicitDiscriminatorColumnNameSource source)
{
    return toIdentifier(
        source.getBuildingContext().getMappingDefaults().getImplicitDiscriminatorColumnName(), source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineTenantIdColumnName(ImplicitTenantIdColumnNameSource source)
{
    return toIdentifier(source.getBuildingContext().getMappingDefaults().getImplicitTenantIdColumnName(), source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineBasicColumnName(ImplicitBasicColumnNameSource source)
{
    return toIdentifier(transformAttributePath(source.getAttributePath()), source.getBuildingContext());
}

/**
 * The determineJoinColumnName.
 *
 * @param source the source.
 * @return identifier.
 */
@Override
public Identifier determineJoinColumnName(ImplicitJoinColumnNameSource source)
{
    final String name;

    if (source.getNature() == ImplicitJoinColumnNameSource.Nature.ELEMENT_COLLECTION)
    {
        name = transformEntityName(source.getEntityNaming()) + '_' + source.getReferencedColumnName().getText();
    }
    else
    {
        if (source.getAttributePath() == null)
        {
            name = source.getReferencedTableName().getText();
        }
        else
        {
            name = transformAttributePath(source.getAttributePath());
        }
    }
    return toIdentifier(name, source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determinePrimaryKeyJoinColumnName(ImplicitPrimaryKeyJoinColumnNameSource source)
{
    return source.getReferencedPrimaryKeyColumnName();
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineAnyDiscriminatorColumnName(ImplicitAnyDiscriminatorColumnNameSource source)
{
    return toIdentifier(
        transformAttributePath(source.getAttributePath()) + "_" +
        source.getBuildingContext().getMappingDefaults().getImplicitDiscriminatorColumnName(),
        source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineAnyKeyColumnName(ImplicitAnyKeyColumnNameSource source)
{
    return toIdentifier(
        transformAttributePath(source.getAttributePath()) + "_" +
        source.getBuildingContext().getMappingDefaults().getImplicitIdColumnName(),
        source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineMapKeyColumnName(ImplicitMapKeyColumnNameSource source)
{
    return toIdentifier(
        transformAttributePath(source.getPluralAttributePath()) + "_KEY",
        source.getBuildingContext()
    );
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineListIndexColumnName(ImplicitIndexColumnNameSource source)
{
    return toIdentifier(transformAttributePath(source.getPluralAttributePath()) + "_ORDER", source.getBuildingContext());
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineForeignKeyName(ImplicitForeignKeyNameSource source)
{
    return toIdentifier(
        NamingHelper.INSTANCE.generateHashedFkName(
            "FK",
            source.getTableName(),
            source.getReferencedTableName(),
            source.getColumnNames()
        ),
        source.getBuildingContext()
    );
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineUniqueKeyName(ImplicitUniqueKeyNameSource source)
{
    return toIdentifier(
        NamingHelper.INSTANCE.generateHashedConstraintName(
            "UK",
            source.getTableName(),
            source.getColumnNames()
        ),
        source.getBuildingContext()
    );
}

/**
 * The determinePrimaryTableName.
 *
 * @param source the source.
 * @return the identifier.
 */
@Override
public Identifier determineIndexName(ImplicitIndexNameSource source)
{
    return toIdentifier(
        NamingHelper.INSTANCE.generateHashedConstraintName(
            "IDX",
            source.getTableName(),
            source.getColumnNames()
        ),
        source.getBuildingContext()
    );
}

/**
 * For JPA standards we typically need the unqualified name.  However, a more usable
 * impl tends to use the whole path.  This method provides an easy hook for subclasses
 * to accomplish that
 *
 * @param attributePath The attribute path
 * @return The extracted name
 */
protected String transformAttributePath(AttributePath attributePath)
{
    return attributePath.getProperty();
}

/**
 * Easy hook to build an Identifier using the keyword safe IdentifierHelper.
 *
 * @param stringForm The String form of the name
 * @param buildingContext Access to the IdentifierHelper
 * @return The identifier
 */
protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext)
{

    return buildingContext.getMetadataCollector()
                          .getDatabase()
                          .getJdbcEnvironment()
                          .getIdentifierHelper()
                          .toIdentifier(stringForm);
}
}

N'joy.

Nall answered 4/4, 2016 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.