Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]
    1   //$Id: Ejb3JoinColumn.java 11223 2007-02-20 19:37:47Z epbernard $
    2   package org.hibernate.cfg;
    3   
    4   import java.util.HashSet;
    5   import java.util.Iterator;
    6   import java.util.Map;
    7   import java.util.Set;
    8   import javax.persistence.JoinColumn;
    9   import javax.persistence.PrimaryKeyJoinColumn;
   10   
   11   import org.hibernate.AnnotationException;
   12   import org.hibernate.MappingException;
   13   import org.hibernate.annotations.common.util.StringHelper;
   14   import org.hibernate.mapping.Column;
   15   import org.hibernate.mapping.Join;
   16   import org.hibernate.mapping.PersistentClass;
   17   import org.hibernate.mapping.SimpleValue;
   18   import org.hibernate.mapping.Table;
   19   import org.hibernate.mapping.Value;
   20   
   21   /**
   22    * Wrap state of an EJB3 @JoinColumn annotation
   23    * and build the Hibernate column mapping element
   24    *
   25    * @author Emmanuel Bernard
   26    */
   27   public class Ejb3JoinColumn extends Ejb3Column {
   28   	/**
   29   	 * property name repated to this column
   30   	 */
   31   	private String referencedColumn;
   32   	private String mappedBy;
   33   	//property name on the mapped by side if any
   34   	private String mappedByPropertyName;
   35   	//table name on the mapped by side if any
   36   	private String mappedByTableName;
   37   	private String mappedByEntityName;
   38   
   39   	//FIXME hacky solution to get the information at proeprty ref resolution
   40   	public String getManyToManyOwnerSideEntityName() {
   41   		return manyToManyOwnerSideEntityName;
   42   	}
   43   
   44   	public void setManyToManyOwnerSideEntityName(String manyToManyOwnerSideEntityName) {
   45   		this.manyToManyOwnerSideEntityName = manyToManyOwnerSideEntityName;
   46   	}
   47   
   48   	private String manyToManyOwnerSideEntityName;
   49   
   50   	public void setReferencedColumn(String referencedColumn) {
   51   		this.referencedColumn = referencedColumn;
   52   	}
   53   
   54   	public String getMappedBy() {
   55   		return mappedBy;
   56   	}
   57   
   58   	public void setMappedBy(String mappedBy) {
   59   		this.mappedBy = mappedBy;
   60   	}
   61   
   62   	//Due to @AnnotationOverride overriding rules, I don't want the constructor to be public
   63   	private Ejb3JoinColumn() {
   64   		setMappedBy( BinderHelper.ANNOTATION_STRING_DEFAULT );
   65   	}
   66   
   67   	//Due to @AnnotationOverride overriding rules, I don't want the constructor to be public
   68   	//TODO get rid of it and use setters
   69   	private Ejb3JoinColumn(
   70   			String sqlType,
   71   			String name,
   72   			boolean nullable,
   73   			boolean unique,
   74   			boolean insertable,
   75   			boolean updatable,
   76   			String referencedColumn,
   77   			String secondaryTable,
   78   			Map<String, Join> joins,
   79   			PropertyHolder propertyHolder,
   80   			String propertyName,
   81   			String mappedBy,
   82   			boolean isImplicit,
   83   			ExtendedMappings mappings
   84   	) {
   85   		super();
   86   		setImplicit( isImplicit );
   87   		setSqlType( sqlType );
   88   		setLogicalColumnName( name );
   89   		setNullable( nullable );
   90   		setUnique( unique );
   91   		setInsertable( insertable );
   92   		setUpdatable( updatable );
   93   		setSecondaryTableName( secondaryTable );
   94   		setPropertyHolder( propertyHolder );
   95   		setJoins( joins );
   96   		setMappings( mappings );
   97   		setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
   98   		bind();
   99   		this.referencedColumn = referencedColumn;
  100   		this.mappedBy = mappedBy;
  101   	}
  102   
  103   	public String getReferencedColumn() {
  104   		return referencedColumn;
  105   	}
  106   
  107   	public static Ejb3JoinColumn[] buildJoinColumns(
  108   			JoinColumn[] anns,
  109   			String mappedBy, Map<String, Join> joins,
  110   			PropertyHolder propertyHolder,
  111   			String propertyName,
  112   			ExtendedMappings mappings
  113   	) {
  114   		JoinColumn[] actualColumns = propertyHolder.getOverriddenJoinColumn(
  115   				StringHelper.qualify( propertyHolder.getPath(), propertyName )
  116   		);
  117   		if ( actualColumns == null ) actualColumns = anns;
  118   		if ( actualColumns == null || actualColumns.length == 0 ) {
  119   			return new Ejb3JoinColumn[]{
  120   					buildJoinColumn( (JoinColumn) null, mappedBy, joins, propertyHolder, propertyName, mappings )
  121   			};
  122   		}
  123   		else {
  124   			int size = actualColumns.length;
  125   			Ejb3JoinColumn[] result = new Ejb3JoinColumn[ size ];
  126   			for ( int index = 0; index < size ; index++ ) {
  127   				result[index] = buildJoinColumn(
  128   						actualColumns[index],
  129   						mappedBy,
  130   						joins,
  131   						propertyHolder,
  132   						propertyName,
  133   						mappings
  134   				);
  135   			}
  136   			return result;
  137   		}
  138   	}
  139   
  140   	/**
  141   	 * build join column for SecondaryTables
  142   	 */
  143   	private static Ejb3JoinColumn buildJoinColumn(
  144   			JoinColumn ann,
  145   			String mappedBy, Map<String, Join> joins,
  146   			PropertyHolder propertyHolder,
  147   			String propertyName,
  148   			ExtendedMappings mappings
  149   	) {
  150   		if ( ann != null ) {
  151   			if ( BinderHelper.isDefault( mappedBy ) ) {
  152   				throw new AnnotationException(
  153   						"Illegal attempt to define a @JoinColumn with a mappedBy association: "
  154   								+ BinderHelper.getRelativePath( propertyHolder, propertyName )
  155   				);
  156   			}
  157   			Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
  158   			joinColumn.setJoinAnnotation( ann, null );
  159   			joinColumn.setJoins( joins );
  160   			joinColumn.setPropertyHolder( propertyHolder );
  161   			joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
  162   			joinColumn.setImplicit( false );
  163   			joinColumn.setMappings( mappings );
  164   			joinColumn.bind();
  165   			return joinColumn;
  166   		}
  167   		else {
  168   			Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
  169   			joinColumn.setMappedBy( mappedBy );
  170   			joinColumn.setJoins( joins );
  171   			joinColumn.setPropertyHolder( propertyHolder );
  172   			joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
  173   			joinColumn.setImplicit( true );
  174   			joinColumn.setMappings( mappings );
  175   			joinColumn.bind();
  176   			return joinColumn;
  177   		}
  178   	}
  179   
  180   
  181   	//FIXME default name still useful in association table
  182   	public void setJoinAnnotation(JoinColumn annJoin, String defaultName) {
  183   		if ( annJoin == null ) {
  184   			setImplicit( true );
  185   		}
  186   		else {
  187   			setImplicit( false );
  188   			if ( ! BinderHelper.isDefault( annJoin.columnDefinition() ) ) setSqlType( annJoin.columnDefinition() );
  189   			if ( ! BinderHelper.isDefault( annJoin.name() ) ) setLogicalColumnName( annJoin.name() );
  190   			setNullable( annJoin.nullable() );
  191   			setUnique( annJoin.unique() );
  192   			setInsertable( annJoin.insertable() );
  193   			setUpdatable( annJoin.updatable() );
  194   			setReferencedColumn( annJoin.referencedColumnName() );
  195   			setSecondaryTableName( annJoin.table() );
  196   		}
  197   	}
  198   
  199   	/**
  200   	 * Build JoinColumn for a JOINED hierarchy
  201   	 */
  202   	public static Ejb3JoinColumn buildJoinColumn(
  203   			PrimaryKeyJoinColumn pkJoinAnn,
  204   			JoinColumn joinAnn,
  205   			Value identifier,
  206   			Map<String, Join> joins,
  207   			PropertyHolder propertyHolder, ExtendedMappings mappings
  208   	) {
  209   
  210   		Column col = (Column) identifier.getColumnIterator().next();
  211   		String defaultName = mappings.getLogicalColumnName( col.getName(), identifier.getTable() );
  212   		if ( pkJoinAnn != null || joinAnn != null ) {
  213   			String colName;
  214   			String columnDefinition;
  215   			String referencedColumnName;
  216   			if ( pkJoinAnn != null ) {
  217   				colName = pkJoinAnn.name();
  218   				columnDefinition = pkJoinAnn.columnDefinition();
  219   				referencedColumnName = pkJoinAnn.referencedColumnName();
  220   			}
  221   			else {
  222   				colName = joinAnn.name();
  223   				columnDefinition = joinAnn.columnDefinition();
  224   				referencedColumnName = joinAnn.referencedColumnName();
  225   			}
  226   			String sqlType = "".equals( columnDefinition ) ? null : columnDefinition;
  227   			String name = "".equals( colName ) ? defaultName : colName;
  228   			return new Ejb3JoinColumn(
  229   					sqlType,
  230   					name, false, false,
  231   					true, true,
  232   					referencedColumnName,
  233   					null, joins,
  234   					propertyHolder, null, null, false, mappings
  235   			);
  236   		}
  237   		else {
  238   			return new Ejb3JoinColumn(
  239   					(String) null, defaultName,
  240   					false, false, true, true, null, (String) null,
  241   					joins, propertyHolder, null, null, true, mappings
  242   			);
  243   		}
  244   	}
  245   
  246   	/**
  247   	 * Override persistent class on oneToMany Cases for late settings
  248   	 * Must only be used on second level pass binding
  249   	 */
  250   	public void setPersistentClass(PersistentClass persistentClass, Map<String, Join> joins) {
  251   		//FIXME shouldn't we deduce the classname from the persistentclasS?
  252   		this.propertyHolder = PropertyHolderBuilder.buildPropertyHolder( persistentClass, joins, getMappings() );
  253   	}
  254   
  255   	public static void checkIfJoinColumn(Object columns, PropertyHolder holder, PropertyData property) {
  256   		if ( ! ( columns instanceof Ejb3JoinColumn[] ) ) {
  257   			throw new AnnotationException(
  258   					"@Column cannot be used on an association property: "
  259   							+ holder.getEntityName()
  260   							+ "."
  261   							+ property.getPropertyName()
  262   			);
  263   		}
  264   	}
  265   
  266   	public void linkValueUsingDefaultColumnNaming(
  267   			Column referencedColumn, PersistentClass referencedEntity, SimpleValue value
  268   	) {
  269   		String columnName;
  270   		String logicalReferencedColumn = getMappings().getLogicalColumnName(
  271   				referencedColumn.getName(), referencedEntity.getTable()
  272   		);
  273   		boolean mappedBySide = mappedByTableName != null || mappedByPropertyName != null;
  274   		boolean ownerSide = getPropertyName() != null;
  275   
  276   		Boolean isRefColumnQuoted = StringHelper.isQuoted( logicalReferencedColumn );
  277   		String unquotedLogicalReferenceColumn = isRefColumnQuoted ?
  278   				StringHelper.unquote( logicalReferencedColumn ) :
  279   				logicalReferencedColumn;
  280   
  281   		if ( mappedBySide ) {
  282   			String unquotedMappedbyTable = StringHelper.unquote( mappedByTableName );
  283   			columnName = getMappings().getNamingStrategy().foreignKeyColumnName(
  284   					mappedByPropertyName,
  285   					mappedByEntityName,
  286   					unquotedMappedbyTable,
  287   					unquotedLogicalReferenceColumn
  288   			);
  289   			//one element was quoted so we quote
  290   			if ( isRefColumnQuoted || StringHelper.isQuoted( mappedByTableName ) ) {
  291   				columnName = StringHelper.quote( columnName );
  292   			}
  293   		}
  294   		else if ( ownerSide ) {
  295   			String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() );
  296   			String unquotedLogicalTableName = StringHelper.unquote( logicalTableName );
  297   			columnName = getMappings().getNamingStrategy().foreignKeyColumnName(
  298   					getPropertyName(),
  299   					referencedEntity.getEntityName(),
  300   					unquotedLogicalTableName,
  301   					unquotedLogicalReferenceColumn
  302   			);
  303   			//one element was quoted so we quote
  304   			if ( isRefColumnQuoted || StringHelper.isQuoted( logicalTableName ) ) {
  305   				columnName = StringHelper.quote( columnName );
  306   			}
  307   		}
  308   		else {
  309   			//is an intra-entity hierarchy table join so copy the name by default
  310   			String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() );
  311   			String unquotedLogicalTableName = StringHelper.unquote( logicalTableName );
  312   			columnName = getMappings().getNamingStrategy().joinKeyColumnName(
  313   					unquotedLogicalReferenceColumn,
  314   					unquotedLogicalTableName
  315   			);
  316   			//one element was quoted so we quote
  317   			if ( isRefColumnQuoted || StringHelper.isQuoted( logicalTableName ) ) {
  318   				columnName = StringHelper.quote( columnName );
  319   			}
  320   		}
  321   		//yuk side effect on an implicit column
  322   		setLogicalColumnName( columnName );
  323   		setReferencedColumn( logicalReferencedColumn );
  324   		initMappingColumn(
  325   				columnName,
  326   				null, referencedColumn.getLength(),
  327   				referencedColumn.getPrecision(),
  328   				referencedColumn.getScale(),
  329   				getMappingColumn().isNullable(),
  330   				referencedColumn.getSqlType(),
  331   				getMappingColumn().isUnique(), false
  332   		);
  333   		linkWithValue( value );
  334   	}
  335   
  336   	/**
  337   	 * used for mappedBy cases
  338   	 */
  339   	public void linkValueUsingAColumnCopy(Column column, SimpleValue value) {
  340   		initMappingColumn(
  341   				//column.getName(),
  342   				column.getQuotedName(),
  343   				null, column.getLength(),
  344   				column.getPrecision(),
  345   				column.getScale(),
  346   				getMappingColumn().isNullable(),
  347   				column.getSqlType(),
  348   				getMappingColumn().isUnique(),
  349   				false //We do copy no strategy here
  350   		);
  351   		linkWithValue( value );
  352   	}
  353   
  354   	protected void addColumnBinding(SimpleValue value) {
  355   		if ( StringHelper.isEmpty( mappedBy ) ) {
  356   			String unquotedLogColName = StringHelper.unquote( getLogicalColumnName() );
  357   			String unquotedRefColumn = StringHelper.unquote( getReferencedColumn() );
  358   			String logicalColumnName = getMappings().getNamingStrategy()
  359   					.logicalCollectionColumnName( unquotedLogColName, getPropertyName(), unquotedRefColumn );
  360   			if ( StringHelper.isQuoted( getLogicalColumnName() ) || StringHelper.isQuoted( getLogicalColumnName() ) ) {
  361   				logicalColumnName = StringHelper.quote( logicalColumnName );
  362   			}
  363   			getMappings().addColumnBinding( logicalColumnName, getMappingColumn(), value.getTable() );
  364   		}
  365   	}
  366   
  367   	//keep it JDK 1.4 compliant
  368   	//implicit way
  369   	public static final int NO_REFERENCE = 0;
  370   	//reference to the pk in an explicit order
  371   	public static final int PK_REFERENCE = 1;
  372   	//reference to non pk columns
  373   	public static final int NON_PK_REFERENCE = 2;
  374   
  375   	public static int checkReferencedColumnsType(
  376   			Ejb3JoinColumn[] columns, PersistentClass referencedEntity,
  377   			ExtendedMappings mappings
  378   	) {
  379   		//convenient container to find whether a column is an id one or not
  380   		Set<Column> idColumns = new HashSet<Column>();
  381   		Iterator idColumnsIt = referencedEntity.getKey().getColumnIterator();
  382   		while ( idColumnsIt.hasNext() ) {
  383   			idColumns.add( (Column) idColumnsIt.next() );
  384   		}
  385   
  386   		boolean isFkReferencedColumnName = false;
  387   		boolean noReferencedColumn = true;
  388   		//build the list of potential tables
  389   		if ( columns.length == 0 ) return NO_REFERENCE; //shortcut
  390   		Object columnOwner = BinderHelper.findColumnOwner(
  391   				referencedEntity, columns[0].getReferencedColumn(), mappings
  392   		);
  393   		if ( columnOwner == null ) {
  394   			throw new MappingException(
  395   					"Unable to find column with logical name: "
  396   							+ columns[0].getReferencedColumn() + " in " + referencedEntity.getTable() + " and its related "
  397   							+ "supertables and secondary tables"
  398   			);
  399   		}
  400   		Table matchingTable = columnOwner instanceof PersistentClass ?
  401   				( (PersistentClass) columnOwner ).getTable() :
  402   				( (Join) columnOwner ).getTable();
  403   		//check each referenced column
  404   		for ( Ejb3JoinColumn ejb3Column : columns ) {
  405   			String logicalReferencedColumnName = ejb3Column.getReferencedColumn();
  406   			if ( StringHelper.isNotEmpty( logicalReferencedColumnName ) ) {
  407   				String referencedColumnName = null;
  408   				try {
  409   					referencedColumnName = mappings.getPhysicalColumnName( logicalReferencedColumnName, matchingTable );
  410   				}
  411   				catch (MappingException me) {
  412   					//rewrite the exception
  413   					throw new MappingException(
  414   							"Unable to find column with logical name: "
  415   									+ logicalReferencedColumnName + " in " + matchingTable.getName()
  416   					);
  417   				}
  418   				noReferencedColumn = false;
  419   				Column refCol = new Column( referencedColumnName );
  420   				boolean contains = idColumns.contains( refCol );
  421   				if ( ! contains ) {
  422   					isFkReferencedColumnName = true;
  423   					break; //we know the state
  424   				}
  425   			}
  426   		}
  427   		if ( isFkReferencedColumnName ) {
  428   			return NON_PK_REFERENCE;
  429   		}
  430   		else if ( noReferencedColumn ) {
  431   			return NO_REFERENCE;
  432   		}
  433   		else if ( idColumns.size() != columns.length ) {
  434   			//reference use PK but is a subset or a superset
  435   			return NON_PK_REFERENCE;
  436   		} else {
  437   			return PK_REFERENCE;
  438   		}
  439   	}
  440   
  441   	public void overrideSqlTypeIfNecessary(org.hibernate.mapping.Column column) {
  442   		if ( StringHelper.isEmpty( sqlType ) ) {
  443   			sqlType = column.getSqlType();
  444   			if ( getMappingColumn() != null ) getMappingColumn().setSqlType( sqlType );
  445   		}
  446   	}
  447   
  448   	@Override
  449   	public void redefineColumnName(String columnName, String propertyName, boolean applyNamingStrategy) {
  450   		if ( StringHelper.isNotEmpty( columnName ) ) {
  451   			getMappingColumn().setName(
  452   					applyNamingStrategy ?
  453   							getMappings().getNamingStrategy().columnName( columnName ) :
  454   							columnName
  455   			);
  456   		}
  457   	}
  458   
  459   	public static Ejb3JoinColumn[] buildJoinTableJoinColumns(
  460   			JoinColumn[] annJoins, Map<String, Join> secondaryTables,
  461   			PropertyHolder propertyHolder, String propertyName, String mappedBy, ExtendedMappings mappings
  462   	) {
  463   		Ejb3JoinColumn[] joinColumns;
  464   		if ( annJoins == null ) {
  465   			Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
  466   			currentJoinColumn.setImplicit( true );
  467   			currentJoinColumn.setNullable( false ); //I break the spec, but it's for good
  468   			currentJoinColumn.setPropertyHolder( propertyHolder );
  469   			currentJoinColumn.setJoins( secondaryTables );
  470   			currentJoinColumn.setMappings( mappings );
  471   			currentJoinColumn.setPropertyName(
  472   					BinderHelper.getRelativePath( propertyHolder, propertyName )
  473   			);
  474   			currentJoinColumn.setMappedBy( mappedBy );
  475   			currentJoinColumn.bind();
  476   
  477   			joinColumns = new Ejb3JoinColumn[]{
  478   					currentJoinColumn
  479   
  480   			};
  481   		}
  482   		else {
  483   			joinColumns = new Ejb3JoinColumn[annJoins.length];
  484   			JoinColumn annJoin;
  485   			int length = annJoins.length;
  486   			for ( int index = 0; index < length ; index++ ) {
  487   				annJoin = annJoins[index];
  488   				Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
  489   				currentJoinColumn.setImplicit( true );
  490   				currentJoinColumn.setPropertyHolder( propertyHolder );
  491   				currentJoinColumn.setJoins( secondaryTables );
  492   				currentJoinColumn.setMappings( mappings );
  493   				currentJoinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
  494   				currentJoinColumn.setMappedBy( mappedBy );
  495   				currentJoinColumn.setJoinAnnotation( annJoin, propertyName );
  496   				currentJoinColumn.setNullable( false ); //I break the spec, but it's for good
  497   				//done after the annotation to override it
  498   				currentJoinColumn.bind();
  499   				joinColumns[index] = currentJoinColumn;
  500   			}
  501   		}
  502   		return joinColumns;
  503   	}
  504   
  505   	public void setMappedBy(String entityName, String logicalTableName, String mappedByProperty) {
  506   		this.mappedByEntityName = entityName;
  507   		this.mappedByTableName = logicalTableName;
  508   		this.mappedByPropertyName = mappedByProperty;
  509   	}
  510   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » [javadoc | source]