HHH-17448 Add newly standard column annotation attributes to Hibernate column annotations

This commit is contained in:
Andrea Boriero 2024-07-29 21:39:25 +02:00 committed by Steve Ebersole
parent ec556f0fa5
commit 0b964a3f19
5 changed files with 198 additions and 3 deletions

View File

@ -68,4 +68,22 @@ public @interface TimeZoneColumn {
*/
String table() default "";
/**
* (Optional) The SQL fragment that is used when
* generating the DDL for the column.
* <p>
* The DDL must be written in the native SQL dialect
* of the target database (it is not portable across databases).
*
* @since 7.0
*/
String options() default "";
/**
* (Optional) A comment to be applied to the column.
*
* @since 7.0
*/
String comment() default "";
}

View File

@ -504,6 +504,8 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
created.nullable( column.nullable() );
if ( timeZoneColumn != null ) {
created.options( timeZoneColumn.options() );
created.comment( timeZoneColumn.comment() );
created.table( timeZoneColumn.table() );
created.insertable( timeZoneColumn.insertable() );
created.updatable( timeZoneColumn.updatable() );
@ -514,6 +516,8 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
created.insertable( column.insertable() );
created.updatable( column.updatable() );
created.columnDefinition( column.columnDefinition() );
created.options( column.options() );
created.comment( column.comment() );
}
return created;

View File

@ -99,6 +99,8 @@ public class AnnotatedColumn {
private String options;
private String comment;
public AnnotatedColumns getParent() {
return parent;
}
@ -267,9 +269,9 @@ public class AnnotatedColumn {
}
mappingColumn.setOptions( options );
// if ( isNotEmpty( comment ) ) {
// mappingColumn.setComment( comment );
// }
if ( isNotEmpty( comment ) ) {
mappingColumn.setComment( comment );
}
if ( generatedAs != null ) {
mappingColumn.setGeneratedAs( generatedAs );
}
@ -318,6 +320,7 @@ public class AnnotatedColumn {
}
mappingColumn.setDefaultValue( defaultValue );
mappingColumn.setOptions( options );
mappingColumn.setComment( comment );
if ( writeExpression != null ) {
final int numberOfJdbcParams = StringHelper.count( writeExpression, '?' );
@ -814,6 +817,7 @@ public class AnnotatedColumn {
annotatedColumn.applyGeneratedAs( inferredData, numberOfColumns );
annotatedColumn.applyColumnCheckConstraint( column );
annotatedColumn.applyColumnOptions( column );
annotatedColumn.applyColumnComment(column);
annotatedColumn.applyCheckConstraint( inferredData, numberOfColumns );
annotatedColumn.extractDataFromPropertyData( propertyHolder, inferredData, sourceModelContext );
annotatedColumn.bind();
@ -1036,6 +1040,12 @@ public class AnnotatedColumn {
options = column.options();
}
private void applyColumnComment(jakarta.persistence.Column column) {
if ( !column.comment().isEmpty() ) {
comment = column.comment();
}
}
void setOptions(String options){
this.options = options;
}

View File

@ -24,6 +24,8 @@ public class TimeZoneColumnAnnotation implements TimeZoneColumn {
private boolean updatable;
private String columnDefinition;
private String table;
private String options;
private String comment;
/**
* Used in creating dynamic annotation instances (e.g. from XML)
@ -34,6 +36,8 @@ public class TimeZoneColumnAnnotation implements TimeZoneColumn {
this.updatable = true;
this.columnDefinition = "";
this.table = "";
this.options = "";
this.comment = "";
}
/**
@ -45,6 +49,8 @@ public class TimeZoneColumnAnnotation implements TimeZoneColumn {
this.updatable = annotation.updatable();
this.columnDefinition = annotation.columnDefinition();
this.table = annotation.table();
this.options = annotation.options();
this.comment = annotation.comment();
}
/**
@ -71,6 +77,8 @@ public class TimeZoneColumnAnnotation implements TimeZoneColumn {
modelContext
);
this.table = extractJandexValue( annotation, HibernateAnnotations.TIME_ZONE_COLUMN, "table", modelContext );
this.options = extractJandexValue( annotation, HibernateAnnotations.TIME_ZONE_COLUMN, "options", modelContext );
this.comment = extractJandexValue( annotation, HibernateAnnotations.TIME_ZONE_COLUMN, "comment", modelContext );
}
@Override
@ -127,5 +135,21 @@ public class TimeZoneColumnAnnotation implements TimeZoneColumn {
this.table = value;
}
@Override
public String options() {
return options;
}
public void options(String value) {
this.options = value;
}
@Override
public String comment() {
return comment;
}
public void comment(String value) {
this.comment = value;
}
}

View File

@ -0,0 +1,139 @@
package org.hibernate.orm.test.schemaupdate;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.OffsetTime;
import java.util.EnumSet;
import java.util.Locale;
import org.hibernate.annotations.TimeZoneColumn;
import org.hibernate.annotations.TimeZoneStorage;
import org.hibernate.annotations.TimeZoneStorageType;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.testing.orm.junit.BaseUnitTest;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.util.ServiceRegistryUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BaseUnitTest
@JiraKey("HHH-17448")
public class TimeZoneColumnTest {
private File output;
private StandardServiceRegistry ssr;
private MetadataImplementor metadata;
@BeforeEach
public void setUp() throws IOException {
output = File.createTempFile( "update_script", ".sql" );
output.deleteOnExit();
ssr = ServiceRegistryUtil.serviceRegistry();
}
@AfterEach
public void tearsDown() {
output.delete();
StandardServiceRegistryBuilder.destroy( ssr );
}
@Test
public void testTableCommentAreCreated() throws Exception {
createSchema( TestEntity.class );
assertTrue(
tableCreationStatementContainsOptions( output, "birthtime_offset_offset", "option_1" ),
"TimeZoneColumn options have not been created "
);
JdbcEnvironment jdbcEnvironment = ssr.getService( JdbcEnvironment.class );
Dialect dialect = jdbcEnvironment.getDialect();
if ( dialect.supportsCommentOn() ) {
assertTrue(
tableCreationStatementContainsComment( output, "birthtime_offset_offset", "This is a comment" ),
"TimeZoneColumn comment have not been created "
);
}
}
private void createSchema(Class... annotatedClasses) {
final MetadataSources metadataSources = new MetadataSources( ssr );
for ( Class c : annotatedClasses ) {
metadataSources.addAnnotatedClass( c );
}
metadata = (MetadataImplementor) metadataSources.buildMetadata();
metadata.orderColumns( false );
metadata.validate();
new SchemaExport()
.setHaltOnError( true )
.setOutputFile( output.getAbsolutePath() )
.setFormat( false )
.createOnly( EnumSet.of( TargetType.SCRIPT ), metadata );
}
private static boolean tableCreationStatementContainsOptions(
File output,
String columnName,
String options) throws Exception {
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
.split( System.lineSeparator() );
for ( int i = 0; i < fileContent.length; i++ ) {
String statement = fileContent[i].toUpperCase( Locale.ROOT );
if ( statement.contains( options.toUpperCase( Locale.ROOT ) ) ) {
return true;
}
}
return false;
}
private static boolean tableCreationStatementContainsComment(
File output,
String columnName,
String comment) throws Exception {
String[] fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase()
.split( System.lineSeparator() );
for ( int i = 0; i < fileContent.length; i++ ) {
String statement = fileContent[i].toUpperCase( Locale.ROOT );
if ( statement.contains( comment.toUpperCase( Locale.ROOT ) ) ) {
return true;
}
}
return false;
}
@Entity(name = "TestEntity")
public static class TestEntity {
@Id
private Long id;
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
@TimeZoneColumn(name = "birthtime_offset_offset", comment = "This is a comment", options = "option_1")
@Column(name = "birthtime_offset")
private OffsetTime offsetTimeColumn;
// @TimeZoneStorage(TimeZoneStorageType.COLUMN)
// @TimeZoneColumn(name = "birthday_zoned_offset")
// @Column(name = "birthday_zoned")
// private ZonedDateTime zonedDateTimeColumn;
}
}