HHH-17275: Fix NPE in BooleanJavaType for converter returning NULL for relational value
This commit is contained in:
parent
62e6732206
commit
b139d449c8
|
@ -808,7 +808,8 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents an enumerated value
|
||||
* by its {@linkplain jakarta.persistence.EnumType#STRING string representation}.
|
||||
* by its {@linkplain jakarta.persistence.EnumType#STRING string representation}
|
||||
* or a given list of values (with NULL value allowed).
|
||||
*
|
||||
* @return a SQL expression that will occur in a {@code check} constraint
|
||||
*/
|
||||
|
@ -816,11 +817,20 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
StringBuilder check = new StringBuilder();
|
||||
check.append( columnName ).append( " in (" );
|
||||
String separator = "";
|
||||
boolean nullIsValid = false;
|
||||
for ( String value : values ) {
|
||||
if ( value == null ) {
|
||||
nullIsValid = true;
|
||||
continue;
|
||||
}
|
||||
check.append( separator ).append('\'').append( value ).append('\'');
|
||||
separator = ",";
|
||||
}
|
||||
return check.append( ')' ).toString();
|
||||
check.append( ')' );
|
||||
if ( nullIsValid ) {
|
||||
check.append( " or " ).append( columnName ).append( " is null" );
|
||||
}
|
||||
return check.toString();
|
||||
}
|
||||
|
||||
public String getCheckCondition(String columnName, Class<? extends Enum<?>> enumType) {
|
||||
|
@ -842,16 +852,43 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
* by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation}.
|
||||
*
|
||||
* @return a SQL expression that will occur in a {@code check} constraint
|
||||
* @deprecated use {@link #getCheckCondition(String, Long[])} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public String getCheckCondition(String columnName, long[] values) {
|
||||
Long objValues [] = new Long[ values.length ];
|
||||
int i = 0;
|
||||
for( long temp : values){
|
||||
objValues[ i++ ] = temp;
|
||||
}
|
||||
return getCheckCondition(columnName, objValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a SQL check condition for a column that represents an enumerated value
|
||||
* by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation}
|
||||
* or a given list of values.
|
||||
*
|
||||
* @return a SQL expression that will occur in a {@code check} constraint
|
||||
*/
|
||||
public String getCheckCondition(String columnName, Long[] values) {
|
||||
StringBuilder check = new StringBuilder();
|
||||
check.append( columnName ).append( " in (" );
|
||||
String separator = "";
|
||||
for ( long value : values ) {
|
||||
boolean nullIsValid = false;
|
||||
for ( Long value : values ) {
|
||||
if ( value == null ) {
|
||||
nullIsValid = true;
|
||||
continue;
|
||||
}
|
||||
check.append( separator ).append( value );
|
||||
separator = ",";
|
||||
}
|
||||
return check.append( ')' ).toString();
|
||||
check.append( ')' );
|
||||
if ( nullIsValid ) {
|
||||
check.append( " or " ).append( columnName ).append( " is null" );
|
||||
}
|
||||
return check.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -191,9 +191,11 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
|
|||
@SuppressWarnings("unchecked")
|
||||
BasicValueConverter<Boolean, ?> stringConverter =
|
||||
(BasicValueConverter<Boolean, ?>) converter;
|
||||
final Object falseValue = stringConverter.toRelationalValue( false );
|
||||
final Object trueValue = stringConverter.toRelationalValue( true );
|
||||
String[] values = new String[] {
|
||||
stringConverter.toRelationalValue(false).toString(),
|
||||
stringConverter.toRelationalValue(true).toString()
|
||||
falseValue != null ? falseValue.toString() : null,
|
||||
trueValue != null ? trueValue.toString() : null
|
||||
};
|
||||
return dialect.getCheckCondition( columnName, values );
|
||||
}
|
||||
|
@ -201,9 +203,11 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
|
|||
@SuppressWarnings("unchecked")
|
||||
BasicValueConverter<Boolean, ? extends Number> numericConverter =
|
||||
(BasicValueConverter<Boolean, ? extends Number>) converter;
|
||||
long[] values = new long[] {
|
||||
numericConverter.toRelationalValue(false).longValue(),
|
||||
numericConverter.toRelationalValue(true).longValue()
|
||||
final Number falseValue = numericConverter.toRelationalValue( false );
|
||||
final Number trueValue = numericConverter.toRelationalValue( true );
|
||||
Long[] values = new Long[] {
|
||||
falseValue != null ? Long.valueOf(falseValue.longValue()) : null,
|
||||
trueValue != null ? Long.valueOf(trueValue.longValue()) : null
|
||||
};
|
||||
return dialect.getCheckCondition( columnName, values );
|
||||
}
|
||||
|
|
|
@ -6,8 +6,19 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.mapping.type.java;
|
||||
|
||||
import jakarta.persistence.AttributeConverter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.testing.orm.junit.JiraKey;
|
||||
import org.hibernate.type.NumericBooleanConverter;
|
||||
import org.hibernate.type.TrueFalseConverter;
|
||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
|
||||
import org.hibernate.type.descriptor.java.IntegerJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.StringJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.IntegerJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
@ -15,6 +26,43 @@ import static org.junit.Assert.*;
|
|||
public class BooleanJavaTypeDescriptorTest {
|
||||
private BooleanJavaType underTest = new BooleanJavaType();
|
||||
|
||||
@Test
|
||||
@JiraKey( "HHH-17275" )
|
||||
public void testCheckConditionShouldReturnCorrectStatementWhenNullXStringGiven() {
|
||||
// given
|
||||
// when
|
||||
String checkCondition = underTest.getCheckCondition("is_active", VarcharJdbcType.INSTANCE, new BooleanXConverter(), new AnyDialect());
|
||||
// then
|
||||
assertEquals("is_active in ('X') or is_active is null", checkCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckConditionShouldReturnCorrectStatementWhenTFStringGiven() {
|
||||
// given
|
||||
// when
|
||||
String checkCondition = underTest.getCheckCondition("is_active", VarcharJdbcType.INSTANCE, new TrueFalseConverter(), new AnyDialect());
|
||||
// then
|
||||
assertEquals("is_active in ('F','T')", checkCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckConditionShouldReturnCorrectStatementWhen1AndNullIntegerGiven() {
|
||||
// given
|
||||
// when
|
||||
String checkCondition = underTest.getCheckCondition("is_active", IntegerJdbcType.INSTANCE, new OneNullBooleanConverter(), new AnyDialect());
|
||||
// then
|
||||
assertEquals("is_active in (1) or is_active is null", checkCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckConditionShouldReturnCorrectStatementWhen1And0IntegerGiven() {
|
||||
// given
|
||||
// when
|
||||
String checkCondition = underTest.getCheckCondition("is_active", IntegerJdbcType.INSTANCE, new NumericBooleanConverter(), new AnyDialect());
|
||||
// then
|
||||
assertEquals("is_active in (0,1)", checkCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrapShouldReturnTrueWhenYStringGiven() {
|
||||
// given
|
||||
|
@ -59,4 +107,73 @@ public class BooleanJavaTypeDescriptorTest {
|
|||
// then
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
private static class AnyDialect extends Dialect {
|
||||
|
||||
}
|
||||
|
||||
private static class BooleanXConverter implements AttributeConverter<Boolean, String>, BasicValueConverter<Boolean, String> {
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(Boolean value) {
|
||||
return value != null && value ? "X" : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(String value) {
|
||||
return value != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Boolean toDomainValue(@Nullable String relationalForm) {
|
||||
return convertToEntityAttribute(relationalForm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String toRelationalValue(@Nullable Boolean domainForm) {
|
||||
return convertToDatabaseColumn(domainForm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Boolean> getDomainJavaType() {
|
||||
return BooleanJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<String> getRelationalJavaType() {
|
||||
return StringJavaType.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
private static class OneNullBooleanConverter implements AttributeConverter<Boolean, Integer>, BasicValueConverter<Boolean, Integer> {
|
||||
@Override
|
||||
public Integer convertToDatabaseColumn(Boolean attribute) {
|
||||
return attribute != null && attribute ? 1 : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(Integer dbData) {
|
||||
return dbData != null && dbData == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Boolean toDomainValue(@Nullable Integer relationalForm) {
|
||||
return convertToEntityAttribute(relationalForm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Integer toRelationalValue(@Nullable Boolean domainForm) {
|
||||
return convertToDatabaseColumn(domainForm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Boolean> getDomainJavaType() {
|
||||
return BooleanJavaType.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Integer> getRelationalJavaType() {
|
||||
return IntegerJavaType.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue