use of org.hibernate.cfg.AnnotatedJoinColumn in project hibernate-orm by hibernate.
the class CollectionBinder method bindManytoManyInverseFk.
/**
* bind the inverse FK of a {@link 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, AnnotatedJoinColumn[] columns, SimpleValue value, boolean unique, MetadataBuildingContext buildingContext) {
final String mappedBy = columns[0].getMappedBy();
if (StringHelper.isNotEmpty(mappedBy)) {
final Property property = referencedEntity.getRecursiveProperty(mappedBy);
List<Selectable> mappedByColumns;
if (property.getValue() instanceof Collection) {
mappedByColumns = ((Collection) property.getValue()).getKey().getSelectables();
} else {
// find the appropriate reference key, can be in a join
KeyValue key = null;
for (Join join : referencedEntity.getJoins()) {
if (join.containsProperty(property)) {
key = join.getKey();
break;
}
}
if (key == null) {
key = property.getPersistentClass().getIdentifier();
}
mappedByColumns = key.getSelectables();
}
for (Selectable selectable : mappedByColumns) {
Column column = (Column) selectable;
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);
}
}
use of org.hibernate.cfg.AnnotatedJoinColumn in project hibernate-orm by hibernate.
the class CollectionBinder method bindOneToManySecondPass.
protected void bindOneToManySecondPass(Collection collection, Map<String, PersistentClass> persistentClasses, AnnotatedJoinColumn[] fkJoinColumns, XClass collectionType, boolean cascadeDeleteEnabled, boolean ignoreNotFound, MetadataBuildingContext buildingContext, Map<XClass, InheritanceState> inheritanceStatePerClass) {
if (LOG.isDebugEnabled()) {
LOG.debugf("Binding a OneToMany: %s.%s through a foreign key", propertyHolder.getEntityName(), propertyName);
}
if (buildingContext == null) {
throw new AssertionFailure("CollectionSecondPass for oneToMany should not be called with null mappings");
}
org.hibernate.mapping.OneToMany oneToMany = new org.hibernate.mapping.OneToMany(buildingContext, collection.getOwner());
collection.setElement(oneToMany);
oneToMany.setReferencedEntityName(collectionType.getName());
oneToMany.setIgnoreNotFound(ignoreNotFound);
String assocClass = oneToMany.getReferencedEntityName();
PersistentClass associatedClass = persistentClasses.get(assocClass);
handleJpaOrderBy(collection, associatedClass);
Map<String, Join> joins = buildingContext.getMetadataCollector().getJoins(assocClass);
if (associatedClass == null) {
throw new MappingException(String.format("Association [%s] for entity [%s] references unmapped class [%s]", propertyName, propertyHolder.getClassName(), assocClass));
}
oneToMany.setAssociatedClass(associatedClass);
for (AnnotatedJoinColumn column : fkJoinColumns) {
column.setPersistentClass(associatedClass, joins, inheritanceStatePerClass);
column.setJoins(joins);
collection.setCollectionTable(column.getTable());
}
if (LOG.isDebugEnabled()) {
LOG.debugf("Mapping collection: %s -> %s", collection.getRole(), collection.getCollectionTable().getName());
}
bindFilters(false);
handleWhere(false);
bindCollectionSecondPass(collection, null, fkJoinColumns, cascadeDeleteEnabled, property, propertyHolder, buildingContext);
if (!collection.isInverse() && !collection.getKey().isNullable()) {
// for non-inverse one-to-many, with a not-null fk, add a backref!
String entityName = oneToMany.getReferencedEntityName();
PersistentClass referenced = buildingContext.getMetadataCollector().getEntityBinding(entityName);
Backref prop = new Backref();
prop.setName('_' + fkJoinColumns[0].getPropertyName() + '_' + fkJoinColumns[0].getLogicalColumnName() + "Backref");
prop.setUpdateable(false);
prop.setSelectable(false);
prop.setCollectionRole(collection.getRole());
prop.setEntityName(collection.getOwner().getEntityName());
prop.setValue(collection.getKey());
referenced.addProperty(prop);
}
}
use of org.hibernate.cfg.AnnotatedJoinColumn in project hibernate-orm by hibernate.
the class MapBinder method bindKeyFromAssociationTable.
private void bindKeyFromAssociationTable(XClass collType, Map<String, PersistentClass> persistentClasses, String mapKeyPropertyName, XProperty property, boolean isEmbedded, MetadataBuildingContext buildingContext, AnnotatedColumn[] mapKeyColumns, AnnotatedJoinColumn[] mapKeyManyToManyColumns, String targetPropertyName) {
if (mapKeyPropertyName != null) {
// this is an EJB3 @MapKey
PersistentClass associatedClass = 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);
map.setMapKeyPropertyName(mapKeyPropertyName);
} else {
// this is a true Map mapping
// TODO ugly copy/paste 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 = 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 {
final XClass keyXClass;
AnnotatedClassType classType;
if (BinderHelper.PRIMITIVE_NAMES.contains(mapKeyType)) {
classType = AnnotatedClassType.NONE;
keyXClass = null;
} else {
final BootstrapContext bootstrapContext = buildingContext.getBootstrapContext();
final Class<Object> mapKeyClass = bootstrapContext.getClassLoaderAccess().classForName(mapKeyType);
keyXClass = bootstrapContext.getReflectionManager().toXClass(mapKeyClass);
classType = buildingContext.getMetadataCollector().getClassType(keyXClass);
// force in case of attribute override naming the key
if (isEmbedded || mappingDefinedAttributeOverrideOnMapKey(property)) {
classType = AnnotatedClassType.EMBEDDABLE;
}
}
CollectionPropertyHolder holder = 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 = owner.getIdentifierMapper().getProperties().get(0);
accessType = prop.getPropertyAccessorName().equals("property") ? AccessType.PROPERTY : AccessType.FIELD;
} else {
throw new AssertionFailure("Unable to guess collection property accessor name");
}
final Class<? extends CompositeUserType<?>> compositeUserType = resolveCompositeUserType(property, keyXClass, buildingContext);
if (AnnotatedClassType.EMBEDDABLE.equals(classType) || compositeUserType != null) {
EntityBinder entityBinder = new EntityBinder();
PropertyData inferredData = isHibernateExtensionMapping() ? new PropertyPreloadedData(AccessType.PROPERTY, "index", keyXClass) : new PropertyPreloadedData(AccessType.PROPERTY, "key", keyXClass);
// "key" is the JPA 2 prefix for map keys
// TODO be smart with isNullable
Component component = AnnotationBinder.fillComponent(holder, inferredData, accessType, true, entityBinder, false, false, true, null, compositeUserType, buildingContext, inheritanceStatePerClass);
mapValue.setIndex(component);
} else {
final BasicValueBinder elementBinder = new BasicValueBinder(BasicValueBinder.Kind.MAP_KEY, buildingContext);
elementBinder.setReturnedClassName(mapKeyType);
AnnotatedColumn[] elementColumns = mapKeyColumns;
if (elementColumns == null || elementColumns.length == 0) {
elementColumns = new AnnotatedColumn[1];
AnnotatedColumn column = new AnnotatedColumn();
column.setImplicit(false);
column.setNullable(true);
column.setLength(Size.DEFAULT_LENGTH);
column.setLogicalColumnName(Collection.DEFAULT_KEY_COLUMN_NAME);
// TODO create an EMPTY_JOINS collection
column.setJoins(new HashMap<>());
column.setBuildingContext(buildingContext);
column.bind();
elementColumns[0] = column;
}
// override the table
for (AnnotatedColumn column : elementColumns) {
column.setTable(mapValue.getCollectionTable());
}
elementBinder.setColumns(elementColumns);
// do not call setType as it extracts the type from @Type
// the algorithm generally does not apply for map key anyway
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 should not be null
for (AnnotatedJoinColumn col : mapKeyManyToManyColumns) {
col.forceNotNull();
}
}
if (element != null) {
final jakarta.persistence.ForeignKey foreignKey = getMapKeyForeignKey(property);
if (foreignKey != null) {
if (foreignKey.value() == ConstraintMode.NO_CONSTRAINT || foreignKey.value() == ConstraintMode.PROVIDER_DEFAULT && getBuildingContext().getBuildingOptions().isNoConstraintByDefault()) {
element.disableForeignKey();
} 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);
}
}
}
use of org.hibernate.cfg.AnnotatedJoinColumn in project hibernate-orm by hibernate.
the class TableBinder method linkJoinColumnWithValueOverridingNameIfImplicit.
public static void linkJoinColumnWithValueOverridingNameIfImplicit(PersistentClass referencedEntity, Value value, AnnotatedJoinColumn[] columns, SimpleValue simpleValue) {
List<Column> valueColumns = value.getColumns();
for (int i = 0; i < columns.length; i++) {
AnnotatedJoinColumn joinCol = columns[i];
Column synthCol = valueColumns.get(i);
if (joinCol.isNameDeferred()) {
// this has to be the default value
joinCol.linkValueUsingDefaultColumnNaming(synthCol, referencedEntity, simpleValue);
} else {
joinCol.linkWithValue(simpleValue);
joinCol.overrideFromReferencedColumnIfNecessary(synthCol);
}
}
}
use of org.hibernate.cfg.AnnotatedJoinColumn in project hibernate-orm by hibernate.
the class TableBinder method bindFk.
public static void bindFk(PersistentClass referencedEntity, PersistentClass destinationEntity, AnnotatedJoinColumn[] columns, SimpleValue value, boolean unique, MetadataBuildingContext buildingContext) {
PersistentClass associatedClass;
if (destinationEntity != null) {
// overridden destination
associatedClass = destinationEntity;
} else {
associatedClass = columns[0].getPropertyHolder() == null ? null : columns[0].getPropertyHolder().getPersistentClass();
}
final String mappedByProperty = columns[0].getMappedBy();
if (StringHelper.isNotEmpty(mappedByProperty)) {
/*
* Get the columns of the mapped-by property
* copy them and link the copy to the actual value
*/
LOG.debugf("Retrieving property %s.%s", associatedClass.getEntityName(), mappedByProperty);
final Property property = associatedClass.getRecursiveProperty(columns[0].getMappedBy());
List<Column> mappedByColumns;
if (property.getValue() instanceof Collection) {
Collection collection = ((Collection) property.getValue());
Value element = collection.getElement();
if (element == null) {
throw new AnnotationException("Illegal use of mappedBy on both sides of the relationship: " + associatedClass.getEntityName() + "." + mappedByProperty);
}
mappedByColumns = element.getColumns();
} else {
mappedByColumns = property.getValue().getColumns();
}
for (Column column : mappedByColumns) {
columns[0].overrideFromReferencedColumnIfNecessary(column);
columns[0].linkValueUsingAColumnCopy(column, value);
}
} else if (columns[0].isImplicit()) {
/*
* if columns are implicit, then create the columns based on the
* referenced entity id columns
*/
List<Column> idColumns = referencedEntity instanceof JoinedSubclass ? referencedEntity.getKey().getColumns() : referencedEntity.getIdentifier().getColumns();
for (Column column : idColumns) {
columns[0].linkValueUsingDefaultColumnNaming(column, referencedEntity, value);
columns[0].overrideFromReferencedColumnIfNecessary(column);
}
} else {
int fkEnum = AnnotatedJoinColumn.checkReferencedColumnsType(columns, referencedEntity, buildingContext);
if (AnnotatedJoinColumn.NON_PK_REFERENCE == fkEnum) {
String referencedPropertyName;
if (value instanceof ToOne) {
referencedPropertyName = ((ToOne) value).getReferencedPropertyName();
} else if (value instanceof DependantValue) {
String propertyName = columns[0].getPropertyName();
if (propertyName != null) {
Collection collection = (Collection) referencedEntity.getRecursiveProperty(propertyName).getValue();
referencedPropertyName = collection.getReferencedPropertyName();
} else {
throw new AnnotationException("SecondaryTable JoinColumn cannot reference a non primary key");
}
} else {
throw new AssertionFailure("Do a property ref on an unexpected Value type: " + value.getClass().getName());
}
if (referencedPropertyName == null) {
throw new AssertionFailure("No property ref found while expected");
}
Property synthProp = referencedEntity.getReferencedProperty(referencedPropertyName);
if (synthProp == null) {
throw new AssertionFailure("Cannot find synthProp: " + referencedEntity.getEntityName() + "." + referencedPropertyName);
}
linkJoinColumnWithValueOverridingNameIfImplicit(referencedEntity, synthProp.getValue(), columns, value);
if (value instanceof SortableValue) {
((SortableValue) value).sortProperties();
}
} else {
if (AnnotatedJoinColumn.NO_REFERENCE == fkEnum) {
// implicit case, we hope PK and FK columns are in the same order
if (columns.length != referencedEntity.getIdentifier().getColumnSpan()) {
throw new AnnotationException("A Foreign key referring " + referencedEntity.getEntityName() + " from " + associatedClass.getEntityName() + " has the wrong number of column. should be " + referencedEntity.getIdentifier().getColumnSpan());
}
linkJoinColumnWithValueOverridingNameIfImplicit(referencedEntity, referencedEntity.getIdentifier(), columns, value);
if (value instanceof SortableValue) {
((SortableValue) value).sortProperties();
}
} else {
// Ensure the component is sorted so that we can simply set sorted to true on the to-one
if (referencedEntity.getKey() instanceof Component) {
((Component) referencedEntity.getKey()).sortProperties();
}
// explicit referencedColumnName
List<Column> idColumns = referencedEntity.getKey().getColumns();
// works cause the pk has to be on the primary table
Table table = referencedEntity.getTable();
if (idColumns.isEmpty()) {
LOG.debug("No column in the identifier!");
}
for (Column col : idColumns) {
boolean match = false;
// for each PK column, find the associated FK column.
Dialect dialect = buildingContext.getMetadataCollector().getDatabase().getJdbcEnvironment().getDialect();
final String colName = col.getQuotedName(dialect);
for (AnnotatedJoinColumn joinCol : columns) {
String referencedColumn = joinCol.getReferencedColumn();
referencedColumn = buildingContext.getMetadataCollector().getPhysicalColumnName(table, referencedColumn);
// In JPA 2 referencedColumnName is case-insensitive
if (referencedColumn.equalsIgnoreCase(colName)) {
// proper join column
if (joinCol.isNameDeferred()) {
joinCol.linkValueUsingDefaultColumnNaming(col, referencedEntity, value);
} else {
joinCol.linkWithValue(value);
}
joinCol.overrideFromReferencedColumnIfNecessary(col);
match = true;
break;
}
}
if (!match) {
throw new AnnotationException("Column name " + col.getName() + " of " + referencedEntity.getEntityName() + " not found in JoinColumns.referencedColumnName");
}
}
if (value instanceof ToOne) {
((ToOne) value).setSorted(true);
}
}
}
}
value.createForeignKey();
if (unique) {
value.createUniqueKey();
}
}
Aggregations