Hibernate internally uses an abstract metadata model to validate the data model defined by the declared entities. The framework also offers entry points to enhance this metadata model with custom code.
In this article we show how to extend the generated metadata model using annotations. Our example is to implement a convenient way to define common table prefixes for subclasses of a “@MappedSuperclass” entity.
Meta data enhancement
Imagine a deeply nested data model where you want to use a common prefix for all tables of a given mapped super class. You could of course repeatedly specify the complete name for every entity. Here we want to declare this prefix just once using a custom annotation:
@MappedSuperclass
@MappedSuperclassTable(prefix = "MY_PREFIX_")
public class AbstractSuperThing
{ ... }
In the sub classes we just use the @Table
annotation as usual.
@Entity
@Table(name = "TEST1")
public class MyEntity extends AbstractSuperThing
{ ... }
To generate the physical table name "MY_PREFIX_TEST1"
in the data base for these entities we implement an MetadataEnhancer
class with the following method:
public void addEntityTableMetadata(final Metadata metadata)
{
for (PersistentClass entityBinding : metadata.getEntityBindings())
{
Table table = entityBinding.getTable();
String tableName = table.getName();
String tableNamePrefix = determineTableNamePrefix(entityBinding);
...
table.setName(tableNamePrefix + tableName);
}
}
Integrate using Hibernate SPI
At startup, Hibernate first validates the meta model for inconsistencies. Afterwards the meta model is used, e.g., to generate SQL queries.
For some reason however, Hibernate generates the metadata twice for each of these phases. Furthermore the framework uses different program codes to create the metadata model. Thus we have to apply our modifications also twice, and have to use two different entry points to do so.
For the validation phase we use a MetadataBuilderImplementor
, for the latter a SessionFactoryBuilderImplementor
. Both of which will be created by factories specified in META-INF/services/org.hibernate.boot.spi.MetadataBuilderFactory
and META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory
, respectively.
MetadataBuilderFactory
public class My_MetadataBuilderFactory implements MetadataBuilderFactory
{
public MetadataBuilderImplementor getMetadataBuilder(
MetadataSources metadatasources,
MetadataBuilderImplementor defaultBuilder)
{
return new My_MetadataBuilderImplementor(defaultBuilder);
}
}
public class My_MetadataBuilderImplementor
implements MetadataBuilderImplementor
{
...
public Metadata build()
{
final Metadata metadata = super.build();
this.enhancer.addEntityTableMetadata(metadata);
return metadata;
}
}
SessionFactoryBuilderFactory
public class My_SessionFactoryBuilder
implements SessionFactoryBuilderFactory
{
public SessionFactoryBuilder getSessionFactoryBuilder(
MetadataImplementor metadata,
SessionFactoryBuilderImplementor defaultBuilder)
{
final My_SessionFactoryBuilderImplementor implementor =
new My_SessionFactoryBuilderImplementor(metadata, defaultBuilder);
implementor.enhance();
return implementor;
}
}
public class My_SessionFactoryBuilderImplementor
extends AbstractDelegatingSessionFactoryBuilderImplementor
{
...
public void enhance()
{
this.enhancer.addEntityTableMetadata(this.metadata);
}
}
At runtime, the concatenated table name is then used as well for schema generation/validation as for creating SQL queries.
markus.dahm@akquinet.de