问题
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?
回答1:
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());
}
回答2:
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);
}
}
}
回答3:
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.
来源:https://stackoverflow.com/questions/33111949/hibernate-5-implicitnamingstrategy