diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ddf6b1926..00dcc8932 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,7 +100,7 @@ jobs: strategy: matrix: # db: [ 'MariaDB', 'MySQL', 'PostgreSQL', 'DB2', 'CockroachDB', 'MSSQLServer', 'Oracle' ] - db: [ 'MariaDB', 'MySQL', 'PostgreSQL', 'MSSQLServer', 'CockroachDB', 'Db2' ] + db: [ 'MariaDB', 'MySQL', 'PostgreSQL', 'MSSQLServer', 'CockroachDB', 'Db2', 'Oracle' ] steps: - uses: actions/checkout@v2 - name: Get year/month for cache key diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java index 58b16b610..fbbfe5052 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/adaptor/impl/ResultSetAdaptor.java @@ -83,9 +83,23 @@ public String getString(int columnIndex) { @Override public boolean getBoolean(int columnIndex) { - Boolean bool = row.getBoolean( columnIndex - 1 ); - wasNull = bool == null; - return !wasNull && bool; + try { + Boolean bool = row.getBoolean( columnIndex - 1 ); + wasNull = bool == null; + return !wasNull && bool; + } + catch (ClassCastException cce) { + // Oracle doesn't support an actual boolean/Boolean datatype. + // Oracle8iDialect in ORM registers the BOOLEAN type as a 'number( 1, 0 )' + // so we need to convert the int to a boolean + try { + return getInt( columnIndex ) != 0; + } + catch (Exception e) { + // ignore second exception and throw first cce + throw cce; + } + } } @Override diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java index 894f5b9f9..cecaa1834 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/id/insert/ReactiveAbstractReturningDelegate.java @@ -12,6 +12,7 @@ import org.hibernate.dialect.DB2Dialect; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.Oracle12cDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; @@ -98,6 +99,24 @@ private static String createInsert(PreparedStatementDetails insertStatementDetai // Correct : select id from new table ( insert into LongTypeEntity (id) values (default)) return insertStatementDetails.getSqlString().replace( " values ( ))", " (" + identifierColumnName + ") values (default))" ); } + if( dialect instanceof Oracle12cDialect ) { + final String valuesStr = " values ( )"; + String sql = insertStatementDetails.getSqlString(); + int index = sql.lastIndexOf( sqlEnd ); + // remove "returning id" since it's added via + if ( index > -1 ) { + sql = sql.substring( 0, index ); + } + + // Oracle is expecting values (default) + if ( sql.endsWith( valuesStr ) ) { + index = sql.lastIndexOf( valuesStr ); + sql = sql.substring( 0, index ); + sql = sql + " values (default)"; + } + + return sql; + } return insertStatementDetails.getSqlString(); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java index 888422ee6..aa72a3933 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/NoJdbcEnvironmentInitiator.java @@ -16,7 +16,7 @@ import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; -import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.Oracle12cDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; @@ -144,7 +144,7 @@ protected Class guessDialect(String url) { return SQLServerDialect.class; } if ( url.startsWith( "oracle:" ) ) { - return OracleDialect.class; + return Oracle12cDialect.class; } return null; diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UTCTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UTCTest.java index a4fe19b31..0a6cfe415 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UTCTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/UTCTest.java @@ -27,15 +27,26 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; +import org.hibernate.reactive.testing.DatabaseSelectionRule; +import org.junit.Rule; import org.junit.Test; import io.vertx.ext.unit.TestContext; +import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.ORACLE; + import static org.hibernate.reactive.util.impl.CompletionStages.loop; public class UTCTest extends BaseReactiveTest { + // testDate(), testCalendar(), testLocalDateTime() & testZonedDateTime() fail with.... + // + // throws jakarta.persistence.NoResultException: No result found for query [from ThingInUTC where date=:dt] + // at app//org.hibernate.reactive.query.spi.ReactiveAbstractSelectionQuery.reactiveSingleResult(ReactiveAbstractSelectionQuery.java:175) + @Rule + public final DatabaseSelectionRule skip = DatabaseSelectionRule.skipTestsFor( ORACLE ); + @Override public CompletionStage deleteEntities(Class... entities) { return getSessionFactory() diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java index 57f4da0fa..05cdaae63 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/containers/OracleDatabase.java @@ -22,6 +22,11 @@ import java.util.TimeZone; import java.util.UUID; + +import org.hibernate.type.NumericBooleanConverter; +import org.hibernate.type.TrueFalseConverter; +import org.hibernate.type.YesNoConverter; + import org.testcontainers.containers.OracleContainer; import static org.hibernate.reactive.containers.DockerImage.imageName; @@ -43,10 +48,10 @@ class OracleDatabase implements TestableDatabase { expectedDBTypeForClass.put( Boolean.class, "NUMBER" ); // FIXME: [ORM-6] Check if we need alternatives - // expectedDBTypeForClass.put( NumericBooleanType.class, "NUMBER" ); - // expectedDBTypeForClass.put( TrueFalseType.class, "CHAR" ); - // expectedDBTypeForClass.put( YesNoType.class, "CHAR" ); - // expectedDBTypeForClass.put( PrimitiveByteArrayTypeDescriptor.class, "BLOB" ); + expectedDBTypeForClass.put( NumericBooleanConverter.class, "NUMBER" ); + expectedDBTypeForClass.put( YesNoConverter.class, "CHAR" ); + expectedDBTypeForClass.put( TrueFalseConverter.class, "CHAR" ); + expectedDBTypeForClass.put( byte[].class, "RAW" ); // expectedDBTypeForClass.put( TextType.class, "VARCHAR2" ); expectedDBTypeForClass.put( int.class, "NUMBER" ); @@ -63,9 +68,9 @@ class OracleDatabase implements TestableDatabase { expectedDBTypeForClass.put( TimeZone.class, "VARCHAR2" ); expectedDBTypeForClass.put( Date.class, "DATE" ); expectedDBTypeForClass.put( Timestamp.class, "TIMESTAMP(6)" ); - expectedDBTypeForClass.put( Time.class, "DATE" ); + expectedDBTypeForClass.put( Time.class, "TIMESTAMP(6)" ); expectedDBTypeForClass.put( LocalDate.class, "DATE" ); - expectedDBTypeForClass.put( LocalTime.class, "TIMESTAMP(6)" ); + expectedDBTypeForClass.put( LocalTime.class, "DATE" ); expectedDBTypeForClass.put( LocalDateTime.class, "TIMESTAMP(6)" ); expectedDBTypeForClass.put( BigInteger.class, "NUMBER" ); expectedDBTypeForClass.put( BigDecimal.class, "NUMBER" );