Skip to content

Commit

Permalink
add explicit handling of JDBC type for Oracle boolean type (#2852)
Browse files Browse the repository at this point in the history
  • Loading branch information
graemerocher authored Apr 8, 2024
1 parent 7596853 commit 9544469
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.data.exceptions.DataAccessException;
import io.micronaut.data.jdbc.config.DataJdbcConfiguration;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.runtime.convert.DataConversionService;
import io.micronaut.data.runtime.mapper.QueryStatement;
import io.micronaut.data.model.DataType;
Expand All @@ -37,6 +39,7 @@
public class JdbcQueryStatement implements QueryStatement<PreparedStatement, Integer> {

private final ConversionService conversionService;
private final DataJdbcConfiguration jdbcConfiguration;

public JdbcQueryStatement() {
this(null);
Expand All @@ -49,22 +52,43 @@ public JdbcQueryStatement() {
* @since 3.1
*/
public JdbcQueryStatement(DataConversionService conversionService) {
// Backwards compatibility should be removed in the next version
this.conversionService = conversionService == null ? ConversionService.SHARED : conversionService;
this(conversionService, new DataJdbcConfiguration("default"));
}

/**
* Constructs a new instance.
*
* @param conversionService The data conversion service
* @param jdbcConfiguration The JDBC configuration
* @since 4.6.1
*/
public JdbcQueryStatement(DataConversionService conversionService, DataJdbcConfiguration jdbcConfiguration) {
this.conversionService = conversionService;
this.jdbcConfiguration = jdbcConfiguration;
}

/**
* Find the SQL type from {@link DataType}.
*
* @param dataType The data type
* @param dialect The dialect
* @return The SQL type
*/
@Internal
public static int findSqlType(@NonNull DataType dataType) {
public static int findSqlType(@NonNull DataType dataType, @NonNull Dialect dialect) {
return switch (dataType) {
case LONG -> Types.BIGINT;
case STRING, JSON -> Types.VARCHAR;
case DATE -> Types.DATE;
case BOOLEAN -> Types.BOOLEAN;
case BOOLEAN -> {
if (dialect == Dialect.ORACLE) {
// oracle driver treats Boolean types as bits
// see https://github.com/micronaut-projects/micronaut-data/issues/1259
yield Types.BIT;
} else {
yield Types.BOOLEAN;
}
}
case INTEGER -> Types.INTEGER;
case TIMESTAMP -> Types.TIMESTAMP;
case TIME -> Types.TIME;
Expand Down Expand Up @@ -96,7 +120,7 @@ public QueryStatement<PreparedStatement, Integer> setDynamic(@NonNull PreparedSt
return this;
}
default -> {
int sqlType = findSqlType(dataType);
int sqlType = findSqlType(dataType, jdbcConfiguration.getDialect());
if (sqlType != -1) {
statement.setNull(index, sqlType);
} else if (dataType.isArray()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public final class DefaultJdbcRepositoryOperations extends AbstractSqlRepository
dataSourceName,
new ColumnNameResultSetReader(conversionService),
new ColumnIndexResultSetReader(conversionService),
new JdbcQueryStatement(conversionService),
new JdbcQueryStatement(conversionService, jdbcConfiguration),
dateTimeProvider,
entityRegistry,
beanContext,
Expand Down Expand Up @@ -565,7 +565,7 @@ private <R> List<R> callProcedure(Connection connection, SqlPreparedQuery<?, R>
preparedQuery.bindParameters(new JdbcParameterBinder(connection, callableStatement, preparedQuery));
if (!preparedQuery.getResultArgument().isVoid()) {
DataType resultDataType = preparedQuery.getResultDataType();
int sqlType = JdbcQueryStatement.findSqlType(resultDataType);
int sqlType = JdbcQueryStatement.findSqlType(resultDataType, jdbcConfiguration.getDialect());
int outIndex = preparedQuery.getQueryBindings().size() + 1;
callableStatement.registerOutParameter(outIndex, sqlType);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.micronaut.data.jdbc.oraclexe.bool;

import io.micronaut.core.annotation.Nullable;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
import jakarta.persistence.Column;

@MappedEntity
public record BooleanTest(
@Id @GeneratedValue
Long id,
@Column(nullable = true)
@Nullable
Boolean canBeNull
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.micronaut.data.jdbc.oraclexe.bool;

import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;

@JdbcRepository(dialect = Dialect.ORACLE)
public interface OracleBooleanTestRepository extends CrudRepository<BooleanTest, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.micronaut.data.jdbc.oraclexe.bool

import io.micronaut.context.ApplicationContext
import io.micronaut.data.jdbc.oraclexe.OracleTestPropertyProvider
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

class OracleXEBooleanTypeSpec extends Specification implements OracleTestPropertyProvider {
@AutoCleanup
@Shared
ApplicationContext applicationContext = ApplicationContext.run(properties)

void "test persist oracle boolean type as null"() {
given:
def repository = applicationContext.getBean(OracleBooleanTestRepository)

when:
def result = repository.save(new BooleanTest(null, null))
result = repository.findById(result.id()).orElse(null)

then:
result != null
result.canBeNull() == null

when:
repository.update(new BooleanTest(result.id(), true))
result = repository.findById(result.id()).orElse(null)

then:
result != null
result.canBeNull()

when:
repository.update(new BooleanTest(result.id(), null))
result = repository.findById(result.id()).orElse(null)

then:
result != null
result.canBeNull() == null

}
}

0 comments on commit 9544469

Please sign in to comment.