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
|
* 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
|
* @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();
|
StringBuilder check = new StringBuilder();
|
||||||
check.append( columnName ).append( " in (" );
|
check.append( columnName ).append( " in (" );
|
||||||
String separator = "";
|
String separator = "";
|
||||||
|
boolean nullIsValid = false;
|
||||||
for ( String value : values ) {
|
for ( String value : values ) {
|
||||||
|
if ( value == null ) {
|
||||||
|
nullIsValid = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
check.append( separator ).append('\'').append( value ).append('\'');
|
check.append( separator ).append('\'').append( value ).append('\'');
|
||||||
separator = ",";
|
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) {
|
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}.
|
* by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation}.
|
||||||
*
|
*
|
||||||
* @return a SQL expression that will occur in a {@code check} constraint
|
* @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) {
|
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();
|
StringBuilder check = new StringBuilder();
|
||||||
check.append( columnName ).append( " in (" );
|
check.append( columnName ).append( " in (" );
|
||||||
String separator = "";
|
String separator = "";
|
||||||
for ( long value : values ) {
|
boolean nullIsValid = false;
|
||||||
|
for ( Long value : values ) {
|
||||||
|
if ( value == null ) {
|
||||||
|
nullIsValid = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
check.append( separator ).append( value );
|
check.append( separator ).append( value );
|
||||||
separator = ",";
|
separator = ",";
|
||||||
}
|
}
|
||||||
return check.append( ')' ).toString();
|
check.append( ')' );
|
||||||
|
if ( nullIsValid ) {
|
||||||
|
check.append( " or " ).append( columnName ).append( " is null" );
|
||||||
|
}
|
||||||
|
return check.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -191,9 +191,11 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
BasicValueConverter<Boolean, ?> stringConverter =
|
BasicValueConverter<Boolean, ?> stringConverter =
|
||||||
(BasicValueConverter<Boolean, ?>) converter;
|
(BasicValueConverter<Boolean, ?>) converter;
|
||||||
|
final Object falseValue = stringConverter.toRelationalValue( false );
|
||||||
|
final Object trueValue = stringConverter.toRelationalValue( true );
|
||||||
String[] values = new String[] {
|
String[] values = new String[] {
|
||||||
stringConverter.toRelationalValue(false).toString(),
|
falseValue != null ? falseValue.toString() : null,
|
||||||
stringConverter.toRelationalValue(true).toString()
|
trueValue != null ? trueValue.toString() : null
|
||||||
};
|
};
|
||||||
return dialect.getCheckCondition( columnName, values );
|
return dialect.getCheckCondition( columnName, values );
|
||||||
}
|
}
|
||||||
|
@ -201,9 +203,11 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
BasicValueConverter<Boolean, ? extends Number> numericConverter =
|
BasicValueConverter<Boolean, ? extends Number> numericConverter =
|
||||||
(BasicValueConverter<Boolean, ? extends Number>) converter;
|
(BasicValueConverter<Boolean, ? extends Number>) converter;
|
||||||
long[] values = new long[] {
|
final Number falseValue = numericConverter.toRelationalValue( false );
|
||||||
numericConverter.toRelationalValue(false).longValue(),
|
final Number trueValue = numericConverter.toRelationalValue( true );
|
||||||
numericConverter.toRelationalValue(true).longValue()
|
Long[] values = new Long[] {
|
||||||
|
falseValue != null ? Long.valueOf(falseValue.longValue()) : null,
|
||||||
|
trueValue != null ? Long.valueOf(trueValue.longValue()) : null
|
||||||
};
|
};
|
||||||
return dialect.getCheckCondition( columnName, values );
|
return dialect.getCheckCondition( columnName, values );
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,19 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.mapping.type.java;
|
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.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 org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
@ -15,6 +26,43 @@ import static org.junit.Assert.*;
|
||||||
public class BooleanJavaTypeDescriptorTest {
|
public class BooleanJavaTypeDescriptorTest {
|
||||||
private BooleanJavaType underTest = new BooleanJavaType();
|
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
|
@Test
|
||||||
public void testWrapShouldReturnTrueWhenYStringGiven() {
|
public void testWrapShouldReturnTrueWhenYStringGiven() {
|
||||||
// given
|
// given
|
||||||
|
@ -59,4 +107,73 @@ public class BooleanJavaTypeDescriptorTest {
|
||||||
// then
|
// then
|
||||||
assertFalse(result);
|
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