/// <summary> /// Add the properties for an entity. /// </summary> /// <param name="meta"></param> /// <param name="pClass"></param> /// <param name="dataList">will be populated with the data properties of the entity</param> /// <param name="navList">will be populated with the navigation properties of the entity</param> void AddClassProperties(IClassMetadata meta, PersistentClass pClass, List<Dictionary<string, object>> dataList, List<Dictionary<string, object>> navList) { // maps column names to their related data properties. Used in MakeAssociationProperty to convert FK column names to entity property names. var relatedDataPropertyMap = new Dictionary<string, Dictionary<string, object>>(); var persister = meta as AbstractEntityPersister; var type = pClass.MappedClass; var propNames = meta.PropertyNames; var propTypes = meta.PropertyTypes; var propNull = meta.PropertyNullability; for (int i = 0; i < propNames.Length; i++) { var propName = propNames[i]; var pClassProp = pClass.GetProperty(propName); if (!hasOwnProperty(pClass, pClassProp)) continue; // skip property defined on superclass var propType = propTypes[i]; if (!propType.IsAssociationType) // skip association types until we handle all the data types, so the relatedDataPropertyMap will be populated. { var propColumns = pClassProp.ColumnIterator.ToList(); if (propType.IsComponentType) { // complex type var compType = (ComponentType)propType; var complexTypeName = AddComponent(compType, propColumns); var compMap = new Dictionary<string, object>(); compMap.Add("nameOnServer", propName); compMap.Add("complexTypeName", complexTypeName); compMap.Add("isNullable", propNull[i]); dataList.Add(compMap); } else { // data property var col = propColumns.Count() == 1 ? propColumns[0] as Column : null; var isKey = meta.HasNaturalIdentifier && meta.NaturalIdentifierProperties.Contains(i); var isVersion = meta.IsVersioned && i == meta.VersionProperty; var dmap = MakeDataProperty(propName, propType.Name, propNull[i], col, isKey, isVersion); dataList.Add(dmap); var columnNameString = GetPropertyColumnNames(persister, propName); relatedDataPropertyMap.Add(columnNameString, dmap); } } } // Hibernate identifiers are excluded from the list of data properties, so we have to add them separately if (meta.HasIdentifierProperty && hasOwnProperty(pClass, meta.IdentifierPropertyName)) { var dmap = MakeDataProperty(meta.IdentifierPropertyName, meta.IdentifierType.Name, false, null, true, false); dataList.Insert(0, dmap); var columnNameString = GetPropertyColumnNames(persister, meta.IdentifierPropertyName); relatedDataPropertyMap.Add(columnNameString, dmap); } else if (meta.IdentifierType != null && meta.IdentifierType.IsComponentType && pClass.Identifier is Component && ((Component)pClass.Identifier).Owner == pClass) { // composite key is a ComponentType var compType = (ComponentType)meta.IdentifierType; var compNames = compType.PropertyNames; for (int i = 0; i < compNames.Length; i++) { var compName = compNames[i]; var propType = compType.Subtypes[i]; if (!propType.IsAssociationType) { var dmap = MakeDataProperty(compName, propType.Name, compType.PropertyNullability[i], null, true, false); dataList.Insert(0, dmap); } else { var manyToOne = propType as ManyToOneType; //var joinable = manyToOne.GetAssociatedJoinable(this._sessionFactory); var propColumnNames = GetPropertyColumnNames(persister, compName); var assProp = MakeAssociationProperty(type, (IAssociationType)propType, compName, propColumnNames, pClass, relatedDataPropertyMap, true); navList.Add(assProp); } } } // We do the association properties after the data properties, so we can do the foreign key lookups for (int i = 0; i < propNames.Length; i++) { var propName = propNames[i]; if (!hasOwnProperty(pClass, propName)) continue; // skip property defined on superclass var propType = propTypes[i]; if (propType.IsAssociationType) { // navigation property var propColumnNames = GetPropertyColumnNames(persister, propName); var assProp = MakeAssociationProperty(type, (IAssociationType)propType, propName, propColumnNames, pClass, relatedDataPropertyMap, false); navList.Add(assProp); } } }
private IDictionary<string, string[]> BindPropertyResults(string alias, HbmReturnDiscriminator discriminatorSchema, HbmReturnProperty[] returnProperties, PersistentClass pc) { Dictionary<string, string[]> propertyresults = new Dictionary<string, string[]>(); // maybe a concrete SQLpropertyresult type, but Map is exactly what is required at the moment if (discriminatorSchema != null) { propertyresults["class"] = GetResultColumns(discriminatorSchema).ToArray(); } List<HbmReturnProperty> properties = new List<HbmReturnProperty>(); List<string> propertyNames = new List<string>(); foreach (HbmReturnProperty returnPropertySchema in returnProperties ?? new HbmReturnProperty[0]) { string name = returnPropertySchema.name; if (pc == null || name.IndexOf('.') == -1) { //if dotted and not load-collection nor return-join //regular property properties.Add(returnPropertySchema); propertyNames.Add(name); } else { // Reorder properties // 1. get the parent property // 2. list all the properties following the expected one in the parent property // 3. calculate the lowest index and insert the property int dotIndex = name.LastIndexOf('.'); string reducedName = name.Substring(0, dotIndex); IValue value = pc.GetRecursiveProperty(reducedName).Value; IEnumerable<Mapping.Property> parentPropIter; if (value is Component) { Component comp = (Component) value; parentPropIter = comp.PropertyIterator; } else if (value is ToOne) { ToOne toOne = (ToOne) value; PersistentClass referencedPc = mappings.GetClass(toOne.ReferencedEntityName); if (toOne.ReferencedPropertyName != null) try { parentPropIter = ((Component) referencedPc.GetRecursiveProperty(toOne.ReferencedPropertyName).Value).PropertyIterator; } catch (InvalidCastException e) { throw new MappingException("dotted notation reference neither a component nor a many/one to one", e); } else try { parentPropIter = ((Component) referencedPc.IdentifierProperty.Value).PropertyIterator; } catch (InvalidCastException e) { throw new MappingException("dotted notation reference neither a component nor a many/one to one", e); } } else throw new MappingException("dotted notation reference neither a component nor a many/one to one"); bool hasFollowers = false; List<string> followers = new List<string>(); foreach (Mapping.Property prop in parentPropIter) { string currentPropertyName = prop.Name; string currentName = reducedName + '.' + currentPropertyName; if (hasFollowers) followers.Add(currentName); if (name.Equals(currentName)) hasFollowers = true; } int index = propertyNames.Count; int followersSize = followers.Count; for (int loop = 0; loop < followersSize; loop++) { string follower = followers[loop]; int currentIndex = GetIndexOfFirstMatchingProperty(propertyNames, follower); index = currentIndex != -1 && currentIndex < index ? currentIndex : index; } propertyNames.Insert(index, name); properties.Insert(index, returnPropertySchema); } } var uniqueReturnProperty = new HashSet<string>(); foreach (HbmReturnProperty returnPropertySchema in properties) { string name = returnPropertySchema.name; if ("class".Equals(name)) throw new MappingException( "class is not a valid property name to use in a <return-property>, use <return-discriminator> instead" ); //TODO: validate existing of property with the chosen name. (secondpass ) List<string> allResultColumns = GetResultColumns(returnPropertySchema); if (allResultColumns.Count == 0) throw new MappingException( "return-property for alias " + alias + " must specify at least one column or return-column name" ); if (uniqueReturnProperty.Contains(name)) throw new MappingException( "duplicate return-property for property " + name + " on alias " + alias ); uniqueReturnProperty.Add(name); // the issue here is that for <return-join/> representing an entity collection, // the collection element values (the property values of the associated entity) // are represented as 'element.{propertyname}'. Thus the StringHelper.root() // here puts everything under 'element' (which additionally has significant // meaning). Probably what we need to do is to something like this instead: // String root = StringHelper.root( name ); // String key = root; // by default // if ( !root.equals( name ) ) { // // we had a dot // if ( !root.equals( alias ) { // // the root does not apply to the specific alias // if ( "elements".equals( root ) { // // we specifically have a <return-join/> representing an entity collection // // and this <return-property/> is one of that entity's properties // key = name; // } // } // } // but I am not clear enough on the intended purpose of this code block, especially // in relation to the "Reorder properties" code block above... // String key = StringHelper.root( name ); string key = name; string[] intermediateResults; if (!propertyresults.TryGetValue(key,out intermediateResults)) propertyresults[key] = allResultColumns.ToArray(); else { throw new NotImplementedException(); // 2013-02-24: In 89994bc113e1bb35bf6bcd0b7408d08340bfbccd, 2008-05-29, the intermediateResults // variable was changed from ArrayList to string[]. The following code line was there before. // Since an array cannot be modified, it seems this code line has never been hit since then. // While working on NH-3345, I'm adding an ambigous overload for AddAll(), and I don't want to // try to understand this code right now, so comment it out instead. /Oskar //ArrayHelper.AddAll(intermediateResults, allResultColumns); // TODO: intermediateResults not used after this } } Dictionary<string, string[]> newPropertyResults = new Dictionary<string, string[]>(); foreach (KeyValuePair<string, string[]> entry in propertyresults) { newPropertyResults[entry.Key] = entry.Value; } return newPropertyResults.Count == 0 ? (IDictionary<string, string[]>)new CollectionHelper.EmptyMapClass<string, string[]>() : newPropertyResults; }