Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » cfg » annotations » [javadoc | source]
    1   //$Id: TableBinder.java 11223 2007-02-20 19:37:47Z epbernard $
    2   package org.hibernate.cfg.annotations;
    3   
    4   import java.util.ArrayList;
    5   import java.util.Iterator;
    6   import java.util.List;
    7   import javax.persistence.UniqueConstraint;
    8   
    9   import org.apache.commons.logging.Log;
   10   import org.apache.commons.logging.LogFactory;
   11   import org.hibernate.AnnotationException;
   12   import org.hibernate.AssertionFailure;
   13   import org.hibernate.annotations.Index;
   14   import org.hibernate.annotations.common.util.StringHelper;
   15   import org.hibernate.cfg.BinderHelper;
   16   import org.hibernate.cfg.Ejb3JoinColumn;
   17   import org.hibernate.cfg.ExtendedMappings;
   18   import org.hibernate.cfg.IndexSecondPass;
   19   import org.hibernate.mapping.Collection;
   20   import org.hibernate.mapping.Column;
   21   import org.hibernate.mapping.DependantValue;
   22   import org.hibernate.mapping.JoinedSubclass;
   23   import org.hibernate.mapping.PersistentClass;
   24   import org.hibernate.mapping.Property;
   25   import org.hibernate.mapping.SimpleValue;
   26   import org.hibernate.mapping.Table;
   27   import org.hibernate.mapping.ToOne;
   28   import org.hibernate.mapping.Value;
   29   
   30   /**
   31    * Table related operations
   32    *
   33    * @author Emmanuel Bernard
   34    */
   35   public class TableBinder {
   36   	//TODO move it to a getter/setter strategy
   37   	private static Log log = LogFactory.getLog( TableBinder.class );
   38   	private String schema;
   39   	private String catalog;
   40   	private String name;
   41   	private boolean isAbstract;
   42   	private List<String[]> uniqueConstraints;
   43   	String constraints;
   44   	Table denormalizedSuperTable;
   45   	ExtendedMappings mappings;
   46   	private String ownerEntityTable;
   47   	private String associatedEntityTable;
   48   	private String propertyName;
   49   	private String ownerEntity;
   50   	private String associatedEntity;
   51   
   52   	public void setSchema(String schema) {
   53   		this.schema = schema;
   54   	}
   55   
   56   	public void setCatalog(String catalog) {
   57   		this.catalog = catalog;
   58   	}
   59   
   60   	public String getName() {
   61   		return name;
   62   	}
   63   
   64   	public void setName(String name) {
   65   		this.name = name;
   66   	}
   67   
   68   	public void setAbstract(boolean anAbstract) {
   69   		isAbstract = anAbstract;
   70   	}
   71   
   72   	public void setUniqueConstraints(UniqueConstraint[] uniqueConstraints) {
   73   		this.uniqueConstraints = TableBinder.buildUniqueConstraints( uniqueConstraints );
   74   	}
   75   
   76   	public void setConstraints(String constraints) {
   77   		this.constraints = constraints;
   78   	}
   79   
   80   	public void setDenormalizedSuperTable(Table denormalizedSuperTable) {
   81   		this.denormalizedSuperTable = denormalizedSuperTable;
   82   	}
   83   
   84   	public void setMappings(ExtendedMappings mappings) {
   85   		this.mappings = mappings;
   86   	}
   87   
   88   	// only bind association table currently
   89   	public Table bind() {
   90   		//logicalName only accurate for assoc table...
   91   		String unquotedOwnerTable = StringHelper.unquote( ownerEntityTable );
   92   		String unquotedAssocTable = StringHelper.unquote( associatedEntityTable );
   93   
   94   		String logicalName = mappings.getNamingStrategy()
   95   				.logicalCollectionTableName(
   96   						name,
   97   						unquotedOwnerTable,
   98   						unquotedAssocTable,
   99   						propertyName );
  100   		if ( StringHelper.isQuoted( ownerEntityTable ) || StringHelper.isQuoted( associatedEntityTable ) ) {
  101   			logicalName = StringHelper.quote( logicalName );
  102   		}
  103   		String extendedName;
  104   		if ( name != null ) {
  105   			extendedName = mappings.getNamingStrategy().tableName( name );
  106   		}
  107   		else {
  108   			extendedName = mappings.getNamingStrategy()
  109   				.collectionTableName(
  110   						ownerEntity,
  111   						unquotedOwnerTable,
  112   						associatedEntity,
  113   						unquotedAssocTable,
  114   						propertyName
  115   				);
  116   			if ( StringHelper.isQuoted( ownerEntityTable ) || StringHelper.isQuoted( associatedEntityTable ) ) {
  117   				extendedName = StringHelper.quote( extendedName );
  118   			}
  119   		}
  120   		return fillTable(
  121   				schema, catalog,
  122   				extendedName, logicalName, isAbstract, uniqueConstraints, constraints,
  123   				denormalizedSuperTable, mappings
  124   		);
  125   	}
  126   
  127   	public static Table fillTable(
  128   			String schema, String catalog, String realTableName, String logicalName, boolean isAbstract,
  129   			List uniqueConstraints, String constraints, Table denormalizedSuperTable, ExtendedMappings mappings
  130   	) {
  131   		schema = BinderHelper.isDefault( schema ) ? mappings.getSchemaName() : schema;
  132   		catalog = BinderHelper.isDefault( catalog ) ? mappings.getCatalogName() : catalog;
  133   		Table table;
  134   		if ( denormalizedSuperTable != null ) {
  135   			table = mappings.addDenormalizedTable(
  136   					schema,
  137   					catalog,
  138   					realTableName,
  139   					isAbstract,
  140   					null, //subselect
  141   					denormalizedSuperTable
  142   			);
  143   		}
  144   		else {
  145   			table = mappings.addTable(
  146   					schema,
  147   					catalog,
  148   					realTableName,
  149   					null, //subselect
  150   					isAbstract
  151   			);
  152   		}
  153   		if ( uniqueConstraints != null && uniqueConstraints.size() > 0 ) {
  154   			mappings.addUniqueConstraints( table, uniqueConstraints );
  155   		}
  156   		if ( constraints != null ) table.addCheckConstraint( constraints );
  157   		//logicalName is null if we are in the second pass
  158   		if ( logicalName != null ) {
  159   			mappings.addTableBinding( schema, catalog, logicalName, realTableName, denormalizedSuperTable );
  160   		}
  161   		return table;
  162   	}
  163   
  164   	public static void bindFk(
  165   			PersistentClass referencedEntity, PersistentClass destinationEntity, Ejb3JoinColumn[] columns,
  166   			SimpleValue value,
  167   			boolean unique, ExtendedMappings mappings
  168   	) {
  169   		PersistentClass associatedClass;
  170   		if ( destinationEntity != null ) {
  171   			//overidden destination
  172   			associatedClass = destinationEntity;
  173   		}
  174   		else {
  175   			associatedClass = columns[0].getPropertyHolder() == null ? null : columns[0].getPropertyHolder()
  176   					.getPersistentClass();
  177   		}
  178   		final String mappedByProperty = columns[0].getMappedBy();
  179   		if ( StringHelper.isNotEmpty( mappedByProperty ) ) {
  180   			/**
  181   			 * Get the columns of the mapped-by property
  182   			 * copy them and link the copy to the actual value
  183   			 */
  184   			if ( log.isDebugEnabled() ) {
  185   				log.debug(
  186   						"Retrieving property " + associatedClass.getEntityName() + "." + mappedByProperty
  187   				);
  188   			}
  189   
  190   			final Property property = associatedClass.getRecursiveProperty( columns[0].getMappedBy() );
  191   			Iterator mappedByColumns;
  192   			if ( property.getValue() instanceof Collection ) {
  193   				Collection collection = ( (Collection) property.getValue() );
  194   				Value element = collection.getElement();
  195   				if ( element == null ) {
  196   					throw new AnnotationException(
  197   							"Illegal use of mappedBy on both sides of the relationship: "
  198   									+ associatedClass.getEntityName() + "." + mappedByProperty
  199   					);
  200   				}
  201   				mappedByColumns = element.getColumnIterator();
  202   			}
  203   			else {
  204   				mappedByColumns = property.getValue().getColumnIterator();
  205   			}
  206   			while ( mappedByColumns.hasNext() ) {
  207   				Column column = (Column) mappedByColumns.next();
  208   				columns[0].overrideSqlTypeIfNecessary( column );
  209   				columns[0].linkValueUsingAColumnCopy( column, value );
  210   			}
  211   		}
  212   		else if ( columns[0].isImplicit() ) {
  213   			/**
  214   			 * if columns are implicit, then create the columns based on the
  215   			 * referenced entity id columns
  216   			 */
  217   			Iterator idColumns;
  218   			if ( referencedEntity instanceof JoinedSubclass ) {
  219   				idColumns = ( (JoinedSubclass) referencedEntity ).getKey().getColumnIterator();
  220   			}
  221   			else {
  222   				idColumns = referencedEntity.getIdentifier().getColumnIterator();
  223   			}
  224   			while ( idColumns.hasNext() ) {
  225   				Column column = (Column) idColumns.next();
  226   				columns[0].overrideSqlTypeIfNecessary( column );
  227   				columns[0].linkValueUsingDefaultColumnNaming( column, referencedEntity, value );
  228   			}
  229   		}
  230   		else {
  231   			int fkEnum = Ejb3JoinColumn.checkReferencedColumnsType( columns, referencedEntity, mappings );
  232   
  233   			if ( Ejb3JoinColumn.NON_PK_REFERENCE == fkEnum ) {
  234   				String referencedPropertyName;
  235   				if ( value instanceof ToOne ) {
  236   					referencedPropertyName = ( (ToOne) value ).getReferencedPropertyName();
  237   				}
  238   				else if ( value instanceof DependantValue ) {
  239   					String propertyName = columns[0].getPropertyName();
  240   					if ( propertyName != null ) {
  241   						Collection collection = (Collection) referencedEntity.getRecursiveProperty( propertyName )
  242   								.getValue();
  243   						referencedPropertyName = collection.getReferencedPropertyName();
  244   					}
  245   					else {
  246   						throw new AnnotationException( "SecondaryTable JoinColumn cannot reference a non primary key" );
  247   					}
  248   
  249   				}
  250   				else {
  251   					throw new AssertionFailure(
  252   							"Do a property ref on an unexpected Value type: "
  253   									+ value.getClass().getName()
  254   					);
  255   				}
  256   				if ( referencedPropertyName == null ) {
  257   					throw new AssertionFailure(
  258   							"No property ref found while expected"
  259   					);
  260   				}
  261   				Property synthProp = referencedEntity.getRecursiveProperty( referencedPropertyName );
  262   				if ( synthProp == null ) {
  263   					throw new AssertionFailure(
  264   							"Cannot find synthProp: " + referencedEntity.getEntityName() + "." + referencedPropertyName
  265   					);
  266   				}
  267   				linkJoinColumnWithValueOverridingNameIfImplicit(
  268   						referencedEntity, synthProp.getColumnIterator(), columns, value
  269   				);
  270   
  271   			}
  272   			else {
  273   				if ( Ejb3JoinColumn.NO_REFERENCE == fkEnum ) {
  274   					//implicit case, we hope PK and FK columns are in the same order
  275   					if ( columns.length != referencedEntity.getIdentifier().getColumnSpan() ) {
  276   						throw new AnnotationException(
  277   								"A Foreign key refering " + referencedEntity.getEntityName()
  278   										+ " from " + associatedClass.getEntityName()
  279   										+ " has the wrong number of column. should be " + referencedEntity.getIdentifier()
  280   										.getColumnSpan()
  281   						);
  282   					}
  283   					linkJoinColumnWithValueOverridingNameIfImplicit(
  284   							referencedEntity,
  285   							referencedEntity.getIdentifier().getColumnIterator(),
  286   							columns,
  287   							value
  288   					);
  289   				}
  290   				else {
  291   					//explicit referencedColumnName
  292   					Iterator idColItr = referencedEntity.getKey().getColumnIterator();
  293   					org.hibernate.mapping.Column col;
  294   					Table table = referencedEntity.getTable(); //works cause the pk has to be on the primary table
  295   					if ( ! idColItr.hasNext() ) log.debug( "No column in the identifier!" );
  296   					while ( idColItr.hasNext() ) {
  297   						boolean match = false;
  298   						//for each PK column, find the associated FK column.
  299   						col = (org.hibernate.mapping.Column) idColItr.next();
  300   						for ( Ejb3JoinColumn joinCol : columns ) {
  301   							String referencedColumn = joinCol.getReferencedColumn();
  302   							referencedColumn = mappings.getPhysicalColumnName( referencedColumn, table );
  303   							if ( referencedColumn.equals( col.getName() ) ) {
  304   								//proper join column
  305   								if ( joinCol.isNameDeferred() ) {
  306   									joinCol.linkValueUsingDefaultColumnNaming(
  307   											col, referencedEntity, value
  308   									);
  309   								}
  310   								else {
  311   									joinCol.linkWithValue( value );
  312   								}
  313   								joinCol.overrideSqlTypeIfNecessary( col );
  314   								match = true;
  315   								break;
  316   							}
  317   						}
  318   						if ( !match ) {
  319   							throw new AnnotationException(
  320   									"Column name " + col.getName() + " of "
  321   											+ referencedEntity.getEntityName() + " not found in JoinColumns.referencedColumnName"
  322   							);
  323   						}
  324   					}
  325   				}
  326   			}
  327   		}
  328   		value.createForeignKey();
  329   		if ( unique == true ) {
  330   			createUniqueConstraint( value );
  331   		}
  332   	}
  333   
  334   	private static void linkJoinColumnWithValueOverridingNameIfImplicit(
  335   			PersistentClass referencedEntity, Iterator columnIterator, Ejb3JoinColumn[] columns, SimpleValue value
  336   	) {
  337   		for ( Ejb3JoinColumn joinCol : columns ) {
  338   			Column synthCol = (Column) columnIterator.next();
  339   			if ( joinCol.isNameDeferred() ) {
  340   				//this has to be the default value
  341   				joinCol.linkValueUsingDefaultColumnNaming( synthCol, referencedEntity, value );
  342   			}
  343   			else {
  344   				joinCol.linkWithValue( value );
  345   			}
  346   			joinCol.overrideSqlTypeIfNecessary( synthCol );
  347   		}
  348   	}
  349   
  350   	public static void createUniqueConstraint(Value value) {
  351   		Iterator iter = value.getColumnIterator();
  352   		ArrayList cols = new ArrayList();
  353   		while ( iter.hasNext() ) {
  354   			cols.add( iter.next() );
  355   		}
  356   		value.getTable().createUniqueKey( cols );
  357   	}
  358   
  359   	public static void addIndexes(Table hibTable, Index[] indexes, ExtendedMappings mappings) {
  360   		for ( Index index : indexes ) {
  361   			//no need to handle inSecondPass here since it is only called from EntityBinder
  362   			mappings.addSecondPass(
  363   					new IndexSecondPass( hibTable, index.name(), index.columnNames(), mappings )
  364   			);
  365   		}
  366   	}
  367   
  368   	public static List<String[]> buildUniqueConstraints(UniqueConstraint[] constraintsArray) {
  369   		List<String[]> result = new ArrayList<String[]>();
  370   		if ( constraintsArray.length != 0 ) {
  371   			for ( UniqueConstraint uc : constraintsArray ) {
  372   				result.add( uc.columnNames() );
  373   			}
  374   		}
  375   		return result;
  376   	}
  377   
  378   	public void setDefaultName(
  379   			String ownerEntity, String ownerEntityTable, String associatedEntity, String associatedEntityTable,
  380   			String propertyName
  381   	) {
  382   		this.ownerEntity = ownerEntity;
  383   		this.ownerEntityTable = ownerEntityTable;
  384   		this.associatedEntity = associatedEntity;
  385   		this.associatedEntityTable = associatedEntityTable;
  386   		this.propertyName = propertyName;
  387   		this.name = null;
  388   	}
  389   }

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