From c23033dfba9619a6963a396d4c75dfb647efa7f8 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 7 Nov 2025 09:44:29 +0100 Subject: [PATCH 1/2] [#2738] Return the correct column data type for Oracle The Oracle schema extractor is only partially implemented, returning data type 0 for most of the SQL column types. This causes the schema validation to fail even if the columns on the table are valid. --- ...leSqlReactiveInformationExtractorImpl.java | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java index 67852da79..21f2a0dc1 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/OracleSqlReactiveInformationExtractorImpl.java @@ -185,12 +185,45 @@ protected String getResultSetIsNullableLabel() { @Override protected int dataTypeCode(String typeName) { - // ORACLE only supports "float" sql type for double precision - // so return code for double for both double and float column types - if ( typeName.equalsIgnoreCase( "float" ) || - typeName.toLowerCase().startsWith( "double" ) ) { + if ( typeName.equalsIgnoreCase( "float" ) + || typeName.toLowerCase().startsWith( "double" ) + || typeName.equalsIgnoreCase( "binary_double" ) ) { return Types.DOUBLE; } - return super.dataTypeCode( typeName ); + if ( typeName.equalsIgnoreCase( "timestamp" ) ) { + return Types.TIMESTAMP; + } + if ( typeName.equalsIgnoreCase( "timestamp with time zone" ) + || typeName.equalsIgnoreCase( "timestamp with local time zone" ) ) { + return Types.TIMESTAMP_WITH_TIMEZONE; + } + if ( typeName.equalsIgnoreCase( "clob" ) ) { + return Types.CLOB; + } + if ( typeName.equalsIgnoreCase( "blob" ) ) { + return Types.BLOB; + } + if ( typeName.equalsIgnoreCase( "raw" ) ) { + return Types.VARBINARY; + } + if ( typeName.equalsIgnoreCase( "long raw" ) ) { + return Types.LONGVARBINARY; + } + if ( typeName.equalsIgnoreCase( "ref cursor" ) ) { + return Types.REF_CURSOR; + } + if ( typeName.equalsIgnoreCase( "number" ) ) { + return Types.NUMERIC; + } + if ( typeName.equalsIgnoreCase( "date" ) ) { + return Types.DATE; + } + if ( typeName.equalsIgnoreCase( "nvarchar2" ) ) { + return Types.NVARCHAR; + } + if ( typeName.equalsIgnoreCase( "varchar2" ) ) { + return Types.VARCHAR; + } + return 0; } } From 620f24a591c3d89b43a664b1e482d5620334fd71 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Fri, 7 Nov 2025 11:41:37 +0100 Subject: [PATCH 2/2] [#2738] Test column type validation in Oracle --- .../schema/SchemaValidationTestBase.java | 64 ++++++++++++++++++- .../resources/oracle-SchemaValidationTest.sql | 12 ++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 hibernate-reactive-core/src/test/resources/oracle-SchemaValidationTest.sql diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java index 4a5117fee..2034704cc 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/schema/SchemaValidationTestBase.java @@ -6,11 +6,15 @@ package org.hibernate.reactive.schema; +import java.net.URL; + import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.reactive.BaseReactiveTest; -import org.hibernate.reactive.provider.Settings; import org.hibernate.reactive.annotations.DisabledFor; +import org.hibernate.reactive.annotations.EnabledFor; +import org.hibernate.reactive.provider.Settings; import org.hibernate.tool.schema.spi.SchemaManagementException; import org.junit.jupiter.api.AfterEach; @@ -19,6 +23,7 @@ import io.vertx.junit5.Timeout; import io.vertx.junit5.VertxTestContext; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; @@ -26,6 +31,7 @@ import static java.util.concurrent.TimeUnit.MINUTES; import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.ORACLE; import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.GROUPED; import static org.hibernate.tool.schema.JdbcMetadaAccessStrategy.INDIVIDUALLY; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -74,6 +80,11 @@ public void before(VertxTestContext context) { Configuration createConf = constructConfiguration( "create" ); createConf.addAnnotatedClass( BasicTypesTestEntity.class ); + final URL importFileURL = Thread.currentThread() + .getContextClassLoader() + .getResource( "oracle-SchemaValidationTest.sql" ); + createConf.setProperty( AvailableSettings.JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE, importFileURL.getFile() ); + // Make sure that the extra table is not in the db Configuration dropConf = constructConfiguration( "drop" ); dropConf.addAnnotatedClass( Extra.class ); @@ -92,6 +103,18 @@ public void after(VertxTestContext context) { closeFactory( context ); } + @Test + @Timeout(value = 10, timeUnit = MINUTES) + @EnabledFor( ORACLE ) + public void testOracleColumnTypeValidation(VertxTestContext context) { + Configuration validateConf = constructConfiguration( "validate" ); + validateConf.addAnnotatedClass( Fruit.class ); + + StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder() + .applySettings( validateConf.getProperties() ); + test( context, setupSessionFactory( validateConf ) ); + } + // When we have created the table, the validation should pass @Test @Timeout(value = 10, timeUnit = MINUTES) @@ -139,4 +162,43 @@ public static class Extra { private String description; } + + @Entity(name = "Fruit") + public static class Fruit { + + @Id + @GeneratedValue + private Integer id; + + @Column(name = "something_name", nullable = false, updatable = false) + private String name; + + public Fruit() { + } + + public Fruit(String name) { + this.name = name; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "Fruit{" + id + "," + name + '}'; + } + } } diff --git a/hibernate-reactive-core/src/test/resources/oracle-SchemaValidationTest.sql b/hibernate-reactive-core/src/test/resources/oracle-SchemaValidationTest.sql new file mode 100644 index 000000000..69a01b357 --- /dev/null +++ b/hibernate-reactive-core/src/test/resources/oracle-SchemaValidationTest.sql @@ -0,0 +1,12 @@ +-- Import file for testing schema validation in SchemaValidationTest +drop table if exists Fruit cascade constraints +drop sequence if exists Fruit_SEQ + +-- Create the table manually, so that we can check if the validation succeeds +create sequence fruit_seq start with 1 increment by 50; +create table Fruit (id number(10,0) not null, something_name nvarchar2(20) not null, primary key (id)) + +INSERT INTO fruit(id, something_name) VALUES (1, 'Cherry'); +INSERT INTO fruit(id, something_name) VALUES (2, 'Apple'); +INSERT INTO fruit(id, something_name) VALUES (3, 'Banana'); +ALTER SEQUENCE fruit_seq RESTART start with 4; \ No newline at end of file