diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/ColumnProcessing.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/ColumnProcessing.java index 7199170b44..c8d7950e12 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/ColumnProcessing.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/ColumnProcessing.java @@ -145,7 +145,7 @@ public class ColumnProcessing { JaxbColumnNullable jaxbColumn, MutableAnnotationUsage columnAnn, XmlDocumentContext xmlDocumentContext) { - XmlProcessingHelper.applyAttributeIfSpecified( "unique", jaxbColumn.isNullable(), columnAnn ); + XmlProcessingHelper.applyAttributeIfSpecified( "nullable", jaxbColumn.isNullable(), columnAnn ); } private static void applyColumnMutability( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/intg/AdditionalMappingContributorBasicColumnTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/AdditionalMappingContributorBasicColumnTests.java new file mode 100644 index 0000000000..4ab1bc512a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/intg/AdditionalMappingContributorBasicColumnTests.java @@ -0,0 +1,328 @@ +/* + * 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.intg; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Locale; + +import org.hibernate.boot.ResourceStreamLocator; +import org.hibernate.boot.spi.AdditionalMappingContributions; +import org.hibernate.boot.spi.AdditionalMappingContributor; +import org.hibernate.boot.spi.InFlightMetadataCollector; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; + +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry; +import org.hibernate.testing.orm.junit.BootstrapServiceRegistry.JavaService; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.junit.jupiter.api.Test; + +public class AdditionalMappingContributorBasicColumnTests { + + @Test + @BootstrapServiceRegistry( + javaServices = @JavaService( + role = AdditionalMappingContributor.class, + impl = NameColumnOrmXmlContributor.class + ) + ) + @DomainModel + @SuppressWarnings("JUnitMalformedDeclaration") + void name(DomainModelScope domainModelScope) { + verifyOrmXmlContribution( domainModelScope, new NameColumnOrmXmlContributor() ); + } + + @Test + @BootstrapServiceRegistry( + javaServices = @JavaService( + role = AdditionalMappingContributor.class, + impl = UniqueColumnOrmXmlContributor.class + ) + ) + @DomainModel + @SuppressWarnings("JUnitMalformedDeclaration") + void unique(DomainModelScope domainModelScope) { + verifyOrmXmlContribution( domainModelScope, new UniqueColumnOrmXmlContributor() ); + } + + @Test + @BootstrapServiceRegistry( + javaServices = @JavaService( + role = AdditionalMappingContributor.class, + impl = NullableColumnOrmXmlContributor.class + ) + ) + @DomainModel + @SuppressWarnings("JUnitMalformedDeclaration") + void nullable(DomainModelScope domainModelScope) { + verifyOrmXmlContribution( domainModelScope, new NullableColumnOrmXmlContributor() ); + } + + @Test + @BootstrapServiceRegistry( + javaServices = @JavaService( + role = AdditionalMappingContributor.class, + impl = LengthColumnOrmXmlContributor.class + ) + ) + @DomainModel + @SuppressWarnings("JUnitMalformedDeclaration") + void length(DomainModelScope domainModelScope) { + verifyOrmXmlContribution( domainModelScope, new LengthColumnOrmXmlContributor() ); + } + + @Test + @BootstrapServiceRegistry( + javaServices = @JavaService( + role = AdditionalMappingContributor.class, + impl = PrecisionColumnOrmXmlContributor.class + ) + ) + @DomainModel + @SuppressWarnings("JUnitMalformedDeclaration") + void precision(DomainModelScope domainModelScope) { + verifyOrmXmlContribution( domainModelScope, new PrecisionColumnOrmXmlContributor() ); + } + + @Test + @BootstrapServiceRegistry( + javaServices = @JavaService( + role = AdditionalMappingContributor.class, + impl = ScaleColumnOrmXmlContributor.class + ) + ) + @DomainModel + @SuppressWarnings("JUnitMalformedDeclaration") + void scale(DomainModelScope domainModelScope) { + verifyOrmXmlContribution( domainModelScope, new ScaleColumnOrmXmlContributor() ); + } + + void verifyOrmXmlContribution(DomainModelScope domainModelScope, ColumnOrmXmlContributor contributor) { + final PersistentClass binding = domainModelScope.getDomainModel().getEntityBinding( Entity1.class.getName() ); + assertThat( binding ).isNotNull(); + assertThat( binding.getIdentifierProperty() ).isNotNull(); + assertThat( binding.getProperties() ).hasSize( 2 ); + assertThat( binding.getProperties().stream() + .filter( property -> contributor.attributeName.equals( property.getName() ) ) + .findFirst().orElseThrow() ) + .satisfies( property -> assertColumn( property, contributor ) ); + } + + private void assertColumn(Property property, ColumnOrmXmlContributor contributor) { + assertThat( property.getColumns() ).hasSize( 1 ); + assertThat( property.getColumns().get( 0 ) ).satisfies( column -> { + assertThat( column.getName() ).isEqualTo( contributor.defaultName() ); + assertThat( column.isUnique() ).isEqualTo( contributor.defaultUnique() ); + assertThat( column.isNullable() ).isEqualTo( contributor.defaultNullable() ); + assertThat( column.getLength() ).isEqualTo( contributor.defaultLength() ); + assertThat( column.getPrecision() ).isEqualTo( contributor.defaultPrecision() ); + assertThat( column.getScale() ).isEqualTo( contributor.defaultScale() ); + } ); + } + + + public static class Entity1 { + private Integer id; + private String name; + + private Double number; + + @SuppressWarnings("unused") + protected Entity1() { + // for use by Hibernate + } + + public Entity1(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getNumber() { + return number; + } + + public void setNumber(Double number) { + this.number = number; + } + } + + public static class NameColumnOrmXmlContributor extends ColumnOrmXmlContributor { + + public NameColumnOrmXmlContributor() { + super( "name", "name", "something" ); + } + + @Override + public String defaultName() { + return "something"; + } + } + + public static class UniqueColumnOrmXmlContributor extends ColumnOrmXmlContributor { + + public UniqueColumnOrmXmlContributor() { + super( "name", "unique", true ); + } + + @Override + public boolean defaultUnique() { + return true; + } + } + + public static class NullableColumnOrmXmlContributor extends ColumnOrmXmlContributor { + + public NullableColumnOrmXmlContributor() { + super( "name", "nullable", true ); + } + } + + public static class LengthColumnOrmXmlContributor extends ColumnOrmXmlContributor { + + public LengthColumnOrmXmlContributor() { + super( "name", "length", 512 ); + } + + @Override + public int defaultLength() { + return 512; + } + } + + public static class PrecisionColumnOrmXmlContributor extends ColumnOrmXmlContributor { + + public PrecisionColumnOrmXmlContributor() { + super( "number", "precision", 5 ); + } + + @Override + public Integer defaultPrecision() { + return 5; + } + + @Override + public Integer defaultScale() { + return 0; + } + } + + public static class ScaleColumnOrmXmlContributor extends ColumnOrmXmlContributor { + + public ScaleColumnOrmXmlContributor() { + // scale without precision is ignored + super( "number", "scale", 5 ); + } + + @Override + public Integer defaultScale() { + return null; + } + + @Override + public Integer defaultPrecision() { + return null; + } + } + + public static abstract class ColumnOrmXmlContributor implements AdditionalMappingContributor { + + private final String attributeName; + private final String columnAttribute; + private final Object columnAttributeValue; + + protected ColumnOrmXmlContributor(String attributeName, String columnAttribute, Object columnAttributeValue) { + this.attributeName = attributeName; + this.columnAttribute = columnAttribute; + this.columnAttributeValue = columnAttributeValue; + } + + @Override + public void contribute( + AdditionalMappingContributions contributions, + InFlightMetadataCollector metadata, + ResourceStreamLocator resourceStreamLocator, + MetadataBuildingContext buildingContext) { + try ( final InputStream stream = new ByteArrayInputStream( String.format( Locale.ROOT, + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "", attributeName, columnAttribute, columnAttributeValue + ).getBytes( StandardCharsets.UTF_8 ) ) ) { + + contributions.contributeBinding( stream ); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } + + public String defaultName() { + return attributeName; + } + + public boolean defaultUnique() { + return false; + } + + public boolean defaultNullable() { + return true; + } + + public boolean defaultInsertable() { + return true; + } + + public boolean defaultUpdatable() { + return true; + } + + public String defaultColumnDefinition() { + return ""; + } + + public String defaultTable() { + return ""; + } + + public int defaultLength() { + return 255; + } + + public Integer defaultPrecision() { + return null; + } + + public Integer defaultScale() { + return null; + } + } +}