package org.hibernate.cfg;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.persistence.JoinColumn;
import javax.persistence.PrimaryKeyJoinColumn;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.cfg.naming.NamingStrategyDelegate;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
@SuppressWarnings("unchecked")
public class Ejb3JoinColumn extends Ejb3Column {
private String referencedColumn;
private String mappedBy;
private String mappedByPropertyName;
private String mappedByTableName;
private String mappedByEntityName;
private String mappedByJpaEntityName;
private boolean JPA2ElementCollection;
public void setJPA2ElementCollection(boolean JPA2ElementCollection) {
this.JPA2ElementCollection = JPA2ElementCollection;
}
public String getManyToManyOwnerSideEntityName() {
return manyToManyOwnerSideEntityName;
}
public void setManyToManyOwnerSideEntityName(String manyToManyOwnerSideEntityName) {
this.manyToManyOwnerSideEntityName = manyToManyOwnerSideEntityName;
}
private String manyToManyOwnerSideEntityName;
public void setReferencedColumn(String referencedColumn) {
this.referencedColumn = referencedColumn;
}
public String getMappedBy() {
return mappedBy;
}
public void setMappedBy(String mappedBy) {
this.mappedBy = mappedBy;
}
private Ejb3JoinColumn() {
setMappedBy( BinderHelper.ANNOTATION_STRING_DEFAULT );
}
private Ejb3JoinColumn(
String sqlType,
String name,
boolean nullable,
boolean unique,
boolean insertable,
boolean updatable,
String referencedColumn,
String secondaryTable,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String mappedBy,
boolean isImplicit,
Mappings mappings) {
super();
setImplicit( isImplicit );
setSqlType( sqlType );
setLogicalColumnName( name );
setNullable( nullable );
setUnique( unique );
setInsertable( insertable );
setUpdatable( updatable );
setExplicitTableName( secondaryTable );
setPropertyHolder( propertyHolder );
setJoins( joins );
setMappings( mappings );
setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
bind();
this.referencedColumn = referencedColumn;
this.mappedBy = mappedBy;
}
public String getReferencedColumn() {
return referencedColumn;
}
public static Ejb3JoinColumn[] buildJoinColumnsOrFormulas(
JoinColumnsOrFormulas anns,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
Mappings mappings) {
JoinColumnOrFormula [] ann = anns.value();
Ejb3JoinColumn [] joinColumns = new Ejb3JoinColumn[ann.length];
for (int i = 0; i < ann.length; i++) {
JoinColumnOrFormula join = ann[i];
JoinFormula formula = join.formula();
if (formula.value() != null && !formula.value().equals("")) {
joinColumns[i] = buildJoinFormula(
formula, mappedBy, joins, propertyHolder, propertyName, mappings
);
}
else {
joinColumns[i] = buildJoinColumns(
new JoinColumn[] { join.column() }, mappedBy, joins, propertyHolder, propertyName, mappings
)[0];
}
}
return joinColumns;
}
public static Ejb3JoinColumn buildJoinFormula(
JoinFormula ann,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
Mappings mappings) {
Ejb3JoinColumn formulaColumn = new Ejb3JoinColumn();
formulaColumn.setFormula( ann.value() );
formulaColumn.setReferencedColumn(ann.referencedColumnName());
formulaColumn.setMappings( mappings );
formulaColumn.setPropertyHolder( propertyHolder );
formulaColumn.setJoins( joins );
formulaColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
formulaColumn.bind();
return formulaColumn;
}
public static Ejb3JoinColumn[] buildJoinColumns(
JoinColumn[] anns,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
Mappings mappings) {
return buildJoinColumnsWithDefaultColumnSuffix(
anns, mappedBy, joins, propertyHolder, propertyName, "", mappings
);
}
public static Ejb3JoinColumn[] buildJoinColumnsWithDefaultColumnSuffix(
JoinColumn[] anns,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String suffixForDefaultColumnName,
Mappings mappings) {
JoinColumn[] actualColumns = propertyHolder.getOverriddenJoinColumn(
StringHelper.qualify( propertyHolder.getPath(), propertyName )
);
if ( actualColumns == null ) actualColumns = anns;
if ( actualColumns == null || actualColumns.length == 0 ) {
return new Ejb3JoinColumn[] {
buildJoinColumn(
null,
mappedBy,
joins,
propertyHolder,
propertyName,
suffixForDefaultColumnName,
mappings )
};
}
else {
int size = actualColumns.length;
Ejb3JoinColumn[] result = new Ejb3JoinColumn[size];
for (int index = 0; index < size; index++) {
result[index] = buildJoinColumn(
actualColumns[index],
mappedBy,
joins,
propertyHolder,
propertyName,
suffixForDefaultColumnName,
mappings
);
}
return result;
}
}
private static Ejb3JoinColumn buildJoinColumn(
JoinColumn ann,
String mappedBy, Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String suffixForDefaultColumnName,
Mappings mappings) {
if ( ann != null ) {
if ( BinderHelper.isEmptyAnnotationValue( mappedBy ) ) {
throw new AnnotationException(
"Illegal attempt to define a @JoinColumn with a mappedBy association: "
+ BinderHelper.getRelativePath( propertyHolder, propertyName )
);
}
Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
joinColumn.setMappings( mappings );
joinColumn.setJoinAnnotation( ann, null );
if ( StringHelper.isEmpty( joinColumn.getLogicalColumnName() )
&& ! StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
}
joinColumn.setJoins( joins );
joinColumn.setPropertyHolder( propertyHolder );
joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
joinColumn.setImplicit( false );
joinColumn.bind();
return joinColumn;
}
else {
Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
joinColumn.setMappedBy( mappedBy );
joinColumn.setJoins( joins );
joinColumn.setPropertyHolder( propertyHolder );
joinColumn.setPropertyName(
BinderHelper.getRelativePath( propertyHolder, propertyName )
);
if ( !StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
joinColumn.setImplicit( false );
}
else {
joinColumn.setImplicit( true );
}
joinColumn.setMappings( mappings );
joinColumn.bind();
return joinColumn;
}
}
public void setJoinAnnotation(JoinColumn annJoin, String defaultName) {
if ( annJoin == null ) {
setImplicit( true );
}
else {
setImplicit( false );
final ObjectNameNormalizer nameNormalizer = getMappings().getObjectNameNormalizer();
if ( !BinderHelper.isEmptyAnnotationValue( annJoin.columnDefinition() ) ) setSqlType( annJoin.columnDefinition() );
if ( !BinderHelper.isEmptyAnnotationValue( annJoin.name() ) ) setLogicalColumnName( annJoin.name() );
setNullable( annJoin.nullable() );
setUnique( annJoin.unique() );
setInsertable( annJoin.insertable() );
setUpdatable( annJoin.updatable() );
setReferencedColumn( annJoin.referencedColumnName() );
final String tableName = !BinderHelper.isEmptyAnnotationValue( annJoin.table() )
? nameNormalizer.normalizeIdentifierQuoting( getNamingStrategyDelegate().toPhysicalTableName(
annJoin.table()
) )
: "";
setExplicitTableName( tableName );
}
}
public static Ejb3JoinColumn buildJoinColumn(
PrimaryKeyJoinColumn pkJoinAnn,
JoinColumn joinAnn,
Value identifier,
Map<String, Join> joins,
PropertyHolder propertyHolder,
Mappings mappings) {
Column col = (Column) identifier.getColumnIterator().next();
String defaultName = mappings.getLogicalColumnName( col.getQuotedName(), identifier.getTable() );
if ( pkJoinAnn != null || joinAnn != null ) {
String colName;
String columnDefinition;
String referencedColumnName;
if ( pkJoinAnn != null ) {
colName = pkJoinAnn.name();
columnDefinition = pkJoinAnn.columnDefinition();
referencedColumnName = pkJoinAnn.referencedColumnName();
}
else {
colName = joinAnn.name();
columnDefinition = joinAnn.columnDefinition();
referencedColumnName = joinAnn.referencedColumnName();
}
String sqlType = "".equals( columnDefinition )
? null
: mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( columnDefinition );
String name = "".equals( colName )
? defaultName
: colName;
name = mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( name );
return new Ejb3JoinColumn(
sqlType,
name, false, false,
true, true,
referencedColumnName,
null, joins,
propertyHolder, null, null, false, mappings
);
}
else {
defaultName = mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( defaultName );
return new Ejb3JoinColumn(
null, defaultName,
false, false, true, true, null, null,
joins, propertyHolder, null, null, true, mappings
);
}
}
public void setPersistentClass(
PersistentClass persistentClass,
Map<String, Join> joins,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
this.propertyHolder = PropertyHolderBuilder.buildPropertyHolder( persistentClass, joins, getMappings(), inheritanceStatePerClass );
}
public static void checkIfJoinColumn(Object columns, PropertyHolder holder, PropertyData property) {
if ( !( columns instanceof Ejb3JoinColumn[] ) ) {
throw new AnnotationException(
"@Column cannot be used on an association property: "
+ holder.getEntityName()
+ "."
+ property.getPropertyName()
);
}
}
public void copyReferencedStructureAndCreateDefaultJoinColumns(
PersistentClass referencedEntity,
Iterator columnIterator,
SimpleValue value) {
if ( !isNameDeferred() ) {
throw new AssertionFailure( "Building implicit column but the column is not implicit" );
}
while ( columnIterator.hasNext() ) {
Column synthCol = (Column) columnIterator.next();
this.linkValueUsingDefaultColumnNaming( synthCol, referencedEntity, value );
}
setMappingColumn( null );
}
public void linkValueUsingDefaultColumnNaming(
Column referencedColumn,
PersistentClass referencedEntity,
SimpleValue value) {
String columnName;
String logicalReferencedColumn = getMappings().getLogicalColumnName(
referencedColumn.getQuotedName(), referencedEntity.getTable()
);
columnName = buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
setLogicalColumnName( columnName );
setReferencedColumn( logicalReferencedColumn );
initMappingColumn(
columnName,
null, referencedColumn.getLength(),
referencedColumn.getPrecision(),
referencedColumn.getScale(),
getMappingColumn() != null ? getMappingColumn().isNullable() : false,
referencedColumn.getSqlType(),
getMappingColumn() != null ? getMappingColumn().isUnique() : false,
false
);
linkWithValue( value );
}
public void addDefaultJoinColumnName(PersistentClass referencedEntity, String logicalReferencedColumn) {
final String columnName = buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
getMappingColumn().setName( columnName );
setLogicalColumnName( columnName );
}
private String buildDefaultColumnName(PersistentClass referencedEntity, String logicalReferencedColumn) {
String columnName;
boolean mappedBySide = mappedByTableName != null || mappedByPropertyName != null;
boolean ownerSide = getPropertyName() != null;
Boolean isRefColumnQuoted = StringHelper.isQuoted( logicalReferencedColumn );
String unquotedLogicalReferenceColumn = isRefColumnQuoted ?
StringHelper.unquote( logicalReferencedColumn ) :
logicalReferencedColumn;
if ( mappedBySide ) {
String unquotedMappedbyTable = StringHelper.unquote( mappedByTableName );
if ( JPA2ElementCollection ) {
columnName = getNamingStrategyDelegate().determineImplicitElementCollectionJoinColumnName(
mappedByEntityName,
mappedByJpaEntityName,
unquotedMappedbyTable,
unquotedLogicalReferenceColumn,
mappedByPropertyName
);
}
else {
columnName = getNamingStrategyDelegate().determineImplicitEntityAssociationJoinColumnName(
mappedByEntityName,
mappedByJpaEntityName,
unquotedMappedbyTable,
unquotedLogicalReferenceColumn,
mappedByPropertyName
);
}
if ( isRefColumnQuoted || StringHelper.isQuoted( mappedByTableName ) ) {
columnName = StringHelper.quote( columnName );
}
}
else if ( ownerSide ) {
String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() );
String unquotedLogicalTableName = StringHelper.unquote( logicalTableName );
columnName = getNamingStrategyDelegate().determineImplicitEntityAssociationJoinColumnName(
referencedEntity.getEntityName(),
referencedEntity.getJpaEntityName(),
unquotedLogicalTableName,
unquotedLogicalReferenceColumn,
getPropertyName()
);
if ( isRefColumnQuoted || StringHelper.isQuoted( logicalTableName ) ) {
columnName = StringHelper.quote( columnName );
}
}
else {
String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() );
String unquotedLogicalTableName = StringHelper.unquote( logicalTableName );
columnName = getNamingStrategyDelegate().toPhysicalJoinKeyColumnName(
unquotedLogicalReferenceColumn,
unquotedLogicalTableName
);
if ( isRefColumnQuoted || StringHelper.isQuoted( logicalTableName ) ) {
columnName = StringHelper.quote( columnName );
}
}
return columnName;
}
public void linkValueUsingAColumnCopy(Column column, SimpleValue value) {
initMappingColumn(
column.getQuotedName(),
null, column.getLength(),
column.getPrecision(),
column.getScale(),
getMappingColumn().isNullable(),
column.getSqlType(),
getMappingColumn().isUnique(),
false
);
linkWithValue( value );
}
@Override
protected void addColumnBinding(SimpleValue value) {
if ( StringHelper.isEmpty( mappedBy ) ) {
boolean isLogicalColumnQuoted = StringHelper.isQuoted( getLogicalColumnName() );
final ObjectNameNormalizer nameNormalizer = getMappings().getObjectNameNormalizer();
final String logicalColumnName = nameNormalizer.normalizeIdentifierQuoting( getLogicalColumnName() );
final String referencedColumn = nameNormalizer.normalizeIdentifierQuoting( getReferencedColumn() );
final String unquotedLogColName = StringHelper.unquote( logicalColumnName );
final String unquotedRefColumn = StringHelper.unquote( referencedColumn );
String logicalCollectionColumnName = getNamingStrategyDelegate().determineLogicalCollectionColumnName(
unquotedLogColName,
getPropertyName(),
unquotedRefColumn
);
if ( isLogicalColumnQuoted ) {
logicalCollectionColumnName = StringHelper.quote( logicalCollectionColumnName );
}
getMappings().addColumnBinding( logicalCollectionColumnName, getMappingColumn(), value.getTable() );
}
}
public static final int NO_REFERENCE = 0;
public static final int PK_REFERENCE = 1;
public static final int NON_PK_REFERENCE = 2;
public static int checkReferencedColumnsType(
Ejb3JoinColumn[] columns,
PersistentClass referencedEntity,
Mappings mappings) {
Set<Column> idColumns = new HashSet<Column>();
Iterator idColumnsIt = referencedEntity.getKey().getColumnIterator();
while ( idColumnsIt.hasNext() ) {
idColumns.add( (Column) idColumnsIt.next() );
}
boolean isFkReferencedColumnName = false;
boolean noReferencedColumn = true;
if ( columns.length == 0 ) return NO_REFERENCE;
Object columnOwner = BinderHelper.findColumnOwner(
referencedEntity, columns[0].getReferencedColumn(), mappings
);
if ( columnOwner == null ) {
try {
throw new MappingException(
"Unable to find column with logical name: "
+ columns[0].getReferencedColumn() + " in " + referencedEntity.getTable() + " and its related "
+ "supertables and secondary tables"
);
}
catch (MappingException e) {
throw new RecoverableException( e.getMessage(), e );
}
}
Table matchingTable = columnOwner instanceof PersistentClass ?
( (PersistentClass) columnOwner ).getTable() :
( (Join) columnOwner ).getTable();
for (Ejb3JoinColumn ejb3Column : columns) {
String logicalReferencedColumnName = ejb3Column.getReferencedColumn();
if ( StringHelper.isNotEmpty( logicalReferencedColumnName ) ) {
String referencedColumnName;
try {
referencedColumnName = mappings.getPhysicalColumnName( logicalReferencedColumnName, matchingTable );
}
catch (MappingException me) {
throw new MappingException(
"Unable to find column with logical name: "
+ logicalReferencedColumnName + " in " + matchingTable.getName()
);
}
noReferencedColumn = false;
Column refCol = new Column( referencedColumnName );
boolean contains = idColumns.contains( refCol );
if ( !contains ) {
isFkReferencedColumnName = true;
break;
}
}
}
if ( isFkReferencedColumnName ) {
return NON_PK_REFERENCE;
}
else if ( noReferencedColumn ) {
return NO_REFERENCE;
}
else if ( idColumns.size() != columns.length ) {
return NON_PK_REFERENCE;
}
else {
return PK_REFERENCE;
}
}
public void overrideFromReferencedColumnIfNecessary(org.hibernate.mapping.Column column) {
if (getMappingColumn() != null) {
if ( StringHelper.isEmpty( sqlType ) ) {
sqlType = column.getSqlType();
getMappingColumn().setSqlType( sqlType );
}
getMappingColumn().setLength(column.getLength());
getMappingColumn().setPrecision(column.getPrecision());
getMappingColumn().setScale(column.getScale());
}
}
@Override
public void redefineColumnName(String columnName, String propertyName, boolean applyNamingStrategy) {
if ( StringHelper.isNotEmpty( columnName ) ) {
getMappingColumn().setName(
applyNamingStrategy ?
quoteIdentifier( getNamingStrategyDelegate().toPhysicalColumnName( columnName ) ) :
columnName
);
}
}
private String quoteIdentifier(String identifier) {
return getMappings().getObjectNameNormalizer().isUseQuotedIdentifiersGlobally()
? StringHelper.quote( identifier ) : identifier;
}
public static Ejb3JoinColumn[] buildJoinTableJoinColumns(
JoinColumn[] annJoins,
Map<String, Join> secondaryTables,
PropertyHolder propertyHolder,
String propertyName,
String mappedBy,
Mappings mappings) {
Ejb3JoinColumn[] joinColumns;
if ( annJoins == null ) {
Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
currentJoinColumn.setImplicit( true );
currentJoinColumn.setNullable( false );
currentJoinColumn.setPropertyHolder( propertyHolder );
currentJoinColumn.setJoins( secondaryTables );
currentJoinColumn.setMappings( mappings );
currentJoinColumn.setPropertyName(
BinderHelper.getRelativePath( propertyHolder, propertyName )
);
currentJoinColumn.setMappedBy( mappedBy );
currentJoinColumn.bind();
joinColumns = new Ejb3JoinColumn[] {
currentJoinColumn
};
}
else {
joinColumns = new Ejb3JoinColumn[annJoins.length];
JoinColumn annJoin;
int length = annJoins.length;
for (int index = 0; index < length; index++) {
annJoin = annJoins[index];
Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
currentJoinColumn.setImplicit( true );
currentJoinColumn.setPropertyHolder( propertyHolder );
currentJoinColumn.setJoins( secondaryTables );
currentJoinColumn.setMappings( mappings );
currentJoinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
currentJoinColumn.setMappedBy( mappedBy );
currentJoinColumn.setJoinAnnotation( annJoin, propertyName );
currentJoinColumn.setNullable( false );
currentJoinColumn.bind();
joinColumns[index] = currentJoinColumn;
}
}
return joinColumns;
}
public void setMappedBy(String entityName, String jpaEntityName, String logicalTableName, String mappedByProperty) {
this.mappedByEntityName = entityName;
this.mappedByJpaEntityName = jpaEntityName;
this.mappedByTableName = logicalTableName;
this.mappedByPropertyName = mappedByProperty;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "Ejb3JoinColumn" );
sb.append( "{logicalColumnName='" ).append( getLogicalColumnName() ).append( '\'' );
sb.append( ", referencedColumn='" ).append( referencedColumn ).append( '\'' );
sb.append( ", mappedBy='" ).append( mappedBy ).append( '\'' );
sb.append( '}' );
return sb.toString();
}
}