Hibernate 5 隐式命名策略

Hibernate 5 ImplicitNamingStrategy

我想使用休眠 5.x。对于休眠 5.x,ImplicitNamingStrategyPhysicalNamingStrategy 有新的接口。

对于实体 User 的 属性 name 我想在我的数据库中有一个列名 user_name。我不想用 @Column(name="...") 注释每个 属性。我尝试编写自定义 ImplicitNamingStrategy 但无法获取拥有实体的名称。

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;
    }
}

有没有办法只使用 NamingStrategy 为每列添加 table(或实体)名称前缀?

如果您不介意一些肮脏的反射,这里有一个解决方案:

@Override
public Identifier determineBasicColumnName(final ImplicitBasicColumnNameSource source)
{
    // Get 'this[=10=]' field and make it accessible
    Field ejb3ColumnField = null;
    final Field[] sourceFields = source.getClass().getDeclaredFields();
    for (final Field sourceField : sourceFields) {
        if (sourceField.getName().equals("this[=10=]")) {
            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());
}

这是我基于 Steve Chambers 回答的最终解决方案。该解决方案甚至考虑了带有注释的显式命名并修复了连接列名称。但是史蒂夫应该得到他回答的所有优点

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[=10=]");
            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);
        }
    }
}

我已经实现了两个接口,在休眠 4 中给出了 99.99% 的结果:

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);
}
}

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);
}
}

很高兴。