HHH-17460 - Ongoing JPA 32 work
This commit is contained in:
parent
a223cc6439
commit
9b46ced2b3
|
@ -440,20 +440,19 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
|
||||
final List<AnnotationUsage<AttributeOverride>> overrides = element.getRepeatedAnnotationUsages( AttributeOverride.class );
|
||||
if ( CollectionHelper.isNotEmpty( overrides ) ) {
|
||||
final Map<String, List<AnnotationUsage<Column>>> columnOverrideList = new HashMap<>();
|
||||
for ( AnnotationUsage<AttributeOverride> depAttr : overrides ) {
|
||||
final String qualifiedName = StringHelper.qualify( path, depAttr.getString( "name" ) );
|
||||
final AnnotationUsage<Column> column = depAttr.getNestedUsage( "column" );
|
||||
|
||||
if ( columnOverrideList.containsKey( qualifiedName ) ) {
|
||||
if ( columnOverrideMap.containsKey( qualifiedName ) ) {
|
||||
// already an entry, just add to that List
|
||||
columnOverrideList.get( qualifiedName ).add( column );
|
||||
columnOverrideMap.get( qualifiedName ).add( column );
|
||||
}
|
||||
else {
|
||||
// not yet an entry, create the list and add
|
||||
final List<AnnotationUsage<Column>> list = new ArrayList<>();
|
||||
list.add( column );
|
||||
columnOverrideList.put( qualifiedName, list );
|
||||
columnOverrideMap.put( qualifiedName, list );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
*/
|
||||
package org.hibernate.boot.model.internal;
|
||||
|
||||
import jakarta.persistence.CheckConstraint;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.MapsId;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumns;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.annotations.Columns;
|
||||
import org.hibernate.annotations.Formula;
|
||||
|
@ -20,21 +19,27 @@ import org.hibernate.annotations.JoinColumnsOrFormulas;
|
|||
import org.hibernate.annotations.JoinFormula;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.models.spi.AnnotationDescriptor;
|
||||
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
|
||||
import org.hibernate.models.spi.AnnotationUsage;
|
||||
import org.hibernate.models.spi.MemberDetails;
|
||||
import org.hibernate.models.spi.MutableAnnotationUsage;
|
||||
import org.hibernate.models.spi.SourceModelBuildingContext;
|
||||
|
||||
import jakarta.persistence.CheckConstraint;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.ForeignKey;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinColumns;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.MapsId;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OneToOne;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||
import jakarta.persistence.PrimaryKeyJoinColumns;
|
||||
|
||||
import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnFromAnnotation;
|
||||
import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnFromNoAnnotation;
|
||||
|
@ -282,28 +287,23 @@ class ColumnsBuilder {
|
|||
// masquerade as a regular JoinColumn (when a @OneToOne maps to
|
||||
// the primary key of the child table, it's more elegant and more
|
||||
// spec-compliant to map the association with @PrimaryKeyJoinColumn)
|
||||
if ( property.hasAnnotationUsage( PrimaryKeyJoinColumn.class ) ) {
|
||||
// final AnnotationUsage<PrimaryKeyJoinColumn> nested = property.getAnnotationUsage( PrimaryKeyJoinColumn.class );
|
||||
// return new JoinColumn[] { new JoinColumnAdapter( column ) };
|
||||
throw new UnsupportedOperationException( "Not yet implemented" );
|
||||
}
|
||||
else if ( property.hasAnnotationUsage( PrimaryKeyJoinColumns.class ) ) {
|
||||
// final AnnotationUsage<PrimaryKeyJoinColumns> primaryKeyJoinColumns = property.getAnnotationUsage( PrimaryKeyJoinColumns.class );
|
||||
// final List<PrimaryKeyJoinColumn> nested = primaryKeyJoinColumns.getList( "value" );
|
||||
// final JoinColumn[] joinColumns = new JoinColumn[primaryKeyJoinColumns.value().length];
|
||||
// final PrimaryKeyJoinColumn[] columns = primaryKeyJoinColumns.value();
|
||||
// if ( columns.length == 0 ) {
|
||||
// throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData)
|
||||
// + "' has an empty '@PrimaryKeyJoinColumns' annotation" );
|
||||
// }
|
||||
// for ( int i = 0; i < columns.length; i++ ) {
|
||||
// joinColumns[i] = new JoinColumnAdapter( columns[i] );
|
||||
// }
|
||||
// return joinColumns;
|
||||
throw new UnsupportedOperationException( "Not yet implemented" );
|
||||
//
|
||||
// todo : another option better leveraging hibernate-models would be to simply use an untyped AnnotationUsage
|
||||
if ( property.hasAnnotationUsage( PrimaryKeyJoinColumns.class ) ) {
|
||||
final List<AnnotationUsage<JoinColumn>> adapters = new ArrayList<>();
|
||||
property.forEachAnnotationUsage( PrimaryKeyJoinColumn.class, (usage) -> {
|
||||
adapters.add( makePrimaryKeyJoinColumnAdapter( usage, property ) );
|
||||
} );
|
||||
return adapters;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
final AnnotationUsage<PrimaryKeyJoinColumn> pkJoinColumnAnn = property.getAnnotationUsage( PrimaryKeyJoinColumn.class );
|
||||
if ( pkJoinColumnAnn != null ) {
|
||||
return List.of( makePrimaryKeyJoinColumnAdapter( pkJoinColumnAnn, property ) );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -311,6 +311,33 @@ class ColumnsBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private AnnotationUsage<JoinColumn> makePrimaryKeyJoinColumnAdapter(
|
||||
AnnotationUsage<PrimaryKeyJoinColumn> pkJoinColumnAnn,
|
||||
MemberDetails property) {
|
||||
final SourceModelBuildingContext hibernateModelsContext = buildingContext.getMetadataCollector().getSourceModelBuildingContext();
|
||||
final AnnotationDescriptorRegistry descriptorRegistry = hibernateModelsContext.getAnnotationDescriptorRegistry();
|
||||
final AnnotationDescriptor<JoinColumn> joinColumnDescriptor = descriptorRegistry.getDescriptor( JoinColumn.class );
|
||||
return joinColumnDescriptor.createUsage(
|
||||
property,
|
||||
(usage) -> {
|
||||
transferAttribute( "name", pkJoinColumnAnn, usage );
|
||||
transferAttribute( "referencedColumnName", pkJoinColumnAnn, usage );
|
||||
transferAttribute( "columnDefinition", pkJoinColumnAnn, usage );
|
||||
transferAttribute( "options", pkJoinColumnAnn, usage );
|
||||
transferAttribute( "foreignKey", pkJoinColumnAnn, usage );
|
||||
},
|
||||
hibernateModelsContext
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void transferAttribute(
|
||||
String attributeName,
|
||||
AnnotationUsage source,
|
||||
MutableAnnotationUsage target) {
|
||||
target.setAttributeValue( attributeName, source.getAttributeValue( attributeName ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful to override a column either by {@code @MapsId} or by {@code @IdClass}
|
||||
*/
|
||||
|
|
|
@ -113,6 +113,7 @@ import jakarta.persistence.Access;
|
|||
import jakarta.persistence.AccessType;
|
||||
import jakarta.persistence.AssociationOverride;
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.AttributeOverrides;
|
||||
import jakarta.persistence.CheckConstraint;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Convert;
|
||||
|
@ -153,6 +154,7 @@ import jakarta.persistence.TemporalType;
|
|||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.hibernate.boot.models.xml.internal.XmlProcessingHelper.makeNestedAnnotation;
|
||||
import static org.hibernate.internal.util.NullnessHelper.coalesce;
|
||||
|
||||
/**
|
||||
|
@ -348,7 +350,7 @@ public class XmlAnnotationHelper {
|
|||
|
||||
List<AnnotationUsage<Parameter>> parameterAnnList = new ArrayList<>( jaxbParameters.size() );
|
||||
jaxbParameters.forEach( (jaxbParam) -> {
|
||||
final MutableAnnotationUsage<Parameter> annotationUsage = XmlProcessingHelper.makeNestedAnnotation( Parameter.class, target, xmlDocumentContext );
|
||||
final MutableAnnotationUsage<Parameter> annotationUsage = makeNestedAnnotation( Parameter.class, target, xmlDocumentContext );
|
||||
parameterAnnList.add( annotationUsage );
|
||||
annotationUsage.setAttributeValue( "name", jaxbParam.getName() );
|
||||
annotationUsage.setAttributeValue( "value", jaxbParam.getValue() );
|
||||
|
@ -765,15 +767,33 @@ public class XmlAnnotationHelper {
|
|||
return;
|
||||
}
|
||||
|
||||
final MutableAnnotationUsage<AttributeOverrides> attributeOverridesAnn = XmlProcessingHelper.makeAnnotation(
|
||||
AttributeOverrides.class,
|
||||
memberDetails,
|
||||
xmlDocumentContext
|
||||
);
|
||||
memberDetails.addAnnotationUsage( attributeOverridesAnn );
|
||||
|
||||
final ArrayList<MutableAnnotationUsage<AttributeOverride>> overrideAnnList = CollectionHelper.arrayList( jaxbOverrides.size() );
|
||||
attributeOverridesAnn.setAttributeValue( "value", overrideAnnList );
|
||||
|
||||
jaxbOverrides.forEach( (jaxbOverride) -> {
|
||||
final MutableAnnotationUsage<AttributeOverride> annotationUsage = XmlProcessingHelper.makeAnnotation(
|
||||
final MutableAnnotationUsage<AttributeOverride> attributeOverrideAnn = XmlProcessingHelper.makeNestedAnnotation(
|
||||
AttributeOverride.class,
|
||||
memberDetails,
|
||||
xmlDocumentContext
|
||||
);
|
||||
memberDetails.addAnnotationUsage( annotationUsage );
|
||||
annotationUsage.setAttributeValue( "name", prefixIfNotAlready( jaxbOverride.getName(), namePrefix ) );
|
||||
annotationUsage.setAttributeValue( "column", createColumnAnnotation( jaxbOverride.getColumn(), memberDetails, xmlDocumentContext ) );
|
||||
overrideAnnList.add( attributeOverrideAnn );
|
||||
|
||||
attributeOverrideAnn.setAttributeValue( "name", prefixIfNotAlready( jaxbOverride.getName(), namePrefix ) );
|
||||
|
||||
final MutableAnnotationUsage<Column> columnAnn = makeNestedAnnotation(
|
||||
Column.class,
|
||||
memberDetails,
|
||||
xmlDocumentContext
|
||||
);
|
||||
attributeOverrideAnn.setAttributeValue( "column", columnAnn );
|
||||
ColumnProcessing.applyColumnDetails( jaxbOverride.getColumn(), memberDetails, columnAnn, xmlDocumentContext );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -1209,7 +1229,7 @@ public class XmlAnnotationHelper {
|
|||
XmlDocumentContext xmlDocumentContext) {
|
||||
final List<AnnotationUsage<SqlFragmentAlias>> sqlFragmentAliases = new ArrayList<>( aliases.size() );
|
||||
for ( JaxbHbmFilterImpl.JaxbAliasesImpl alias : aliases ) {
|
||||
final MutableAnnotationUsage<SqlFragmentAlias> aliasAnn = XmlProcessingHelper.makeNestedAnnotation( SqlFragmentAlias.class, target, xmlDocumentContext );
|
||||
final MutableAnnotationUsage<SqlFragmentAlias> aliasAnn = makeNestedAnnotation( SqlFragmentAlias.class, target, xmlDocumentContext );
|
||||
aliasAnn.setAttributeValue( "alias", alias.getAlias() );
|
||||
applyAttributeIfSpecified( aliasAnn, "table", alias.getTable() );
|
||||
if ( StringHelper.isNotEmpty( alias.getEntity() ) ) {
|
||||
|
@ -1491,7 +1511,7 @@ public class XmlAnnotationHelper {
|
|||
final List<MutableAnnotationUsage<NamedAttributeNode>> namedAttributeNodeAnnotations =
|
||||
new ArrayList<>( namedAttributeNodes.size() );
|
||||
for ( JaxbNamedAttributeNodeImpl namedAttributeNode : namedAttributeNodes ) {
|
||||
final MutableAnnotationUsage<NamedAttributeNode> namedAttributeNodeAnn = XmlProcessingHelper.makeNestedAnnotation(
|
||||
final MutableAnnotationUsage<NamedAttributeNode> namedAttributeNodeAnn = makeNestedAnnotation(
|
||||
NamedAttributeNode.class,
|
||||
target,
|
||||
xmlDocumentContext
|
||||
|
|
|
@ -48,7 +48,7 @@ public class BasicAttributeProcessing {
|
|||
final MutableAnnotationUsage<Formula> formulaAnn = XmlProcessingHelper.getOrMakeAnnotation( Formula.class, memberDetails, xmlDocumentContext );
|
||||
formulaAnn.setAttributeValue( "value", jaxbBasic.getFormula() );
|
||||
}
|
||||
else {
|
||||
else if ( jaxbBasic.getColumn() != null ) {
|
||||
final MutableAnnotationUsage<Column> columnAnn = XmlProcessingHelper.getOrMakeAnnotation( Column.class, memberDetails, xmlDocumentContext );
|
||||
ColumnProcessing.applyColumnDetails( jaxbBasic.getColumn(), memberDetails, columnAnn, xmlDocumentContext );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.orm.test.boot.models.xml.override;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.internal.BootstrapContextImpl;
|
||||
import org.hibernate.boot.internal.InFlightMetadataCollectorImpl;
|
||||
import org.hibernate.boot.internal.MetadataBuilderImpl.MetadataBuildingOptionsImpl;
|
||||
import org.hibernate.boot.model.process.spi.ManagedResources;
|
||||
import org.hibernate.boot.model.process.spi.MetadataBuildingProcess;
|
||||
import org.hibernate.boot.model.source.internal.annotations.DomainModelSource;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.models.spi.AnnotationUsage;
|
||||
import org.hibernate.models.spi.ClassDetails;
|
||||
import org.hibernate.models.spi.ClassDetailsRegistry;
|
||||
import org.hibernate.models.spi.FieldDetails;
|
||||
import org.hibernate.orm.test.jpa.xml.Employee;
|
||||
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.AttributeOverrides;
|
||||
import jakarta.persistence.Column;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hibernate.boot.model.process.spi.MetadataBuildingProcess.processManagedResources;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AttributeOverrideXmlTests {
|
||||
@Test
|
||||
@ServiceRegistry
|
||||
void testBasicHandling(ServiceRegistryScope serviceRegistryScope) {
|
||||
final StandardServiceRegistry registry = serviceRegistryScope.getRegistry();
|
||||
|
||||
final MetadataSources metadataSources = new MetadataSources().addResource( "org/hibernate/orm/test/jpa/xml/orm3.xml" );
|
||||
final MetadataBuildingOptionsImpl options = new MetadataBuildingOptionsImpl( registry );
|
||||
final BootstrapContextImpl bootstrapContext = new BootstrapContextImpl( registry, options );
|
||||
options.setBootstrapContext( bootstrapContext );
|
||||
|
||||
final ManagedResources managedResources = MetadataBuildingProcess.prepare( metadataSources, bootstrapContext );
|
||||
final InFlightMetadataCollectorImpl metadataCollector = new InFlightMetadataCollectorImpl( bootstrapContext, options );
|
||||
|
||||
final DomainModelSource domainModelSource = processManagedResources(
|
||||
managedResources,
|
||||
metadataCollector,
|
||||
bootstrapContext
|
||||
);
|
||||
|
||||
final ClassDetailsRegistry classDetailsRegistry = domainModelSource.getClassDetailsRegistry();
|
||||
final ClassDetails employeeClassDetails = classDetailsRegistry.getClassDetails( Employee.class.getName() );
|
||||
assertThat( employeeClassDetails.getFields() ).hasSize( 4 );
|
||||
|
||||
final FieldDetails homeAddressField = employeeClassDetails.findFieldByName( "homeAddress" );
|
||||
checkOverrides( homeAddressField, "home_" );
|
||||
|
||||
final FieldDetails mailAddressField = employeeClassDetails.findFieldByName( "mailAddress" );
|
||||
checkOverrides( mailAddressField, "mail_" );
|
||||
|
||||
|
||||
final ClassDetails embeddable = homeAddressField.getType().determineRawClass();
|
||||
assertThat( embeddable ).isNotNull();
|
||||
assertThat( embeddable.getFields() ).hasSize( 4 );
|
||||
|
||||
final FieldDetails cityField = embeddable.findFieldByName( "city" );
|
||||
assertThat( cityField.hasAnnotationUsage( Column.class ) ).isFalse();
|
||||
}
|
||||
|
||||
private static void checkOverrides(FieldDetails embedded, String prefix) {
|
||||
final AnnotationUsage<AttributeOverrides> overridesUsage = embedded.getAnnotationUsage( AttributeOverrides.class );
|
||||
assertThat( overridesUsage ).isNotNull();
|
||||
|
||||
final List<AnnotationUsage<AttributeOverride>> overrideList = overridesUsage.getList( "value" );
|
||||
assertThat( overrideList ).hasSize( 4 );
|
||||
|
||||
for ( AnnotationUsage<AttributeOverride> overrideUsage : overrideList ) {
|
||||
final String attributeName = overrideUsage.getString( "name" );
|
||||
|
||||
final AnnotationUsage<Column> columnUsage = overrideUsage.getNestedUsage( "column" );
|
||||
final String columnName = columnUsage.getString( "name" );
|
||||
|
||||
if ( attributeName.equals( "street" ) ) {
|
||||
assertThat( columnName ).isEqualTo( prefix + "street" );
|
||||
}
|
||||
else if ( attributeName.equals( "city" ) ) {
|
||||
assertThat( columnName ).isEqualTo( prefix + "city" );
|
||||
}
|
||||
else if ( attributeName.equals( "state" ) ) {
|
||||
assertThat( columnName ).isEqualTo( prefix + "state" );
|
||||
}
|
||||
else if ( attributeName.equals( "zip" ) ) {
|
||||
assertThat( columnName ).isEqualTo( prefix + "zip" );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue