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();
    }
  }
 private void storeMiddleEntityRelationInformation(String mappedBy) {
   // Only if this is a relation (when there is a referenced entity).
   if (referencedEntityName != null) {
     if (propertyValue.isInverse()) {
       referencingEntityConfiguration.addToManyMiddleNotOwningRelation(
           propertyName, mappedBy, referencedEntityName);
     } else {
       referencingEntityConfiguration.addToManyMiddleRelation(propertyName, referencedEntityName);
     }
   }
 }
 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();
 }
  @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);
  }