Search in sources :

Example 26 with ManyToOne

use of org.hibernate.mapping.ManyToOne in project hibernate-orm by hibernate.

the class CollectionBinder method bindManytoManyInverseFk.

/**
 * bind the inverse FK of a ManyToMany
 * If we are in a mappedBy case, read the columns from the associated
 * collection element
 * Otherwise delegates to the usual algorithm
 */
public static void bindManytoManyInverseFk(PersistentClass referencedEntity, Ejb3JoinColumn[] columns, SimpleValue value, boolean unique, MetadataBuildingContext buildingContext) {
    final String mappedBy = columns[0].getMappedBy();
    if (StringHelper.isNotEmpty(mappedBy)) {
        final Property property = referencedEntity.getRecursiveProperty(mappedBy);
        Iterator mappedByColumns;
        if (property.getValue() instanceof Collection) {
            mappedByColumns = ((Collection) property.getValue()).getKey().getColumnIterator();
        } else {
            // find the appropriate reference key, can be in a join
            Iterator joinsIt = referencedEntity.getJoinIterator();
            KeyValue key = null;
            while (joinsIt.hasNext()) {
                Join join = (Join) joinsIt.next();
                if (join.containsProperty(property)) {
                    key = join.getKey();
                    break;
                }
            }
            if (key == null)
                key = property.getPersistentClass().getIdentifier();
            mappedByColumns = key.getColumnIterator();
        }
        while (mappedByColumns.hasNext()) {
            Column column = (Column) mappedByColumns.next();
            columns[0].linkValueUsingAColumnCopy(column, value);
        }
        String referencedPropertyName = buildingContext.getMetadataCollector().getPropertyReferencedAssociation("inverse__" + referencedEntity.getEntityName(), mappedBy);
        if (referencedPropertyName != null) {
            // TODO always a many to one?
            ((ManyToOne) value).setReferencedPropertyName(referencedPropertyName);
            buildingContext.getMetadataCollector().addUniquePropertyReference(referencedEntity.getEntityName(), referencedPropertyName);
        }
        ((ManyToOne) value).setReferenceToPrimaryKey(referencedPropertyName == null);
        value.createForeignKey();
    } else {
        BinderHelper.createSyntheticPropertyReference(columns, referencedEntity, null, value, true, buildingContext);
        TableBinder.bindFk(referencedEntity, null, columns, value, unique, buildingContext);
    }
}
Also used : KeyValue(org.hibernate.mapping.KeyValue) Ejb3Column(org.hibernate.cfg.Ejb3Column) MapKeyColumn(javax.persistence.MapKeyColumn) Column(org.hibernate.mapping.Column) IndexColumn(org.hibernate.cfg.IndexColumn) JoinColumn(javax.persistence.JoinColumn) Ejb3JoinColumn(org.hibernate.cfg.Ejb3JoinColumn) Iterator(java.util.Iterator) LazyCollection(org.hibernate.annotations.LazyCollection) Collection(org.hibernate.mapping.Collection) ElementCollection(javax.persistence.ElementCollection) Join(org.hibernate.mapping.Join) Property(org.hibernate.mapping.Property) XProperty(org.hibernate.annotations.common.reflection.XProperty) ManyToOne(org.hibernate.mapping.ManyToOne)

Example 27 with ManyToOne

use of org.hibernate.mapping.ManyToOne in project hibernate-orm by hibernate.

the class CollectionBinder method bindManyToManySecondPass.

private void bindManyToManySecondPass(Collection collValue, Map persistentClasses, Ejb3JoinColumn[] joinColumns, Ejb3JoinColumn[] inverseJoinColumns, Ejb3Column[] elementColumns, boolean isEmbedded, XClass collType, boolean ignoreNotFound, boolean unique, boolean cascadeDeleteEnabled, TableBinder associationTableBinder, XProperty property, PropertyHolder parentPropertyHolder, MetadataBuildingContext buildingContext) throws MappingException {
    if (property == null) {
        throw new IllegalArgumentException("null was passed for argument property");
    }
    final PersistentClass collectionEntity = (PersistentClass) persistentClasses.get(collType.getName());
    final String hqlOrderBy = extractHqlOrderBy(jpaOrderBy);
    boolean isCollectionOfEntities = collectionEntity != null;
    ManyToAny anyAnn = property.getAnnotation(ManyToAny.class);
    if (LOG.isDebugEnabled()) {
        String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
        if (isCollectionOfEntities && unique) {
            LOG.debugf("Binding a OneToMany: %s through an association table", path);
        } else if (isCollectionOfEntities) {
            LOG.debugf("Binding as ManyToMany: %s", path);
        } else if (anyAnn != null) {
            LOG.debugf("Binding a ManyToAny: %s", path);
        } else {
            LOG.debugf("Binding a collection of element: %s", path);
        }
    }
    // check for user error
    if (!isCollectionOfEntities) {
        if (property.isAnnotationPresent(ManyToMany.class) || property.isAnnotationPresent(OneToMany.class)) {
            String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
            throw new AnnotationException("Use of @OneToMany or @ManyToMany targeting an unmapped class: " + path + "[" + collType + "]");
        } else if (anyAnn != null) {
            if (parentPropertyHolder.getJoinTable(property) == null) {
                String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
                throw new AnnotationException("@JoinTable is mandatory when @ManyToAny is used: " + path);
            }
        } else {
            JoinTable joinTableAnn = parentPropertyHolder.getJoinTable(property);
            if (joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0) {
                String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
                throw new AnnotationException("Use of @JoinTable.inverseJoinColumns targeting an unmapped class: " + path + "[" + collType + "]");
            }
        }
    }
    boolean mappedBy = !BinderHelper.isEmptyAnnotationValue(joinColumns[0].getMappedBy());
    if (mappedBy) {
        if (!isCollectionOfEntities) {
            throw new AnnotationException("Collection of elements must not have mappedBy or association reference an unmapped entity: " + collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName());
        }
        Property otherSideProperty;
        try {
            otherSideProperty = collectionEntity.getRecursiveProperty(joinColumns[0].getMappedBy());
        } catch (MappingException e) {
            throw new AnnotationException("mappedBy reference an unknown target entity property: " + collType + "." + joinColumns[0].getMappedBy() + " in " + collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName());
        }
        Table table;
        if (otherSideProperty.getValue() instanceof Collection) {
            // this is a collection on the other side
            table = ((Collection) otherSideProperty.getValue()).getCollectionTable();
        } else {
            // This is a ToOne with a @JoinTable or a regular property
            table = otherSideProperty.getValue().getTable();
        }
        collValue.setCollectionTable(table);
        String entityName = collectionEntity.getEntityName();
        for (Ejb3JoinColumn column : joinColumns) {
            // column.setDefaultColumnHeader( joinColumns[0].getMappedBy() ); //seems not to be used, make sense
            column.setManyToManyOwnerSideEntityName(entityName);
        }
    } else {
        // FIXME NamingStrategy
        for (Ejb3JoinColumn column : joinColumns) {
            String mappedByProperty = buildingContext.getMetadataCollector().getFromMappedBy(collValue.getOwnerEntityName(), column.getPropertyName());
            Table ownerTable = collValue.getOwner().getTable();
            column.setMappedBy(collValue.getOwner().getEntityName(), collValue.getOwner().getJpaEntityName(), buildingContext.getMetadataCollector().getLogicalTableName(ownerTable), mappedByProperty);
        // String header = ( mappedByProperty == null ) ? mappings.getLogicalTableName( ownerTable ) : mappedByProperty;
        // column.setDefaultColumnHeader( header );
        }
        if (StringHelper.isEmpty(associationTableBinder.getName())) {
            // default value
            associationTableBinder.setDefaultName(collValue.getOwner().getClassName(), collValue.getOwner().getEntityName(), collValue.getOwner().getJpaEntityName(), buildingContext.getMetadataCollector().getLogicalTableName(collValue.getOwner().getTable()), collectionEntity != null ? collectionEntity.getClassName() : null, collectionEntity != null ? collectionEntity.getEntityName() : null, collectionEntity != null ? collectionEntity.getJpaEntityName() : null, collectionEntity != null ? buildingContext.getMetadataCollector().getLogicalTableName(collectionEntity.getTable()) : null, joinColumns[0].getPropertyName());
        }
        associationTableBinder.setJPA2ElementCollection(!isCollectionOfEntities && property.isAnnotationPresent(ElementCollection.class));
        collValue.setCollectionTable(associationTableBinder.bind());
    }
    bindFilters(isCollectionOfEntities);
    bindCollectionSecondPass(collValue, collectionEntity, joinColumns, cascadeDeleteEnabled, property, propertyHolder, buildingContext);
    ManyToOne element = null;
    if (isCollectionOfEntities) {
        element = new ManyToOne(buildingContext, collValue.getCollectionTable());
        collValue.setElement(element);
        element.setReferencedEntityName(collType.getName());
        // element.setFetchMode( fetchMode );
        // element.setLazy( fetchMode != FetchMode.JOIN );
        // make the second join non lazy
        element.setFetchMode(FetchMode.JOIN);
        element.setLazy(false);
        element.setIgnoreNotFound(ignoreNotFound);
        // as per 11.1.38 of JPA 2.0 spec, default to primary key if no column is specified by @OrderBy.
        if (hqlOrderBy != null) {
            collValue.setManyToManyOrdering(buildOrderByClauseFromHql(hqlOrderBy, collectionEntity, collValue.getRole()));
        }
        final ForeignKey fk = property.getAnnotation(ForeignKey.class);
        if (fk != null && !BinderHelper.isEmptyAnnotationValue(fk.name())) {
            element.setForeignKeyName(fk.name());
        } else {
            final JoinTable joinTableAnn = property.getAnnotation(JoinTable.class);
            if (joinTableAnn != null) {
                String foreignKeyName = joinTableAnn.inverseForeignKey().name();
                String foreignKeyDefinition = joinTableAnn.inverseForeignKey().foreignKeyDefinition();
                ConstraintMode foreignKeyValue = joinTableAnn.inverseForeignKey().value();
                if (joinTableAnn.inverseJoinColumns().length != 0) {
                    final JoinColumn joinColumnAnn = joinTableAnn.inverseJoinColumns()[0];
                    if ("".equals(foreignKeyName)) {
                        foreignKeyName = joinColumnAnn.foreignKey().name();
                        foreignKeyDefinition = joinColumnAnn.foreignKey().foreignKeyDefinition();
                    }
                    if (foreignKeyValue != ConstraintMode.NO_CONSTRAINT) {
                        foreignKeyValue = joinColumnAnn.foreignKey().value();
                    }
                }
                if (joinTableAnn.inverseForeignKey().value() == ConstraintMode.NO_CONSTRAINT) {
                    element.setForeignKeyName("none");
                } else {
                    element.setForeignKeyName(StringHelper.nullIfEmpty(foreignKeyName));
                    element.setForeignKeyDefinition(StringHelper.nullIfEmpty(foreignKeyDefinition));
                }
            }
        }
    } else if (anyAnn != null) {
        // @ManyToAny
        // Make sure that collTyp is never used during the @ManyToAny branch: it will be set to void.class
        PropertyData inferredData = new PropertyInferredData(null, property, "unsupported", buildingContext.getBootstrapContext().getReflectionManager());
        // override the table
        for (Ejb3Column column : inverseJoinColumns) {
            column.setTable(collValue.getCollectionTable());
        }
        Any any = BinderHelper.buildAnyValue(anyAnn.metaDef(), inverseJoinColumns, anyAnn.metaColumn(), inferredData, cascadeDeleteEnabled, Nullability.NO_CONSTRAINT, propertyHolder, new EntityBinder(), true, buildingContext);
        collValue.setElement(any);
    } else {
        XClass elementClass;
        AnnotatedClassType classType;
        CollectionPropertyHolder holder;
        if (BinderHelper.PRIMITIVE_NAMES.contains(collType.getName())) {
            classType = AnnotatedClassType.NONE;
            elementClass = null;
            holder = PropertyHolderBuilder.buildPropertyHolder(collValue, collValue.getRole(), null, property, parentPropertyHolder, buildingContext);
        } else {
            elementClass = collType;
            classType = buildingContext.getMetadataCollector().getClassType(elementClass);
            holder = PropertyHolderBuilder.buildPropertyHolder(collValue, collValue.getRole(), elementClass, property, parentPropertyHolder, buildingContext);
            // 'parentPropertyHolder' is the PropertyHolder for the owner of the collection
            // 'holder' is the CollectionPropertyHolder.
            // 'property' is the collection XProperty
            parentPropertyHolder.startingProperty(property);
            // force in case of attribute override
            boolean attributeOverride = property.isAnnotationPresent(AttributeOverride.class) || property.isAnnotationPresent(AttributeOverrides.class);
            // todo : force in the case of Convert annotation(s) with embedded paths (beyond key/value prefixes)?
            if (isEmbedded || attributeOverride) {
                classType = AnnotatedClassType.EMBEDDABLE;
            }
        }
        if (AnnotatedClassType.EMBEDDABLE.equals(classType)) {
            holder.prepare(property);
            EntityBinder entityBinder = new EntityBinder();
            PersistentClass owner = collValue.getOwner();
            boolean isPropertyAnnotated;
            // String accessType = access != null ? access.value() : null;
            if (owner.getIdentifierProperty() != null) {
                isPropertyAnnotated = owner.getIdentifierProperty().getPropertyAccessorName().equals("property");
            } else if (owner.getIdentifierMapper() != null && owner.getIdentifierMapper().getPropertySpan() > 0) {
                Property prop = (Property) owner.getIdentifierMapper().getPropertyIterator().next();
                isPropertyAnnotated = prop.getPropertyAccessorName().equals("property");
            } else {
                throw new AssertionFailure("Unable to guess collection property accessor name");
            }
            PropertyData inferredData;
            if (isMap()) {
                // "value" is the JPA 2 prefix for map values (used to be "element")
                if (isHibernateExtensionMapping()) {
                    inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "element", elementClass);
                } else {
                    inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "value", elementClass);
                }
            } else {
                if (isHibernateExtensionMapping()) {
                    inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "element", elementClass);
                } else {
                    // "collection&&element" is not a valid property name => placeholder
                    inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "collection&&element", elementClass);
                }
            }
            // TODO be smart with isNullable
            boolean isNullable = true;
            Component component = AnnotationBinder.fillComponent(holder, inferredData, isPropertyAnnotated ? AccessType.PROPERTY : AccessType.FIELD, isNullable, entityBinder, false, false, true, buildingContext, inheritanceStatePerClass);
            collValue.setElement(component);
            if (StringHelper.isNotEmpty(hqlOrderBy)) {
                String orderBy = adjustUserSuppliedValueCollectionOrderingFragment(hqlOrderBy);
                if (orderBy != null) {
                    collValue.setOrderBy(orderBy);
                }
            }
        } else {
            holder.prepare(property);
            SimpleValueBinder elementBinder = new SimpleValueBinder();
            elementBinder.setBuildingContext(buildingContext);
            elementBinder.setReturnedClassName(collType.getName());
            if (elementColumns == null || elementColumns.length == 0) {
                elementColumns = new Ejb3Column[1];
                Ejb3Column column = new Ejb3Column();
                column.setImplicit(false);
                // not following the spec but more clean
                column.setNullable(true);
                column.setLength(Ejb3Column.DEFAULT_COLUMN_LENGTH);
                column.setLogicalColumnName(Collection.DEFAULT_ELEMENT_COLUMN_NAME);
                // TODO create an EMPTY_JOINS collection
                column.setJoins(new HashMap<>());
                column.setBuildingContext(buildingContext);
                column.bind();
                elementColumns[0] = column;
            }
            // override the table
            for (Ejb3Column column : elementColumns) {
                column.setTable(collValue.getCollectionTable());
            }
            elementBinder.setColumns(elementColumns);
            elementBinder.setType(property, elementClass, collValue.getOwnerEntityName(), holder.resolveElementAttributeConverterDescriptor(property, elementClass));
            elementBinder.setPersistentClassName(propertyHolder.getEntityName());
            elementBinder.setAccessType(accessType);
            collValue.setElement(elementBinder.make());
            String orderBy = adjustUserSuppliedValueCollectionOrderingFragment(hqlOrderBy);
            if (orderBy != null) {
                collValue.setOrderBy(orderBy);
            }
        }
    }
    checkFilterConditions(collValue);
    // FIXME: do optional = false
    if (isCollectionOfEntities) {
        bindManytoManyInverseFk(collectionEntity, inverseJoinColumns, element, unique, buildingContext);
    }
}
Also used : PropertyData(org.hibernate.cfg.PropertyData) HashMap(java.util.HashMap) PropertyInferredData(org.hibernate.cfg.PropertyInferredData) PropertyPreloadedData(org.hibernate.cfg.PropertyPreloadedData) Any(org.hibernate.mapping.Any) ManyToAny(org.hibernate.annotations.ManyToAny) XClass(org.hibernate.annotations.common.reflection.XClass) ManyToOne(org.hibernate.mapping.ManyToOne) MappingException(org.hibernate.MappingException) ManyToAny(org.hibernate.annotations.ManyToAny) JoinColumn(javax.persistence.JoinColumn) Ejb3JoinColumn(org.hibernate.cfg.Ejb3JoinColumn) AnnotationException(org.hibernate.AnnotationException) Ejb3Column(org.hibernate.cfg.Ejb3Column) Component(org.hibernate.mapping.Component) Property(org.hibernate.mapping.Property) XProperty(org.hibernate.annotations.common.reflection.XProperty) PersistentClass(org.hibernate.mapping.PersistentClass) JoinTable(javax.persistence.JoinTable) CollectionTable(javax.persistence.CollectionTable) WhereJoinTable(org.hibernate.annotations.WhereJoinTable) Table(org.hibernate.mapping.Table) FilterJoinTable(org.hibernate.annotations.FilterJoinTable) CollectionPropertyHolder(org.hibernate.cfg.CollectionPropertyHolder) AssertionFailure(org.hibernate.annotations.common.AssertionFailure) ManyToMany(javax.persistence.ManyToMany) ConstraintMode(javax.persistence.ConstraintMode) OneToMany(javax.persistence.OneToMany) ForeignKey(org.hibernate.annotations.ForeignKey) LazyCollection(org.hibernate.annotations.LazyCollection) Collection(org.hibernate.mapping.Collection) ElementCollection(javax.persistence.ElementCollection) Ejb3JoinColumn(org.hibernate.cfg.Ejb3JoinColumn) AnnotatedClassType(org.hibernate.cfg.AnnotatedClassType) JoinTable(javax.persistence.JoinTable) WhereJoinTable(org.hibernate.annotations.WhereJoinTable) FilterJoinTable(org.hibernate.annotations.FilterJoinTable)

Example 28 with ManyToOne

use of org.hibernate.mapping.ManyToOne in project hibernate-orm by hibernate.

the class MapBinder method createFormulatedValue.

protected Value createFormulatedValue(Value value, Collection collection, String targetPropertyName, PersistentClass associatedClass, PersistentClass targetPropertyPersistentClass, MetadataBuildingContext buildingContext) {
    Value element = collection.getElement();
    String fromAndWhere = null;
    if (!(element instanceof OneToMany)) {
        String referencedPropertyName = null;
        if (element instanceof ToOne) {
            referencedPropertyName = ((ToOne) element).getReferencedPropertyName();
        } else if (element instanceof DependantValue) {
            // TODO this never happen I think
            if (propertyName != null) {
                referencedPropertyName = collection.getReferencedPropertyName();
            } else {
                throw new AnnotationException("SecondaryTable JoinColumn cannot reference a non primary key");
            }
        }
        Iterator<Selectable> referencedEntityColumns;
        if (referencedPropertyName == null) {
            referencedEntityColumns = associatedClass.getIdentifier().getColumnIterator();
        } else {
            Property referencedProperty = associatedClass.getRecursiveProperty(referencedPropertyName);
            referencedEntityColumns = referencedProperty.getColumnIterator();
        }
        fromAndWhere = getFromAndWhereFormula(associatedClass.getTable().getName(), element.getColumnIterator(), referencedEntityColumns);
    } else {
        // HHH-11005 - only if we are OneToMany and location of map key property is at a different level, need to add a select
        if (!associatedClass.equals(targetPropertyPersistentClass)) {
            fromAndWhere = getFromAndWhereFormula(targetPropertyPersistentClass.getTable().getQualifiedTableName().toString(), element.getColumnIterator(), associatedClass.getIdentifier().getColumnIterator());
        }
    }
    if (value instanceof Component) {
        Component component = (Component) value;
        Iterator properties = component.getPropertyIterator();
        Component indexComponent = new Component(getBuildingContext(), collection);
        indexComponent.setComponentClassName(component.getComponentClassName());
        while (properties.hasNext()) {
            Property current = (Property) properties.next();
            Property newProperty = new Property();
            newProperty.setCascade(current.getCascade());
            newProperty.setValueGenerationStrategy(current.getValueGenerationStrategy());
            newProperty.setInsertable(false);
            newProperty.setUpdateable(false);
            newProperty.setMetaAttributes(current.getMetaAttributes());
            newProperty.setName(current.getName());
            newProperty.setNaturalIdentifier(false);
            // newProperty.setOptimisticLocked( false );
            newProperty.setOptional(false);
            newProperty.setPersistentClass(current.getPersistentClass());
            newProperty.setPropertyAccessorName(current.getPropertyAccessorName());
            newProperty.setSelectable(current.isSelectable());
            newProperty.setValue(createFormulatedValue(current.getValue(), collection, targetPropertyName, associatedClass, associatedClass, buildingContext));
            indexComponent.addProperty(newProperty);
        }
        return indexComponent;
    } else if (value instanceof SimpleValue) {
        SimpleValue sourceValue = (SimpleValue) value;
        SimpleValue targetValue;
        if (value instanceof ManyToOne) {
            ManyToOne sourceManyToOne = (ManyToOne) sourceValue;
            ManyToOne targetManyToOne = new ManyToOne(getBuildingContext(), collection.getCollectionTable());
            targetManyToOne.setFetchMode(FetchMode.DEFAULT);
            targetManyToOne.setLazy(true);
            // targetValue.setIgnoreNotFound( ); does not make sense for a map key
            targetManyToOne.setReferencedEntityName(sourceManyToOne.getReferencedEntityName());
            targetValue = targetManyToOne;
        } else {
            targetValue = new SimpleValue(getBuildingContext(), collection.getCollectionTable());
            targetValue.copyTypeFrom(sourceValue);
        }
        Iterator columns = sourceValue.getColumnIterator();
        Random random = new Random();
        while (columns.hasNext()) {
            Object current = columns.next();
            Formula formula = new Formula();
            String formulaString;
            if (current instanceof Column) {
                formulaString = ((Column) current).getQuotedName();
            } else if (current instanceof Formula) {
                formulaString = ((Formula) current).getFormula();
            } else {
                throw new AssertionFailure("Unknown element in column iterator: " + current.getClass());
            }
            if (fromAndWhere != null) {
                formulaString = Template.renderWhereStringTemplate(formulaString, "$alias$", new HSQLDialect());
                formulaString = "(select " + formulaString + fromAndWhere + ")";
                formulaString = StringHelper.replace(formulaString, "$alias$", "a" + random.nextInt(16));
            }
            formula.setFormula(formulaString);
            targetValue.addFormula(formula);
        }
        return targetValue;
    } else {
        throw new AssertionFailure("Unknown type encounters for map key: " + value.getClass());
    }
}
Also used : HSQLDialect(org.hibernate.dialect.HSQLDialect) AssertionFailure(org.hibernate.AssertionFailure) DependantValue(org.hibernate.mapping.DependantValue) OneToMany(org.hibernate.mapping.OneToMany) ManyToOne(org.hibernate.mapping.ManyToOne) SimpleValue(org.hibernate.mapping.SimpleValue) Formula(org.hibernate.mapping.Formula) Random(java.util.Random) Selectable(org.hibernate.mapping.Selectable) Ejb3Column(org.hibernate.cfg.Ejb3Column) MapKeyColumn(javax.persistence.MapKeyColumn) MapKeyJoinColumn(javax.persistence.MapKeyJoinColumn) Column(org.hibernate.mapping.Column) Ejb3JoinColumn(org.hibernate.cfg.Ejb3JoinColumn) SimpleValue(org.hibernate.mapping.SimpleValue) DependantValue(org.hibernate.mapping.DependantValue) Value(org.hibernate.mapping.Value) ToOne(org.hibernate.mapping.ToOne) ManyToOne(org.hibernate.mapping.ManyToOne) Iterator(java.util.Iterator) AnnotationException(org.hibernate.AnnotationException) Component(org.hibernate.mapping.Component) Property(org.hibernate.mapping.Property) XProperty(org.hibernate.annotations.common.reflection.XProperty)

Example 29 with ManyToOne

use of org.hibernate.mapping.ManyToOne in project hibernate-orm by hibernate.

the class MapBinder method bindKeyFromAssociationTable.

private void bindKeyFromAssociationTable(XClass collType, Map persistentClasses, String mapKeyPropertyName, XProperty property, boolean isEmbedded, MetadataBuildingContext buildingContext, Ejb3Column[] mapKeyColumns, Ejb3JoinColumn[] mapKeyManyToManyColumns, String targetPropertyName) {
    if (mapKeyPropertyName != null) {
        // this is an EJB3 @MapKey
        PersistentClass associatedClass = (PersistentClass) persistentClasses.get(collType.getName());
        if (associatedClass == null)
            throw new AnnotationException("Associated class not found: " + collType);
        Property mapProperty = BinderHelper.findPropertyByName(associatedClass, mapKeyPropertyName);
        if (mapProperty == null) {
            throw new AnnotationException("Map key property not found: " + collType + "." + mapKeyPropertyName);
        }
        org.hibernate.mapping.Map map = (org.hibernate.mapping.Map) this.collection;
        // HHH-11005 - if InheritanceType.JOINED then need to find class defining the column
        InheritanceState inheritanceState = inheritanceStatePerClass.get(collType);
        PersistentClass targetPropertyPersistentClass = InheritanceType.JOINED.equals(inheritanceState.getType()) ? mapProperty.getPersistentClass() : associatedClass;
        Value indexValue = createFormulatedValue(mapProperty.getValue(), map, targetPropertyName, associatedClass, targetPropertyPersistentClass, buildingContext);
        map.setIndex(indexValue);
    } else {
        // this is a true Map mapping
        // TODO ugly copy/pastle from CollectionBinder.bindManyToManySecondPass
        String mapKeyType;
        Class target = void.class;
        /*
			 * target has priority over reflection for the map key type
			 * JPA 2 has priority
			 */
        if (property.isAnnotationPresent(MapKeyClass.class)) {
            target = property.getAnnotation(MapKeyClass.class).value();
        }
        if (!void.class.equals(target)) {
            mapKeyType = target.getName();
        } else {
            mapKeyType = property.getMapKey().getName();
        }
        PersistentClass collectionEntity = (PersistentClass) persistentClasses.get(mapKeyType);
        boolean isIndexOfEntities = collectionEntity != null;
        ManyToOne element = null;
        org.hibernate.mapping.Map mapValue = (org.hibernate.mapping.Map) this.collection;
        if (isIndexOfEntities) {
            element = new ManyToOne(buildingContext, mapValue.getCollectionTable());
            mapValue.setIndex(element);
            element.setReferencedEntityName(mapKeyType);
            // element.setFetchMode( fetchMode );
            // element.setLazy( fetchMode != FetchMode.JOIN );
            // make the second join non lazy
            element.setFetchMode(FetchMode.JOIN);
            element.setLazy(false);
        // does not make sense for a map key element.setIgnoreNotFound( ignoreNotFound );
        } else {
            XClass keyXClass;
            AnnotatedClassType classType;
            if (BinderHelper.PRIMITIVE_NAMES.contains(mapKeyType)) {
                classType = AnnotatedClassType.NONE;
                keyXClass = null;
            } else {
                try {
                    keyXClass = buildingContext.getBootstrapContext().getReflectionManager().classForName(mapKeyType);
                } catch (ClassLoadingException e) {
                    throw new AnnotationException("Unable to find class: " + mapKeyType, e);
                }
                classType = buildingContext.getMetadataCollector().getClassType(keyXClass);
                // force in case of attribute override naming the key
                if (isEmbedded || mappingDefinedAttributeOverrideOnMapKey(property)) {
                    classType = AnnotatedClassType.EMBEDDABLE;
                }
            }
            CollectionPropertyHolder holder = PropertyHolderBuilder.buildPropertyHolder(mapValue, StringHelper.qualify(mapValue.getRole(), "mapkey"), keyXClass, property, propertyHolder, buildingContext);
            // 'propertyHolder' is the PropertyHolder for the owner of the collection
            // 'holder' is the CollectionPropertyHolder.
            // 'property' is the collection XProperty
            propertyHolder.startingProperty(property);
            holder.prepare(property);
            PersistentClass owner = mapValue.getOwner();
            AccessType accessType;
            // String accessType = access != null ? access.value() : null;
            if (owner.getIdentifierProperty() != null) {
                accessType = owner.getIdentifierProperty().getPropertyAccessorName().equals("property") ? AccessType.PROPERTY : AccessType.FIELD;
            } else if (owner.getIdentifierMapper() != null && owner.getIdentifierMapper().getPropertySpan() > 0) {
                Property prop = (Property) owner.getIdentifierMapper().getPropertyIterator().next();
                accessType = prop.getPropertyAccessorName().equals("property") ? AccessType.PROPERTY : AccessType.FIELD;
            } else {
                throw new AssertionFailure("Unable to guess collection property accessor name");
            }
            if (AnnotatedClassType.EMBEDDABLE.equals(classType)) {
                EntityBinder entityBinder = new EntityBinder();
                PropertyData inferredData;
                if (isHibernateExtensionMapping()) {
                    inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "index", keyXClass);
                } else {
                    // "key" is the JPA 2 prefix for map keys
                    inferredData = new PropertyPreloadedData(AccessType.PROPERTY, "key", keyXClass);
                }
                // TODO be smart with isNullable
                Component component = AnnotationBinder.fillComponent(holder, inferredData, accessType, true, entityBinder, false, false, true, buildingContext, inheritanceStatePerClass);
                mapValue.setIndex(component);
            } else {
                SimpleValueBinder elementBinder = new SimpleValueBinder();
                elementBinder.setBuildingContext(buildingContext);
                elementBinder.setReturnedClassName(mapKeyType);
                Ejb3Column[] elementColumns = mapKeyColumns;
                if (elementColumns == null || elementColumns.length == 0) {
                    elementColumns = new Ejb3Column[1];
                    Ejb3Column column = new Ejb3Column();
                    column.setImplicit(false);
                    column.setNullable(true);
                    column.setLength(Ejb3Column.DEFAULT_COLUMN_LENGTH);
                    column.setLogicalColumnName(Collection.DEFAULT_KEY_COLUMN_NAME);
                    // TODO create an EMPTY_JOINS collection
                    column.setJoins(new HashMap<String, Join>());
                    column.setBuildingContext(buildingContext);
                    column.bind();
                    elementColumns[0] = column;
                }
                // override the table
                for (Ejb3Column column : elementColumns) {
                    column.setTable(mapValue.getCollectionTable());
                }
                elementBinder.setColumns(elementColumns);
                // do not call setType as it extract the type from @Type
                // the algorithm generally does not apply for map key anyway
                elementBinder.setKey(true);
                elementBinder.setType(property, keyXClass, this.collection.getOwnerEntityName(), holder.mapKeyAttributeConverterDescriptor(property, keyXClass));
                elementBinder.setPersistentClassName(propertyHolder.getEntityName());
                elementBinder.setAccessType(accessType);
                mapValue.setIndex(elementBinder.make());
            }
        }
        // FIXME pass the Index Entity JoinColumns
        if (!collection.isOneToMany()) {
            // index column shoud not be null
            for (Ejb3JoinColumn col : mapKeyManyToManyColumns) {
                col.forceNotNull();
            }
        }
        if (element != null) {
            final javax.persistence.ForeignKey foreignKey = getMapKeyForeignKey(property);
            if (foreignKey != null) {
                if (foreignKey.value() == ConstraintMode.NO_CONSTRAINT) {
                    element.setForeignKeyName("none");
                } else {
                    element.setForeignKeyName(StringHelper.nullIfEmpty(foreignKey.name()));
                    element.setForeignKeyDefinition(StringHelper.nullIfEmpty(foreignKey.foreignKeyDefinition()));
                }
            }
        }
        if (isIndexOfEntities) {
            bindManytoManyInverseFk(collectionEntity, mapKeyManyToManyColumns, element, // a map key column has no unique constraint
            false, buildingContext);
        }
    }
}
Also used : PropertyData(org.hibernate.cfg.PropertyData) PropertyPreloadedData(org.hibernate.cfg.PropertyPreloadedData) XClass(org.hibernate.annotations.common.reflection.XClass) ManyToOne(org.hibernate.mapping.ManyToOne) AnnotationException(org.hibernate.AnnotationException) Ejb3Column(org.hibernate.cfg.Ejb3Column) Component(org.hibernate.mapping.Component) Property(org.hibernate.mapping.Property) XProperty(org.hibernate.annotations.common.reflection.XProperty) AccessType(org.hibernate.cfg.AccessType) PersistentClass(org.hibernate.mapping.PersistentClass) CollectionPropertyHolder(org.hibernate.cfg.CollectionPropertyHolder) AssertionFailure(org.hibernate.AssertionFailure) ClassLoadingException(org.hibernate.annotations.common.reflection.ClassLoadingException) Join(org.hibernate.mapping.Join) InheritanceState(org.hibernate.cfg.InheritanceState) SimpleValue(org.hibernate.mapping.SimpleValue) DependantValue(org.hibernate.mapping.DependantValue) Value(org.hibernate.mapping.Value) MapKeyClass(javax.persistence.MapKeyClass) PersistentClass(org.hibernate.mapping.PersistentClass) XClass(org.hibernate.annotations.common.reflection.XClass) Ejb3JoinColumn(org.hibernate.cfg.Ejb3JoinColumn) Map(java.util.Map) HashMap(java.util.HashMap) AnnotatedClassType(org.hibernate.cfg.AnnotatedClassType)

Example 30 with ManyToOne

use of org.hibernate.mapping.ManyToOne in project hibernate-orm by hibernate.

the class OneToOneSecondPass method doSecondPass.

// TODO refactor this code, there is a lot of duplication in this method
public void doSecondPass(Map persistentClasses) throws MappingException {
    org.hibernate.mapping.OneToOne value = new org.hibernate.mapping.OneToOne(buildingContext, propertyHolder.getTable(), propertyHolder.getPersistentClass());
    final String propertyName = inferredData.getPropertyName();
    value.setPropertyName(propertyName);
    String referencedEntityName = ToOneBinder.getReferenceEntityName(inferredData, targetEntity, buildingContext);
    value.setReferencedEntityName(referencedEntityName);
    AnnotationBinder.defineFetchingStrategy(value, inferredData.getProperty());
    // value.setFetchMode( fetchMode );
    value.setCascadeDeleteEnabled(cascadeOnDelete);
    if (!optional) {
        value.setConstrained(true);
    }
    if (value.isReferenceToPrimaryKey()) {
        value.setForeignKeyType(ForeignKeyDirection.TO_PARENT);
    } else {
        value.setForeignKeyType(value.isConstrained() ? ForeignKeyDirection.FROM_PARENT : ForeignKeyDirection.TO_PARENT);
    }
    PropertyBinder binder = new PropertyBinder();
    binder.setName(propertyName);
    binder.setValue(value);
    binder.setCascade(cascadeStrategy);
    binder.setAccessType(inferredData.getDefaultAccess());
    final LazyGroup lazyGroupAnnotation = inferredData.getProperty().getAnnotation(LazyGroup.class);
    if (lazyGroupAnnotation != null) {
        binder.setLazyGroup(lazyGroupAnnotation.value());
    }
    Property prop = binder.makeProperty();
    prop.setOptional(optional);
    if (BinderHelper.isEmptyAnnotationValue(mappedBy)) {
        /*
			 * we need to check if the columns are in the right order
			 * if not, then we need to create a many to one and formula
			 * but actually, since entities linked by a one to one need
			 * to share the same composite id class, this cannot happen in hibernate
			 */
        boolean rightOrder = true;
        if (rightOrder) {
            String path = StringHelper.qualify(propertyHolder.getPath(), propertyName);
            final ToOneFkSecondPass secondPass = new ToOneFkSecondPass(value, joinColumns, // cannot have nullabe and unique on certain DBs
            !optional, propertyHolder.getEntityOwnerClassName(), path, buildingContext);
            secondPass.doSecondPass(persistentClasses);
            // no column associated since its a one to one
            propertyHolder.addProperty(prop, inferredData.getDeclaringClass());
        } else {
        // this is a many to one with Formula
        }
    } else {
        PersistentClass otherSide = (PersistentClass) persistentClasses.get(value.getReferencedEntityName());
        Property otherSideProperty;
        try {
            if (otherSide == null) {
                throw new MappingException("Unable to find entity: " + value.getReferencedEntityName());
            }
            otherSideProperty = BinderHelper.findPropertyByName(otherSide, mappedBy);
        } catch (MappingException e) {
            throw new AnnotationException("Unknown mappedBy in: " + StringHelper.qualify(ownerEntity, ownerProperty) + ", referenced property unknown: " + StringHelper.qualify(value.getReferencedEntityName(), mappedBy));
        }
        if (otherSideProperty == null) {
            throw new AnnotationException("Unknown mappedBy in: " + StringHelper.qualify(ownerEntity, ownerProperty) + ", referenced property unknown: " + StringHelper.qualify(value.getReferencedEntityName(), mappedBy));
        }
        if (otherSideProperty.getValue() instanceof OneToOne) {
            propertyHolder.addProperty(prop, inferredData.getDeclaringClass());
        } else if (otherSideProperty.getValue() instanceof ManyToOne) {
            Iterator it = otherSide.getJoinIterator();
            Join otherSideJoin = null;
            while (it.hasNext()) {
                Join otherSideJoinValue = (Join) it.next();
                if (otherSideJoinValue.containsProperty(otherSideProperty)) {
                    otherSideJoin = otherSideJoinValue;
                    break;
                }
            }
            if (otherSideJoin != null) {
                // @OneToOne @JoinTable
                Join mappedByJoin = buildJoinFromMappedBySide((PersistentClass) persistentClasses.get(ownerEntity), otherSideProperty, otherSideJoin);
                ManyToOne manyToOne = new ManyToOne(buildingContext, mappedByJoin.getTable());
                // FIXME use ignore not found here
                manyToOne.setIgnoreNotFound(ignoreNotFound);
                manyToOne.setCascadeDeleteEnabled(value.isCascadeDeleteEnabled());
                manyToOne.setFetchMode(value.getFetchMode());
                manyToOne.setLazy(value.isLazy());
                manyToOne.setReferencedEntityName(value.getReferencedEntityName());
                manyToOne.setUnwrapProxy(value.isUnwrapProxy());
                prop.setValue(manyToOne);
                Iterator otherSideJoinKeyColumns = otherSideJoin.getKey().getColumnIterator();
                while (otherSideJoinKeyColumns.hasNext()) {
                    Column column = (Column) otherSideJoinKeyColumns.next();
                    Column copy = new Column();
                    copy.setLength(column.getLength());
                    copy.setScale(column.getScale());
                    copy.setValue(manyToOne);
                    copy.setName(column.getQuotedName());
                    copy.setNullable(column.isNullable());
                    copy.setPrecision(column.getPrecision());
                    copy.setUnique(column.isUnique());
                    copy.setSqlType(column.getSqlType());
                    copy.setCheckConstraint(column.getCheckConstraint());
                    copy.setComment(column.getComment());
                    copy.setDefaultValue(column.getDefaultValue());
                    manyToOne.addColumn(copy);
                }
                mappedByJoin.addProperty(prop);
            } else {
                propertyHolder.addProperty(prop, inferredData.getDeclaringClass());
            }
            value.setReferencedPropertyName(mappedBy);
            // HHH-6813
            // Foo: @Id long id, @OneToOne(mappedBy="foo") Bar bar
            // Bar: @Id @OneToOne Foo foo
            boolean referencesDerivedId = false;
            try {
                referencesDerivedId = otherSide.getIdentifier() instanceof Component && ((Component) otherSide.getIdentifier()).getProperty(mappedBy) != null;
            } catch (MappingException e) {
            // ignore
            }
            boolean referenceToPrimaryKey = referencesDerivedId || mappedBy == null;
            value.setReferenceToPrimaryKey(referenceToPrimaryKey);
            // loop of attempts to resolve identifiers.
            if (referencesDerivedId) {
                ((ManyToOne) otherSideProperty.getValue()).setReferenceToPrimaryKey(false);
            }
            String propertyRef = value.getReferencedPropertyName();
            if (propertyRef != null) {
                buildingContext.getMetadataCollector().addUniquePropertyReference(value.getReferencedEntityName(), propertyRef);
            }
        } else {
            throw new AnnotationException("Referenced property not a (One|Many)ToOne: " + StringHelper.qualify(otherSide.getEntityName(), mappedBy) + " in mappedBy of " + StringHelper.qualify(ownerEntity, ownerProperty));
        }
    }
    final ForeignKey fk = inferredData.getProperty().getAnnotation(ForeignKey.class);
    if (fk != null && !BinderHelper.isEmptyAnnotationValue(fk.name())) {
        value.setForeignKeyName(fk.name());
    } else {
        final javax.persistence.ForeignKey jpaFk = inferredData.getProperty().getAnnotation(javax.persistence.ForeignKey.class);
        if (jpaFk != null) {
            if (jpaFk.value() == ConstraintMode.NO_CONSTRAINT) {
                value.setForeignKeyName("none");
            } else {
                value.setForeignKeyName(StringHelper.nullIfEmpty(jpaFk.name()));
                value.setForeignKeyDefinition(StringHelper.nullIfEmpty(jpaFk.foreignKeyDefinition()));
            }
        }
    }
}
Also used : OneToOne(org.hibernate.mapping.OneToOne) Join(org.hibernate.mapping.Join) ForeignKey(org.hibernate.annotations.ForeignKey) ManyToOne(org.hibernate.mapping.ManyToOne) MappingException(org.hibernate.MappingException) OneToOne(org.hibernate.mapping.OneToOne) Column(org.hibernate.mapping.Column) LazyGroup(org.hibernate.annotations.LazyGroup) Iterator(java.util.Iterator) AnnotationException(org.hibernate.AnnotationException) PropertyBinder(org.hibernate.cfg.annotations.PropertyBinder) Component(org.hibernate.mapping.Component) Property(org.hibernate.mapping.Property) PersistentClass(org.hibernate.mapping.PersistentClass)

Aggregations

ManyToOne (org.hibernate.mapping.ManyToOne)48 Test (org.junit.Test)37 SimpleValue (org.hibernate.mapping.SimpleValue)22 Collection (org.hibernate.mapping.Collection)10 Bag (org.hibernate.mapping.Bag)9 IdentifierBag (org.hibernate.mapping.IdentifierBag)9 Component (org.hibernate.mapping.Component)8 Property (org.hibernate.mapping.Property)7 AnnotationException (org.hibernate.AnnotationException)5 AssertionFailure (org.hibernate.AssertionFailure)5 Any (org.hibernate.mapping.Any)5 OneToOne (org.hibernate.mapping.OneToOne)5 PersistentClass (org.hibernate.mapping.PersistentClass)5 XProperty (org.hibernate.annotations.common.reflection.XProperty)4 Ejb3Column (org.hibernate.cfg.Ejb3Column)4 Ejb3JoinColumn (org.hibernate.cfg.Ejb3JoinColumn)4 Join (org.hibernate.mapping.Join)4 OneToMany (org.hibernate.mapping.OneToMany)4 Iterator (java.util.Iterator)3 Column (org.hibernate.mapping.Column)3