package org.hibernate.cfg.annotations;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.persistence.UniqueConstraint;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.Index;
import org.hibernate.boot.model.naming.EntityNaming;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitCollectionTableNameSource;
import org.hibernate.boot.model.naming.ImplicitJoinTableNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.NamingStrategyHelper;
import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.BinderHelper;
import org.hibernate.cfg.Ejb3JoinColumn;
import org.hibernate.cfg.IndexOrUniqueKeySecondPass;
import org.hibernate.cfg.JPAIndexHolder;
import org.hibernate.cfg.ObjectNameSource;
import org.hibernate.cfg.UniqueConstraintHolder;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.DependantValue;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
import org.jboss.logging.Logger;
@SuppressWarnings("unchecked")
public class TableBinder {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, TableBinder.class.getName() );
MetadataBuildingContext buildingContext;
private String schema;
private String catalog;
private String name;
private boolean isAbstract;
private List<UniqueConstraintHolder> uniqueConstraints;
String constraints;
private String ownerEntityTable;
private String associatedEntityTable;
private String propertyName;
private String ownerClassName;
private String ownerEntity;
private String ownerJpaEntity;
private String associatedClassName;
private String associatedEntity;
private String associatedJpaEntity;
private boolean isJPA2ElementCollection;
private List<JPAIndexHolder> jpaIndexHolders;
public void setBuildingContext(MetadataBuildingContext buildingContext) {
this.buildingContext = buildingContext;
}
public void setSchema(String schema) {
this.schema = schema;
}
public void setCatalog(String catalog) {
this.catalog = catalog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAbstract(boolean anAbstract) {
isAbstract = anAbstract;
}
public void setUniqueConstraints(UniqueConstraint[] uniqueConstraints) {
this.uniqueConstraints = TableBinder.buildUniqueConstraintHolders( uniqueConstraints );
}
public void setJpaIndex(javax.persistence.Index[] jpaIndex){
this.jpaIndexHolders = buildJpaIndexHolder( jpaIndex );
}
public void setConstraints(String constraints) {
this.constraints = constraints;
}
public void setJPA2ElementCollection(boolean isJPA2ElementCollection) {
this.isJPA2ElementCollection = isJPA2ElementCollection;
}
private static class AssociationTableNameSource implements ObjectNameSource {
private final String explicitName;
private final String logicalName;
private AssociationTableNameSource(String explicitName, String logicalName) {
this.explicitName = explicitName;
this.logicalName = logicalName;
}
public String getExplicitName() {
return explicitName;
}
public String getLogicalName() {
return logicalName;
}
}
public Table bind() {
final Identifier ownerEntityTableNameIdentifier = toIdentifier( ownerEntityTable );
final Identifier associatedEntityTableNameIdentifier = toIdentifier( associatedEntityTable );
final String unquotedOwnerTable = StringHelper.unquote( ownerEntityTable );
final String unquotedAssocTable = StringHelper.unquote( associatedEntityTable );
final String ownerObjectName = isJPA2ElementCollection && ownerEntity != null
? StringHelper.unqualify( ownerEntity )
: unquotedOwnerTable;
final ObjectNameSource nameSource = buildNameContext(
ownerObjectName,
unquotedAssocTable
);
final boolean ownerEntityTableQuoted = StringHelper.isQuoted( ownerEntityTable );
final boolean associatedEntityTableQuoted = StringHelper.isQuoted( associatedEntityTable );
final NamingStrategyHelper namingStrategyHelper = new NamingStrategyHelper() {
@Override
public Identifier determineImplicitName(final MetadataBuildingContext buildingContext) {
final ImplicitNamingStrategy namingStrategy = buildingContext.getBuildingOptions().getImplicitNamingStrategy();
Identifier name;
if ( isJPA2ElementCollection ) {
name = namingStrategy.determineCollectionTableName(
new ImplicitCollectionTableNameSource() {
private final EntityNaming entityNaming = new EntityNaming() {
@Override
public String getClassName() {
return ownerClassName;
}
@Override
public String getEntityName() {
return ownerEntity;
}
@Override
public String getJpaEntityName() {
return ownerJpaEntity;
}
};
@Override
public Identifier getOwningPhysicalTableName() {
return ownerEntityTableNameIdentifier;
}
@Override
public EntityNaming getOwningEntityNaming() {
return entityNaming;
}
@Override
public AttributePath getOwningAttributePath() {
return AttributePath.parse( propertyName );
}
@Override
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
}
);
}
else {
name = namingStrategy.determineJoinTableName(
new ImplicitJoinTableNameSource() {
private final EntityNaming owningEntityNaming = new EntityNaming() {
@Override
public String getClassName() {
return ownerClassName;
}
@Override
public String getEntityName() {
return ownerEntity;
}
@Override
public String getJpaEntityName() {
return ownerJpaEntity;
}
};
private final EntityNaming nonOwningEntityNaming = new EntityNaming() {
@Override
public String getClassName() {
return associatedClassName;
}
@Override
public String getEntityName() {
return associatedEntity;
}
@Override
public String getJpaEntityName() {
return associatedJpaEntity;
}
};
@Override
public String getOwningPhysicalTableName() {
return unquotedOwnerTable;
}
@Override
public EntityNaming getOwningEntityNaming() {
return owningEntityNaming;
}
@Override
public String getNonOwningPhysicalTableName() {
return unquotedAssocTable;
}
@Override
public EntityNaming getNonOwningEntityNaming() {
return nonOwningEntityNaming;
}
@Override
public AttributePath getAssociationOwningAttributePath() {
return AttributePath.parse( propertyName );
}
@Override
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
}
);
}
if ( ownerEntityTableQuoted || associatedEntityTableQuoted ) {
name = Identifier.quote( name );
}
return name;
}
@Override
public Identifier handleExplicitName(
String explicitName, MetadataBuildingContext buildingContext) {
return buildingContext.getMetadataCollector().getDatabase().toIdentifier( explicitName );
}
@Override
public Identifier toPhysicalName(Identifier logicalName, MetadataBuildingContext buildingContext) {
return buildingContext.getBuildingOptions().getPhysicalNamingStrategy().toPhysicalTableName(
logicalName,
buildingContext.getMetadataCollector().getDatabase().getJdbcEnvironment()
);
}
};
return buildAndFillTable(
schema,
catalog,
nameSource,
namingStrategyHelper,
isAbstract,
uniqueConstraints,
jpaIndexHolders,
constraints,
buildingContext,
null,
null
);
}
private Identifier toIdentifier(String tableName) {
return buildingContext.getMetadataCollector()
.getDatabase()
.getJdbcEnvironment()
.getIdentifierHelper()
.toIdentifier( tableName );
}
private ObjectNameSource buildNameContext(
String unquotedOwnerTable,
String unquotedAssocTable) {
if ( name != null ) {
return new AssociationTableNameSource( name, null );
}
final Identifier logicalName;
if ( isJPA2ElementCollection ) {
logicalName = buildingContext.getBuildingOptions().getImplicitNamingStrategy().determineCollectionTableName(
new ImplicitCollectionTableNameSource() {
private final EntityNaming owningEntityNaming = new EntityNaming() {
@Override
public String getClassName() {
return ownerClassName;
}
@Override
public String getEntityName() {
return ownerEntity;
}
@Override
public String getJpaEntityName() {
return ownerJpaEntity;
}
};
@Override
public Identifier getOwningPhysicalTableName() {
return toIdentifier( ownerEntityTable );
}
@Override
public EntityNaming getOwningEntityNaming() {
return owningEntityNaming;
}
@Override
public AttributePath getOwningAttributePath() {
return AttributePath.parse( propertyName );
}
@Override
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
}
);
}
else {
logicalName = buildingContext.getBuildingOptions().getImplicitNamingStrategy().determineJoinTableName(
new ImplicitJoinTableNameSource() {
private final EntityNaming owningEntityNaming = new EntityNaming() {
@Override
public String getClassName() {
return ownerClassName;
}
@Override
public String getEntityName() {
return ownerEntity;
}
@Override
public String getJpaEntityName() {
return ownerJpaEntity;
}
};
private final EntityNaming nonOwningEntityNaming = new EntityNaming() {
@Override
public String getClassName() {
return associatedClassName;
}
@Override
public String getEntityName() {
return associatedEntity;
}
@Override
public String getJpaEntityName() {
return associatedJpaEntity;
}
};
@Override
public String getOwningPhysicalTableName() {
return ownerEntityTable;
}
@Override
public EntityNaming getOwningEntityNaming() {
return owningEntityNaming;
}
@Override
public String getNonOwningPhysicalTableName() {
return associatedEntityTable;
}
@Override
public EntityNaming getNonOwningEntityNaming() {
return nonOwningEntityNaming;
}
@Override
public AttributePath getAssociationOwningAttributePath() {
return AttributePath.parse( propertyName );
}
@Override
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
}
);
}
return new AssociationTableNameSource( name, logicalName.render() );
}
public static Table buildAndFillTable(
String schema,
String catalog,
ObjectNameSource nameSource,
NamingStrategyHelper namingStrategyHelper,
boolean isAbstract,
List<UniqueConstraintHolder> uniqueConstraints,
List<JPAIndexHolder> jpaIndexHolders,
String constraints,
MetadataBuildingContext buildingContext,
String subselect,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
final Identifier logicalName;
if ( StringHelper.isNotEmpty( nameSource.getExplicitName() ) ) {
logicalName = namingStrategyHelper.handleExplicitName( nameSource.getExplicitName(), buildingContext );
}
else {
logicalName = namingStrategyHelper.determineImplicitName( buildingContext );
}
return buildAndFillTable(
schema,
catalog,
logicalName,
isAbstract,
uniqueConstraints,
jpaIndexHolders,
constraints,
buildingContext,
subselect,
denormalizedSuperTableXref
);
}
public static Table buildAndFillTable(
String schema,
String catalog,
Identifier logicalName,
boolean isAbstract,
List<UniqueConstraintHolder> uniqueConstraints,
List<JPAIndexHolder> jpaIndexHolders,
String constraints,
MetadataBuildingContext buildingContext,
String subselect,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
schema = BinderHelper.isEmptyOrNullAnnotationValue( schema )
? extract( buildingContext.getMetadataCollector().getDatabase().getDefaultNamespace().getPhysicalName().getSchema() )
: schema;
catalog = BinderHelper.isEmptyOrNullAnnotationValue( catalog )
? extract( buildingContext.getMetadataCollector().getDatabase().getDefaultNamespace().getPhysicalName().getCatalog() )
: catalog;
final Table table;
if ( denormalizedSuperTableXref != null ) {
table = buildingContext.getMetadataCollector().addDenormalizedTable(
schema,
catalog,
logicalName.render(),
isAbstract,
subselect,
denormalizedSuperTableXref.getPrimaryTable()
);
}
else {
table = buildingContext.getMetadataCollector().addTable(
schema,
catalog,
logicalName.render(),
subselect,
isAbstract
);
}
if ( CollectionHelper.isNotEmpty( uniqueConstraints ) ) {
buildingContext.getMetadataCollector().addUniqueConstraintHolders( table, uniqueConstraints );
}
if ( CollectionHelper.isNotEmpty( jpaIndexHolders ) ) {
buildingContext.getMetadataCollector().addJpaIndexHolders( table, jpaIndexHolders );
}
if ( constraints != null ) {
table.addCheckConstraint( constraints );
}
buildingContext.getMetadataCollector().addTableNameBinding( logicalName, table );
return table;
}
private static String (Identifier identifier) {
if ( identifier == null ) {
return null;
}
return identifier.render();
}
public static void bindFk(
PersistentClass referencedEntity,
PersistentClass destinationEntity,
Ejb3JoinColumn[] columns,
SimpleValue value,
boolean unique,
MetadataBuildingContext buildingContext) {
PersistentClass associatedClass;
if ( destinationEntity != null ) {
associatedClass = destinationEntity;
}
else {
associatedClass = columns[0].getPropertyHolder() == null
? null
: columns[0].getPropertyHolder().getPersistentClass();
}
final String mappedByProperty = columns[0].getMappedBy();
if ( StringHelper.isNotEmpty( mappedByProperty ) ) {
LOG.debugf( "Retrieving property %s.%s", associatedClass.getEntityName(), mappedByProperty );
final Property property = associatedClass.getRecursiveProperty( columns[0].getMappedBy() );
Iterator mappedByColumns;
if ( property.getValue() instanceof Collection ) {
Collection collection = ( (Collection) property.getValue() );
Value element = collection.getElement();
if ( element == null ) {
throw new AnnotationException(
"Illegal use of mappedBy on both sides of the relationship: "
+ associatedClass.getEntityName() + "." + mappedByProperty
);
}
mappedByColumns = element.getColumnIterator();
}
else {
mappedByColumns = property.getValue().getColumnIterator();
}
while ( mappedByColumns.hasNext() ) {
Column column = (Column) mappedByColumns.next();
columns[0].overrideFromReferencedColumnIfNecessary( column );
columns[0].linkValueUsingAColumnCopy( column, value );
}
}
else if ( columns[0].isImplicit() ) {
Iterator idColumns;
if ( referencedEntity instanceof JoinedSubclass ) {
idColumns = referencedEntity.getKey().getColumnIterator();
}
else {
idColumns = referencedEntity.getIdentifier().getColumnIterator();
}
while ( idColumns.hasNext() ) {
Column column = (Column) idColumns.next();
columns[0].linkValueUsingDefaultColumnNaming( column, referencedEntity, value );
columns[0].overrideFromReferencedColumnIfNecessary( column );
}
}
else {
int fkEnum = Ejb3JoinColumn.checkReferencedColumnsType( columns, referencedEntity, buildingContext );
if ( Ejb3JoinColumn.NON_PK_REFERENCE == fkEnum ) {
String referencedPropertyName;
if ( value instanceof ToOne ) {
referencedPropertyName = ( (ToOne) value ).getReferencedPropertyName();
}
else if ( value instanceof DependantValue ) {
String propertyName = columns[0].getPropertyName();
if ( propertyName != null ) {
Collection collection = (Collection) referencedEntity.getRecursiveProperty( propertyName )
.getValue();
referencedPropertyName = collection.getReferencedPropertyName();
}
else {
throw new AnnotationException( "SecondaryTable JoinColumn cannot reference a non primary key" );
}
}
else {
throw new AssertionFailure(
"Do a property ref on an unexpected Value type: "
+ value.getClass().getName()
);
}
if ( referencedPropertyName == null ) {
throw new AssertionFailure(
"No property ref found while expected"
);
}
Property synthProp = referencedEntity.getReferencedProperty( referencedPropertyName );
if ( synthProp == null ) {
throw new AssertionFailure(
"Cannot find synthProp: " + referencedEntity.getEntityName() + "." + referencedPropertyName
);
}
linkJoinColumnWithValueOverridingNameIfImplicit(
referencedEntity, synthProp.getColumnIterator(), columns, value
);
}
else {
if ( Ejb3JoinColumn.NO_REFERENCE == fkEnum ) {
if ( columns.length != referencedEntity.getIdentifier().getColumnSpan() ) {
throw new AnnotationException(
"A Foreign key refering " + referencedEntity.getEntityName()
+ " from " + associatedClass.getEntityName()
+ " has the wrong number of column. should be " + referencedEntity.getIdentifier()
.getColumnSpan()
);
}
linkJoinColumnWithValueOverridingNameIfImplicit(
referencedEntity,
referencedEntity.getIdentifier().getColumnIterator(),
columns,
value
);
}
else {
Iterator idColItr = referencedEntity.getKey().getColumnIterator();
org.hibernate.mapping.Column col;
Table table = referencedEntity.getTable();
if ( !idColItr.hasNext() ) {
LOG.debug( "No column in the identifier!" );
}
while ( idColItr.hasNext() ) {
boolean match = false;
col = (org.hibernate.mapping.Column) idColItr.next();
for (Ejb3JoinColumn joinCol : columns) {
String referencedColumn = joinCol.getReferencedColumn();
referencedColumn = buildingContext.getMetadataCollector().getPhysicalColumnName(
table,
referencedColumn
);
if ( referencedColumn.equalsIgnoreCase( col.getQuotedName( buildingContext.getMetadataCollector().getDatabase().getJdbcEnvironment().getDialect() ) ) ) {
if ( joinCol.isNameDeferred() ) {
joinCol.linkValueUsingDefaultColumnNaming(
col, referencedEntity, value
);
}
else {
joinCol.linkWithValue( value );
}
joinCol.overrideFromReferencedColumnIfNecessary( col );
match = true;
break;
}
}
if ( !match ) {
throw new AnnotationException(
"Column name " + col.getName() + " of "
+ referencedEntity.getEntityName() + " not found in JoinColumns.referencedColumnName"
);
}
}
}
}
}
value.createForeignKey();
if ( unique ) {
createUniqueConstraint( value );
}
}
public static void linkJoinColumnWithValueOverridingNameIfImplicit(
PersistentClass referencedEntity,
Iterator columnIterator,
Ejb3JoinColumn[] columns,
SimpleValue value) {
for (Ejb3JoinColumn joinCol : columns) {
Column synthCol = (Column) columnIterator.next();
if ( joinCol.isNameDeferred() ) {
joinCol.linkValueUsingDefaultColumnNaming( synthCol, referencedEntity, value );
}
else {
joinCol.linkWithValue( value );
joinCol.overrideFromReferencedColumnIfNecessary( synthCol );
}
}
}
public static void createUniqueConstraint(Value value) {
Iterator iter = value.getColumnIterator();
ArrayList cols = new ArrayList();
while ( iter.hasNext() ) {
cols.add( iter.next() );
}
value.getTable().createUniqueKey( cols );
}
public static void addIndexes(Table hibTable, Index[] indexes, MetadataBuildingContext buildingContext) {
for (Index index : indexes) {
buildingContext.getMetadataCollector().addSecondPass(
new IndexOrUniqueKeySecondPass( hibTable, index.name(), index.columnNames(), buildingContext )
);
}
}
public static void addIndexes(Table hibTable, javax.persistence.Index[] indexes, MetadataBuildingContext buildingContext) {
buildingContext.getMetadataCollector().addJpaIndexHolders( hibTable, buildJpaIndexHolder( indexes ) );
}
public static List<JPAIndexHolder> buildJpaIndexHolder(javax.persistence.Index[] indexes){
List<JPAIndexHolder> holders = new ArrayList<JPAIndexHolder>( indexes.length );
for(javax.persistence.Index index : indexes){
holders.add( new JPAIndexHolder( index ) );
}
return holders;
}
@Deprecated
@SuppressWarnings({ "JavaDoc" })
public static List<String[]> buildUniqueConstraints(UniqueConstraint[] constraintsArray) {
List<String[]> result = new ArrayList<String[]>();
if ( constraintsArray.length != 0 ) {
for (UniqueConstraint uc : constraintsArray) {
result.add( uc.columnNames() );
}
}
return result;
}
public static List<UniqueConstraintHolder> buildUniqueConstraintHolders(UniqueConstraint[] annotations) {
List<UniqueConstraintHolder> result;
if ( annotations == null || annotations.length == 0 ) {
result = java.util.Collections.emptyList();
}
else {
result = new ArrayList<UniqueConstraintHolder>( CollectionHelper.determineProperSizing( annotations.length ) );
for ( UniqueConstraint uc : annotations ) {
result.add(
new UniqueConstraintHolder()
.setName( uc.name() )
.setColumns( uc.columnNames() )
);
}
}
return result;
}
public void setDefaultName(
String ownerClassName,
String ownerEntity,
String ownerJpaEntity,
String ownerEntityTable,
String associatedClassName,
String associatedEntity,
String associatedJpaEntity,
String associatedEntityTable,
String propertyName) {
this.ownerClassName = ownerClassName;
this.ownerEntity = ownerEntity;
this.ownerJpaEntity = ownerJpaEntity;
this.ownerEntityTable = ownerEntityTable;
this.associatedClassName = associatedClassName;
this.associatedEntity = associatedEntity;
this.associatedJpaEntity = associatedJpaEntity;
this.associatedEntityTable = associatedEntityTable;
this.propertyName = propertyName;
this.name = null;
}
}