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);
    }
  }
Beispiel #8
0
  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());
  }