private String getMiddleTableName(Collection value, String entityName) { // We check how Hibernate maps the collection. if (value.getElement() instanceof OneToMany && !value.isInverse()) { // This must be a @JoinColumn+@OneToMany mapping. Generating the table name, as Hibernate // doesn't use a // middle table for mapping this relation. return StringTools.getLastComponent(entityName) + "_" + StringTools.getLastComponent(MappingTools.getReferencedEntityName(value.getElement())); } // Hibernate uses a middle table for mapping this relation, so we get it's name directly. return value.getCollectionTable().getName(); }
void addCollection() { Type type = propertyValue.getType(); Value value = propertyValue.getElement(); boolean oneToManyAttachedType = type instanceof BagType || type instanceof SetType || type instanceof MapType || type instanceof ListType; boolean inverseOneToMany = (value instanceof OneToMany) && (propertyValue.isInverse()); boolean owningManyToOneWithJoinTableBidirectional = (value instanceof ManyToOne) && (propertyAuditingData.getAuditMappedBy() != null); boolean fakeOneToManyBidirectional = (value instanceof OneToMany) && (propertyAuditingData.getAuditMappedBy() != null); if (oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional || owningManyToOneWithJoinTableBidirectional)) { // A one-to-many relation mapped using @ManyToOne and @OneToMany(mappedBy="...") addOneToManyAttached(fakeOneToManyBidirectional); } else { // All other kinds of relations require a middle (join) table. addWithMiddleTable(); } }
/** * @param mainGenerator Main generator, giving access to configuration and the basic mapper. * @param propertyValue Value of the collection, as mapped by Hibernate. * @param currentMapper Mapper, to which the appropriate {@link * org.hibernate.envers.entities.mapper.PropertyMapper} will be added. * @param referencingEntityName Name of the entity that owns this collection. * @param xmlMappingData In case this collection requires a middle table, additional mapping * documents will be created using this object. * @param propertyAuditingData Property auditing (meta-)data. Among other things, holds the name * of the property that references the collection in the referencing entity, the user data for * middle (join) table and the value of the <code>@MapKey</code> annotation, if there was one. */ public CollectionMetadataGenerator( AuditMetadataGenerator mainGenerator, Collection propertyValue, CompositeMapperBuilder currentMapper, String referencingEntityName, EntityXmlMappingData xmlMappingData, PropertyAuditingData propertyAuditingData) { this.mainGenerator = mainGenerator; this.propertyValue = propertyValue; this.currentMapper = currentMapper; this.referencingEntityName = referencingEntityName; this.xmlMappingData = xmlMappingData; this.propertyAuditingData = propertyAuditingData; this.propertyName = propertyAuditingData.getName(); referencingEntityConfiguration = mainGenerator.getEntitiesConfigurations().get(referencingEntityName); if (referencingEntityConfiguration == null) { throw new MappingException( "Unable to read auditing configuration for " + referencingEntityName + "!"); } referencedEntityName = MappingTools.getReferencedEntityName(propertyValue.getElement()); }
private String getMappedBy(Collection collectionValue) { PersistentClass referencedClass = null; if (collectionValue.getElement() instanceof OneToMany) { OneToMany oneToManyValue = (OneToMany) collectionValue.getElement(); referencedClass = oneToManyValue.getAssociatedClass(); } else if (collectionValue.getElement() instanceof ManyToOne) { // Case for bi-directional relation with @JoinTable on the owning @ManyToOne side. ManyToOne manyToOneValue = (ManyToOne) collectionValue.getElement(); referencedClass = manyToOneValue.getMappings().getClass(manyToOneValue.getReferencedEntityName()); } // If there's an @AuditMappedBy specified, returning it directly. String auditMappedBy = propertyAuditingData.getAuditMappedBy(); if (auditMappedBy != null) { return auditMappedBy; } // searching in referenced class String mappedBy = this.searchMappedBy(referencedClass, collectionValue); if (mappedBy == null) { LOG.debugf( "Going to search the mapped by attribute for %s in superclasses of entity: %s", propertyName, referencedClass.getClassName()); PersistentClass tempClass = referencedClass; while ((mappedBy == null) && (tempClass.getSuperclass() != null)) { LOG.debugf("Searching in superclass: %s", tempClass.getSuperclass().getClassName()); mappedBy = this.searchMappedBy(tempClass.getSuperclass(), collectionValue); tempClass = tempClass.getSuperclass(); } } if (mappedBy == null) { throw new MappingException( "Unable to read the mapped by attribute for " + propertyName + " in " + referencedClass.getClassName() + "!"); } return mappedBy; }
public void doSecondPass(java.util.Map persistentClasses, java.util.Map inheritedMetas) throws MappingException { if (log.isDebugEnabled()) log.debug("Second pass for collection: " + collection.getRole()); secondPass(persistentClasses, inheritedMetas); collection.createAllKeys(); if (log.isDebugEnabled()) { String msg = "Mapped collection key: " + columns(collection.getKey()); if (collection.isIndexed()) msg += ", index: " + columns(((IndexedCollection) collection).getIndex()); if (collection.isOneToMany()) { msg += ", one-to-many: " + ((OneToMany) collection.getElement()).getReferencedEntityName(); } else { msg += ", element: " + columns(collection.getElement()); } log.debug(msg); } }
private static void collectComponents( Map<String, Component> components, Iterator<Property> iter) { while (iter.hasNext()) { Property property = iter.next(); if (!"embedded".equals(property.getPropertyAccessorName()) && // HBX-267, embedded property for <properties> should not be generated as component. property.getValue() instanceof Component) { Component comp = (Component) property.getValue(); addComponent(components, comp); } else if (property.getValue() instanceof Collection) { // compisite-element in collection Collection collection = (Collection) property.getValue(); if (collection.getElement() instanceof Component) { Component comp = (Component) collection.getElement(); addComponent(components, comp); } } } }
public void doSecondPass(java.util.Map persistentClasses) throws MappingException { if (log.isDebugEnabled()) log.debug("Second pass for collection: " + collection.getRole()); secondPass( persistentClasses, localInheritedMetas); // using local since the inheritedMetas at this point is not the // correct map since it is always the empty map collection.createAllKeys(); if (log.isDebugEnabled()) { String msg = "Mapped collection key: " + columns(collection.getKey()); if (collection.isIndexed()) msg += ", index: " + columns(((IndexedCollection) collection).getIndex()); if (collection.isOneToMany()) { msg += ", one-to-many: " + ((OneToMany) collection.getElement()).getReferencedEntityName(); } else { msg += ", element: " + columns(collection.getElement()); } log.debug(msg); } }
public static void bindCollectionSecondPass( Collection collection, java.util.Map persistentClasses, Mappings mappings, java.util.Map inheritedMetas) throws MappingException { if (collection.isOneToMany()) { OneToMany oneToMany = (OneToMany) collection.getElement(); PersistentClass persistentClass = mappings.getClass(oneToMany.getReferencedEntityName()); if (persistentClass == null) throw new MappingException( "Association " + collection.getRole() + " references unmapped class: " + oneToMany.getReferencedEntityName()); oneToMany.setAssociatedClass(persistentClass); // Child } }
@SuppressWarnings({"unchecked"}) private void addWithMiddleTable() { LOG.debugf( "Adding audit mapping for property %s.%s: collection with a join table", referencingEntityName, propertyName); // Generating the name of the middle table String auditMiddleTableName; String auditMiddleEntityName; if (!StringTools.isEmpty(propertyAuditingData.getJoinTable().name())) { auditMiddleTableName = propertyAuditingData.getJoinTable().name(); auditMiddleEntityName = propertyAuditingData.getJoinTable().name(); } else { String middleTableName = getMiddleTableName(propertyValue, referencingEntityName); auditMiddleTableName = mainGenerator.getVerEntCfg().getAuditTableName(null, middleTableName); auditMiddleEntityName = mainGenerator.getVerEntCfg().getAuditEntityName(middleTableName); } LOG.debugf("Using join table name: %s", auditMiddleTableName); // Generating the XML mapping for the middle entity, only if the relation isn't inverse. // If the relation is inverse, will be later checked by comparing middleEntityXml with null. Element middleEntityXml; if (!propertyValue.isInverse()) { // Generating a unique middle entity name auditMiddleEntityName = mainGenerator.getAuditEntityNameRegister().createUnique(auditMiddleEntityName); // Registering the generated name mainGenerator.getAuditEntityNameRegister().register(auditMiddleEntityName); middleEntityXml = createMiddleEntityXml( auditMiddleTableName, auditMiddleEntityName, propertyValue.getWhere()); } else { middleEntityXml = null; } // ****** // Generating the mapping for the referencing entity (it must be an entity). // ****** // Getting the id-mapping data of the referencing entity (the entity that "owns" this // collection). IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData(); // Only valid for an inverse relation; null otherwise. String mappedBy; // The referencing prefix is always for a related entity. So it has always the "_" at the end // added. String referencingPrefixRelated; String referencedPrefix; if (propertyValue.isInverse()) { // If the relation is inverse, then referencedEntityName is not null. mappedBy = getMappedBy( propertyValue.getCollectionTable(), mainGenerator.getCfg().getClassMapping(referencedEntityName)); referencingPrefixRelated = mappedBy + "_"; referencedPrefix = StringTools.getLastComponent(referencedEntityName); } else { mappedBy = null; referencingPrefixRelated = StringTools.getLastComponent(referencingEntityName) + "_"; referencedPrefix = referencedEntityName == null ? "element" : propertyName; } // Storing the id data of the referencing entity: original mapper, prefixed mapper and entity // name. MiddleIdData referencingIdData = createMiddleIdData(referencingIdMapping, referencingPrefixRelated, referencingEntityName); // Creating a query generator builder, to which additional id data will be added, in case this // collection // references some entities (either from the element or index). At the end, this will be used to // build // a query generator to read the raw data collection from the middle table. QueryGeneratorBuilder queryGeneratorBuilder = new QueryGeneratorBuilder( mainGenerator.getGlobalCfg(), mainGenerator.getVerEntCfg(), mainGenerator.getAuditStrategy(), referencingIdData, auditMiddleEntityName); // Adding the XML mapping for the referencing entity, if the relation isn't inverse. if (middleEntityXml != null) { // Adding related-entity (in this case: the referencing's entity id) id mapping to the xml. addRelatedToXmlMapping( middleEntityXml, referencingPrefixRelated, MetadataTools.getColumnNameIterator(propertyValue.getKey().getColumnIterator()), referencingIdMapping); } // ****** // Generating the element mapping. // ****** MiddleComponentData elementComponentData = addValueToMiddleTable( propertyValue.getElement(), middleEntityXml, queryGeneratorBuilder, referencedPrefix, propertyAuditingData.getJoinTable().inverseJoinColumns()); // ****** // Generating the index mapping, if an index exists. // ****** MiddleComponentData indexComponentData = addIndex(middleEntityXml, queryGeneratorBuilder); // ****** // Generating the property mapper. // ****** // Building the query generator. RelationQueryGenerator queryGenerator = queryGeneratorBuilder.build(elementComponentData, indexComponentData); // Creating common data CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData( mainGenerator.getVerEntCfg(), auditMiddleEntityName, propertyAuditingData.getPropertyData(), referencingIdData, queryGenerator); // Checking the type of the collection and adding an appropriate mapper. addMapper(commonCollectionMapperData, elementComponentData, indexComponentData); // ****** // Storing information about this relation. // ****** storeMiddleEntityRelationInformation(mappedBy); }
/** * Here is most of the nuts and bolts of this factory, where we interpret the known JPA metadata * against the known Hibernate metadata and build a descriptor for the attribute. * * @param attributeContext The attribute to be described * @param memberResolver Strategy for how to resolve the member defining the attribute. * @param <X> The owner type * @param <Y> The attribute type * @return The attribute description */ @SuppressWarnings({"unchecked"}) private <X, Y> AttributeMetadata<X, Y> determineAttributeMetadata( AttributeContext<X> attributeContext, MemberResolver memberResolver) { LOG.trace( "Starting attribute metadata determination [" + attributeContext.getPropertyMapping().getName() + "]"); final Member member = memberResolver.resolveMember(attributeContext); LOG.trace(" Determined member [" + member + "]"); final Value value = attributeContext.getPropertyMapping().getValue(); final org.hibernate.type.Type type = value.getType(); LOG.trace( " Determined type [name=" + type.getName() + ", class=" + type.getClass().getName() + "]"); if (type.isAnyType()) { // ANY mappings are currently not supported in the JPA metamodel; see HHH-6589 if (context.isIgnoreUnsupported()) { return null; } else { throw new UnsupportedOperationException("ANY not supported"); } } else if (type.isAssociationType()) { // collection or entity if (type.isEntityType()) { // entity return new SingularAttributeMetadataImpl<X, Y>( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, determineSingularAssociationAttributeType(member)); } // collection if (value instanceof Collection) { final Collection collValue = (Collection) value; final Value elementValue = collValue.getElement(); final org.hibernate.type.Type elementType = elementValue.getType(); // First, determine the type of the elements and use that to help determine the // collection type) final Attribute.PersistentAttributeType elementPersistentAttributeType; final Attribute.PersistentAttributeType persistentAttributeType; if (elementType.isAnyType()) { throw new UnsupportedOperationException("collection of any not supported yet"); } final boolean isManyToMany = isManyToMany(member); if (elementValue instanceof Component) { elementPersistentAttributeType = Attribute.PersistentAttributeType.EMBEDDED; persistentAttributeType = Attribute.PersistentAttributeType.ELEMENT_COLLECTION; } else if (elementType.isAssociationType()) { elementPersistentAttributeType = isManyToMany ? Attribute.PersistentAttributeType.MANY_TO_MANY : Attribute.PersistentAttributeType.ONE_TO_MANY; persistentAttributeType = elementPersistentAttributeType; } else { elementPersistentAttributeType = Attribute.PersistentAttributeType.BASIC; persistentAttributeType = Attribute.PersistentAttributeType.ELEMENT_COLLECTION; } final Attribute.PersistentAttributeType keyPersistentAttributeType; // Finally, we determine the type of the map key (if needed) if (value instanceof Map) { final Value keyValue = ((Map) value).getIndex(); final org.hibernate.type.Type keyType = keyValue.getType(); if (keyType.isAnyType()) throw new UnsupportedOperationException("collection of any not supported yet"); if (keyValue instanceof Component) keyPersistentAttributeType = Attribute.PersistentAttributeType.EMBEDDED; else if (keyType.isAssociationType()) keyPersistentAttributeType = Attribute.PersistentAttributeType.MANY_TO_ONE; else keyPersistentAttributeType = Attribute.PersistentAttributeType.BASIC; } else keyPersistentAttributeType = null; return new PluralAttributeMetadataImpl( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, persistentAttributeType, elementPersistentAttributeType, keyPersistentAttributeType); } else if (value instanceof OneToMany) { // TODO : is this even possible??? Really OneToMany should be describing the // element value within a o.h.mapping.Collection (see logic branch above) throw new IllegalArgumentException("HUH???"); // final boolean isManyToMany = isManyToMany( member ); // //one to many with FK => entity // return new PluralAttributeMetadataImpl( // attributeContext.getPropertyMapping(), // attributeContext.getOwnerType(), // member, // isManyToMany // ? Attribute.PersistentAttributeType.MANY_TO_MANY // : Attribute.PersistentAttributeType.ONE_TO_MANY // value, // AttributeContext.TypeStatus.ENTITY, // Attribute.PersistentAttributeType.ONE_TO_MANY, // null, null, null // ); } } else if (attributeContext.getPropertyMapping().isComposite()) { // component return new SingularAttributeMetadataImpl<X, Y>( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, Attribute.PersistentAttributeType.EMBEDDED); } else { // basic type return new SingularAttributeMetadataImpl<X, Y>( attributeContext.getPropertyMapping(), attributeContext.getOwnerType(), member, Attribute.PersistentAttributeType.BASIC); } throw new UnsupportedOperationException( "oops, we are missing something: " + attributeContext.getPropertyMapping()); }