alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Hibernate example source code file (CollectionMetadataGenerator.java)

This example Hibernate source code file (CollectionMetadataGenerator.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Hibernate tags/keywords

collection, commoncollectionmapperdata, element, element, idmappingdata, middlecomponentdata, middlecomponentdata, middleiddata, onetomany, persistentclass, string, string, suppresswarnings, suppresswarnings, util

The Hibernate CollectionMetadataGenerator.java source code

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Middleware LLC.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.envers.configuration.metadata;

import org.dom4j.Element;
import org.hibernate.MappingException;
import org.hibernate.envers.ModificationStore;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.entities.EntityConfiguration;
import org.hibernate.envers.entities.IdMappingData;
import org.hibernate.envers.entities.PropertyData;
import org.hibernate.envers.entities.mapper.CompositeMapperBuilder;
import org.hibernate.envers.entities.mapper.PropertyMapper;
import org.hibernate.envers.entities.mapper.SinglePropertyMapper;
import org.hibernate.envers.entities.mapper.id.IdMapper;
import org.hibernate.envers.entities.mapper.relation.*;
import org.hibernate.envers.entities.mapper.relation.component.*;
import org.hibernate.envers.entities.mapper.relation.lazy.proxy.*;
import org.hibernate.envers.entities.mapper.relation.query.OneAuditEntityQueryGenerator;
import org.hibernate.envers.entities.mapper.relation.query.RelationQueryGenerator;
import org.hibernate.envers.internal.EnversMessageLogger;
import org.hibernate.envers.tools.MappingTools;
import org.hibernate.envers.tools.StringTools;
import org.hibernate.envers.tools.Tools;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.*;
import org.hibernate.type.*;
import org.jboss.logging.Logger;

import javax.persistence.JoinColumn;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Generates metadata for a collection-valued property.
 * @author Adam Warski (adam at warski dot org)
 * @author Hern�n Chanfreau
 */
public final class CollectionMetadataGenerator {

    public static final EnversMessageLogger LOG = Logger.getMessageLogger(EnversMessageLogger.class, CollectionMetadataGenerator.class.getName());

    private final AuditMetadataGenerator mainGenerator;
    private final String propertyName;
    private final Collection propertyValue;
    private final CompositeMapperBuilder currentMapper;
    private final String referencingEntityName;
    private final EntityXmlMappingData xmlMappingData;
    private final PropertyAuditingData propertyAuditingData;

    private final EntityConfiguration referencingEntityConfiguration;
    /**
     * Null if this collection isn't a relation to another entity.
     */
    private final String referencedEntityName;

	/**
     * @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 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());
    }

    void addCollection() {
        Type type = propertyValue.getType();

        boolean oneToManyAttachedType = type instanceof BagType || type instanceof SetType || type instanceof MapType || type instanceof ListType;
        boolean inverseOneToMany = (propertyValue.getElement() instanceof OneToMany) && (propertyValue.isInverse());
        boolean fakeOneToManyBidirectional = (propertyValue.getElement() instanceof OneToMany) && (propertyAuditingData.getAuditMappedBy() != null);

        if (oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional)) {
            // 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 MiddleIdData createMiddleIdData(IdMappingData idMappingData, String prefix, String entityName) {
        return new MiddleIdData(mainGenerator.getVerEntCfg(), idMappingData, prefix, entityName,
                mainGenerator.getEntitiesConfigurations().containsKey(entityName));
    }

    @SuppressWarnings({"unchecked"})
    private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
        LOG.debugf("Adding audit mapping for property %s.%s: one-to-many collection, using a join column on the referenced entity",
                   referencingEntityName,
                   propertyName);

        String mappedBy = getMappedBy(propertyValue);

        IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
                    referencedEntityName, propertyAuditingData, false);
        IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData();

        // Generating the id mappers data for the referencing side of the relation.
        MiddleIdData referencingIdData = createMiddleIdData(referencingIdMapping,
                mappedBy + "_", referencingEntityName);

        // And for the referenced side. The prefixed mapper won't be used (as this collection isn't persisted
        // in a join table, so the prefix value is arbitrary).
        MiddleIdData referencedIdData = createMiddleIdData(referencedIdMapping,
                null, referencedEntityName);

        // Generating the element mapping.
        MiddleComponentData elementComponentData = new MiddleComponentData(
                new MiddleRelatedComponentMapper(referencedIdData), 0);

        // Generating the index mapping, if an index exists. It can only exists in case a javax.persistence.MapKey
        // annotation is present on the entity. So the middleEntityXml will be not be used. The queryGeneratorBuilder
        // will only be checked for nullnes.
        MiddleComponentData indexComponentData = addIndex(null, null);

        // Generating the query generator - it should read directly from the related entity.
        RelationQueryGenerator queryGenerator = new OneAuditEntityQueryGenerator(mainGenerator.getGlobalCfg(),
                mainGenerator.getVerEntCfg(), mainGenerator.getAuditStrategy(),
                referencingIdData, referencedEntityName, referencedIdData);

        // Creating common mapper data.
        CommonCollectionMapperData commonCollectionMapperData = new CommonCollectionMapperData(
                mainGenerator.getVerEntCfg(), referencedEntityName,
                propertyAuditingData.getPropertyData(),
                referencingIdData, queryGenerator);

        PropertyMapper fakeBidirectionalRelationMapper;
        PropertyMapper fakeBidirectionalRelationIndexMapper;
        if (fakeOneToManyBidirectional) {
            // In case of a fake many-to-one bidirectional relation, we have to generate a mapper which maps
            // the mapped-by property name to the id of the related entity (which is the owner of the collection).
            String auditMappedBy = propertyAuditingData.getAuditMappedBy();

            // Creating a prefixed relation mapper.
            IdMapper relMapper = referencingIdMapping.getIdMapper().prefixMappedProperties(
                    MappingTools.createToOneRelationPrefix(auditMappedBy));

            fakeBidirectionalRelationMapper = new ToOneIdMapper(
                    relMapper,
                    // The mapper will only be used to map from entity to map, so no need to provide other details
                    // when constructing the PropertyData.
                    new PropertyData(auditMappedBy, null, null, null),
                    referencingEntityName, false);

            // Checking if there's an index defined. If so, adding a mapper for it.
            if (propertyAuditingData.getPositionMappedBy() != null) {
                String positionMappedBy = propertyAuditingData.getPositionMappedBy();
                fakeBidirectionalRelationIndexMapper = new SinglePropertyMapper(new PropertyData(positionMappedBy, null, null, null));

                // Also, overwriting the index component data to properly read the index.
                indexComponentData = new MiddleComponentData(new MiddleStraightComponentMapper(positionMappedBy), 0);
            } else {
                fakeBidirectionalRelationIndexMapper = null;
            }
        } else {
            fakeBidirectionalRelationMapper = null;
            fakeBidirectionalRelationIndexMapper = null;
        }

        // Checking the type of the collection and adding an appropriate mapper.
        addMapper(commonCollectionMapperData, elementComponentData, indexComponentData);

        // Storing information about this relation.
        referencingEntityConfiguration.addToManyNotOwningRelation(propertyName, mappedBy,
                referencedEntityName, referencingIdData.getPrefixedMapper(), fakeBidirectionalRelationMapper,
                fakeBidirectionalRelationIndexMapper);
    }

    /**
     * Adds mapping of the id of a related entity to the given xml mapping, prefixing the id with the given prefix.
     * @param xmlMapping Mapping, to which to add the xml.
     * @param prefix Prefix for the names of properties which will be prepended to properties that form the id.
     * @param columnNameIterator Iterator over the column names that will be used for properties that form the id.
     * @param relatedIdMapping Id mapping data of the related entity.
     */
    @SuppressWarnings({"unchecked"})
    private void addRelatedToXmlMapping(Element xmlMapping, String prefix,
                                        MetadataTools.ColumnNameIterator columnNameIterator,
                                        IdMappingData relatedIdMapping) {
        Element properties = (Element) relatedIdMapping.getXmlRelationMapping().clone();
        MetadataTools.prefixNamesInPropertyElement(properties, prefix, columnNameIterator, true, true);
        for (Element idProperty : (java.util.List<Element>) properties.elements()) {
            xmlMapping.add((Element) idProperty.clone());
        }
    }

    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);
    }

    private MiddleComponentData addIndex(Element middleEntityXml, QueryGeneratorBuilder queryGeneratorBuilder) {
        if (propertyValue instanceof IndexedCollection) {
            IndexedCollection indexedValue = (IndexedCollection) propertyValue;
            String mapKey = propertyAuditingData.getMapKey();
            if (mapKey == null) {
                // This entity doesn't specify a javax.persistence.MapKey. Mapping it to the middle entity.
                return addValueToMiddleTable(indexedValue.getIndex(), middleEntityXml,
                        queryGeneratorBuilder, "mapkey", null);
            } else {
                IdMappingData referencedIdMapping = mainGenerator.getEntitiesConfigurations()
                        .get(referencedEntityName).getIdMappingData();
                int currentIndex = queryGeneratorBuilder == null ? 0 : queryGeneratorBuilder.getCurrentIndex();
                if ("".equals(mapKey)) {
                    // The key of the map is the id of the entity.
                    return new MiddleComponentData(new MiddleMapKeyIdComponentMapper(mainGenerator.getVerEntCfg(),
                            referencedIdMapping.getIdMapper()), currentIndex);
                } else {
                    // The key of the map is a property of the entity.
                    return new MiddleComponentData(new MiddleMapKeyPropertyComponentMapper(mapKey,
                            propertyAuditingData.getAccessType()), currentIndex);
                }
            }
        } else {
            // No index - creating a dummy mapper.
            return new MiddleComponentData(new MiddleDummyComponentMapper(), 0);
        }
    }

    /**
     *
     * @param value Value, which should be mapped to the middle-table, either as a relation to another entity,
     * or as a simple value.
     * @param xmlMapping If not <code>null, xml mapping for this value is added to this element.
     * @param queryGeneratorBuilder In case <code>value is a relation to another entity, information about it
     * should be added to the given.
     * @param prefix Prefix for proeprty names of related entities identifiers.
     * @param joinColumns Names of columns to use in the xml mapping, if this array isn't null and has any elements.
     * @return Data for mapping this component.
     */
    @SuppressWarnings({"unchecked"})
    private MiddleComponentData addValueToMiddleTable(Value value, Element xmlMapping,
                                                      QueryGeneratorBuilder queryGeneratorBuilder,
                                                      String prefix, JoinColumn[] joinColumns) {
        Type type = value.getType();
        if (type instanceof ManyToOneType) {
            String prefixRelated = prefix + "_";

            String referencedEntityName = MappingTools.getReferencedEntityName(value);

            IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
                    referencedEntityName, propertyAuditingData, true);

            // Adding related-entity (in this case: the referenced entities id) id mapping to the xml only if the
            // relation isn't inverse (so when <code>xmlMapping is not null).
            if (xmlMapping != null) {
                addRelatedToXmlMapping(xmlMapping, prefixRelated,
                        joinColumns != null && joinColumns.length > 0
                                ? MetadataTools.getColumnNameIterator(joinColumns)
                                : MetadataTools.getColumnNameIterator(value.getColumnIterator()),
                        referencedIdMapping);
            }

            // Storing the id data of the referenced entity: original mapper, prefixed mapper and entity name.
            MiddleIdData referencedIdData = createMiddleIdData(referencedIdMapping,
                    prefixRelated, referencedEntityName);
            // And adding it to the generator builder.
            queryGeneratorBuilder.addRelation(referencedIdData);

            return new MiddleComponentData(new MiddleRelatedComponentMapper(referencedIdData),
                    queryGeneratorBuilder.getCurrentIndex());
        } else {
            // Last but one parameter: collection components are always insertable
            boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(xmlMapping,
                    new PropertyAuditingData(prefix, "field", ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null, false),
                    value, null, true, true);

            if (mapped) {
                // Simple values are always stored in the first item of the array returned by the query generator.
                return new MiddleComponentData(new MiddleSimpleComponentMapper(mainGenerator.getVerEntCfg(), prefix), 0);
            } else {
                mainGenerator.throwUnsupportedTypeException(type, referencingEntityName, propertyName);
                // Impossible to get here.
                throw new AssertionError();
            }
        }
    }

    private void addMapper(CommonCollectionMapperData commonCollectionMapperData, MiddleComponentData elementComponentData,
                           MiddleComponentData indexComponentData) {
        Type type = propertyValue.getType();
        if (type instanceof SortedSetType) {
			currentMapper.addComposite(propertyAuditingData.getPropertyData(),
					new SortedSetCollectionMapper(commonCollectionMapperData,
							TreeSet.class, SortedSetProxy.class, elementComponentData, propertyValue.getComparator()));
		} else if (type instanceof SetType) {
			currentMapper.addComposite(propertyAuditingData.getPropertyData(),
                    new BasicCollectionMapper<Set>(commonCollectionMapperData,
                    HashSet.class, SetProxy.class, elementComponentData));
        } else if (type instanceof SortedMapType) {
            // Indexed collection, so <code>indexComponentData is not null.
			currentMapper.addComposite(propertyAuditingData.getPropertyData(),
					new SortedMapCollectionMapper(commonCollectionMapperData,
							TreeMap.class, SortedMapProxy.class, elementComponentData, indexComponentData, propertyValue.getComparator()));
        } else if (type instanceof MapType) {
            // Indexed collection, so <code>indexComponentData is not null.
            currentMapper.addComposite(propertyAuditingData.getPropertyData(),
                    new MapCollectionMapper<Map>(commonCollectionMapperData,
                    HashMap.class, MapProxy.class, elementComponentData, indexComponentData));
        } else if (type instanceof BagType) {
            currentMapper.addComposite(propertyAuditingData.getPropertyData(),
                    new BasicCollectionMapper<List>(commonCollectionMapperData,
                    ArrayList.class, ListProxy.class, elementComponentData));
        } else if (type instanceof ListType) {
            // Indexed collection, so <code>indexComponentData is not null.
            currentMapper.addComposite(propertyAuditingData.getPropertyData(),
                    new ListCollectionMapper(commonCollectionMapperData,
                    elementComponentData, indexComponentData));
        } else {
            mainGenerator.throwUnsupportedTypeException(type, referencingEntityName, propertyName);
        }
    }

    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 Element createMiddleEntityXml(String auditMiddleTableName, String auditMiddleEntityName, String where) {
        String schema = mainGenerator.getSchema(propertyAuditingData.getJoinTable().schema(), propertyValue.getCollectionTable());
        String catalog = mainGenerator.getCatalog(propertyAuditingData.getJoinTable().catalog(), propertyValue.getCollectionTable());

        Element middleEntityXml = MetadataTools.createEntity(xmlMappingData.newAdditionalMapping(),
                new AuditTableData(auditMiddleEntityName, auditMiddleTableName, schema, catalog), null);
        Element middleEntityXmlId = middleEntityXml.addElement("composite-id");

        // If there is a where clause on the relation, adding it to the middle entity.
        if (where != null) {
            middleEntityXml.addAttribute("where", where);
        }

        middleEntityXmlId.addAttribute("name", mainGenerator.getVerEntCfg().getOriginalIdPropName());

        // Adding the revision number as a foreign key to the revision info entity to the composite id of the
        // middle table.
        mainGenerator.addRevisionInfoRelation(middleEntityXmlId);

        // Adding the revision type property to the entity xml.
        mainGenerator.addRevisionType(middleEntityXml);

        // All other properties should also be part of the primary key of the middle entity.
        return middleEntityXmlId;
    }

    private String getMappedBy(Collection collectionValue) {
        PersistentClass referencedClass = ((OneToMany) collectionValue.getElement()).getAssociatedClass();

        // 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;
    }

    @SuppressWarnings({"unchecked"})
    private String searchMappedBy(PersistentClass referencedClass, Collection collectionValue) {
        Iterator<Property> assocClassProps = referencedClass.getPropertyIterator();
        while (assocClassProps.hasNext()) {
            Property property = assocClassProps.next();

            if (Tools.iteratorsContentEqual(property.getValue().getColumnIterator(),
                    collectionValue.getKey().getColumnIterator())) {
                return property.getName();
            }
        }
        return null;
    }

    private String getMappedBy(Table collectionTable, PersistentClass referencedClass) {
        // 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, collectionTable);

        // not found on referenced class, searching on superclasses
        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(), collectionTable);
                tempClass = tempClass.getSuperclass();
			}
        }

        if(mappedBy == null) {
	        throw new MappingException("Unable to read the mapped by attribute for " + propertyName + " in "
	                + referencedClass.getClassName() + "!");
        }

        return mappedBy;
    }

    @SuppressWarnings({"unchecked"})
    private String searchMappedBy(PersistentClass referencedClass, Table collectionTable) {
        Iterator<Property> properties = referencedClass.getPropertyIterator();
        while (properties.hasNext()) {
            Property property = properties.next();
            if (property.getValue() instanceof Collection) {
                // The equality is intentional. We want to find a collection property with the same collection table.
                //noinspection ObjectEquality
                if (((Collection) property.getValue()).getCollectionTable() == collectionTable) {
                    return property.getName();
                }
            }
        }
        return null;
    }

}

Other Hibernate examples (source code examples)

Here is a short list of links related to this Hibernate CollectionMetadataGenerator.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.