HHH-17840 Fix inconsistency of read/write null JsonNode/JsonValue

This commit is contained in:
imunic 2024-03-12 21:55:09 +01:00 committed by Christian Beikov
parent a882fbdf0c
commit c5d5bc1922
17 changed files with 235 additions and 79 deletions

View File

@ -70,7 +70,6 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLe
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.descriptor.jdbc.EnumJdbcType;
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.OrdinalEnumJdbcType;
import org.hibernate.type.descriptor.jdbc.TimeAsTimestampWithTimeZoneJdbcType;
@ -294,7 +293,7 @@ public class H2LegacyDialect extends Dialect {
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
}
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
}
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );

View File

@ -66,8 +66,8 @@ dependencies {
testRuntimeOnly libs.byteBuddy
testRuntimeOnly testLibs.weld
testRuntimeOnly testLibs.wildFlyTxnClient
testRuntimeOnly jakartaLibs.jsonb
testRuntimeOnly libs.jackson
testImplementation jakartaLibs.jsonb
testImplementation libs.jackson
testRuntimeOnly libs.jacksonXml
testRuntimeOnly libs.jacksonJsr310

View File

@ -65,7 +65,6 @@ import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.descriptor.jdbc.EnumJdbcType;
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.OrdinalEnumJdbcType;
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
@ -244,7 +243,7 @@ public class H2Dialect extends Dialect {
jdbcTypeRegistry.addDescriptor( TimestampUtcAsInstantJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
}

View File

@ -0,0 +1,67 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.dialect;
import java.nio.charset.StandardCharsets;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
/**
* H2 requires binding JSON via {@code setBytes} methods.
*/
public class H2JsonJdbcType extends JsonJdbcType {
/**
* Singleton access
*/
public static final H2JsonJdbcType INSTANCE = new H2JsonJdbcType( null );
protected H2JsonJdbcType(EmbeddableMappingType embeddableMappingType) {
super( embeddableMappingType );
}
@Override
public String toString() {
return "FormatJsonJdbcType";
}
@Override
public AggregateJdbcType resolveAggregateJdbcType(
EmbeddableMappingType mappingType,
String sqlType,
RuntimeModelCreationContext creationContext) {
return new H2JsonJdbcType( mappingType );
}
@Override
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
return new BasicBinder<>( javaType, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
final String json = ( (H2JsonJdbcType) getJdbcType() ).toString( value, getJavaType(), options );
st.setBytes( index, json.getBytes( StandardCharsets.UTF_8 ) );
}
@Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
throws SQLException {
final String json = ( (H2JsonJdbcType) getJdbcType() ).toString( value, getJavaType(), options );
st.setBytes( name, json.getBytes( StandardCharsets.UTF_8 ) );
}
};
}
}

View File

@ -13,6 +13,8 @@ import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.type.descriptor.java.JavaType;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents a literal value in the sqm, e.g.<ul>
* <li>1</li>
@ -75,13 +77,18 @@ public class SqmLiteral<T> extends AbstractSqmExpression<T> {
appendHqlString( sb, getJavaTypeDescriptor(), getLiteralValue() );
}
public static <T> void appendHqlString(StringBuilder sb, JavaType<T> javaType, T value) {
final String string = javaType.toString( value );
if ( javaType.getJavaTypeClass() == String.class ) {
QueryLiteralHelper.appendStringLiteral( sb, string );
public static <T> void appendHqlString(StringBuilder sb, JavaType<T> javaType, @Nullable T value) {
if ( value == null ) {
sb.append( "null" );
}
else {
sb.append( string );
final String string = javaType.toString( value );
if ( javaType.getJavaTypeClass() == String.class ) {
QueryLiteralHelper.appendStringLiteral( sb, string );
}
else {
sb.append( string );
}
}
}

View File

@ -296,7 +296,7 @@ public class EnumType<T extends Enum<T>>
@Override @SuppressWarnings("unchecked")
public String toLoggableString(Object value, SessionFactoryImplementor factory) {
verifyConfigured();
return enumJavaType.toString( (T) value );
return enumJavaType.extractLoggableRepresentation( (T) value );
}
public boolean isOrdinal() {

View File

@ -154,7 +154,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException A problem occurred performing the comparison
*/
boolean isSame(Object x, Object y) throws HibernateException;
boolean isSame(@Nullable Object x, @Nullable Object y) throws HibernateException;
/**
* Compare two instances of the class mapped by this type for persistence "equality",
@ -173,7 +173,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException A problem occurred performing the comparison
*/
boolean isEqual(Object x, Object y) throws HibernateException;
boolean isEqual(@Nullable Object x, @Nullable Object y) throws HibernateException;
/**
* Compare two instances of the class mapped by this type for persistence "equality",
@ -193,7 +193,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException A problem occurred performing the comparison
*/
boolean isEqual(Object x, Object y, SessionFactoryImplementor factory) throws HibernateException;
boolean isEqual(@Nullable Object x, @Nullable Object y, SessionFactoryImplementor factory) throws HibernateException;
/**
* Get a hash code, consistent with persistence "equality". For most types this could
@ -229,9 +229,9 @@ public interface Type extends Serializable {
*
* @see java.util.Comparator#compare(Object, Object)
*/
int compare(Object x, Object y);
int compare(@Nullable Object x, @Nullable Object y);
int compare(Object x, Object y, SessionFactoryImplementor sessionFactory);
int compare(@Nullable Object x, @Nullable Object y, SessionFactoryImplementor sessionFactory);
/**
* Should the parent be considered dirty, given both the old and current value?
@ -244,7 +244,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException A problem occurred performing the checking
*/
boolean isDirty(Object old, Object current, SharedSessionContractImplementor session) throws HibernateException;
boolean isDirty(@Nullable Object old, @Nullable Object current, SharedSessionContractImplementor session) throws HibernateException;
/**
* Should the parent be considered dirty, given both the old and current value?
@ -258,7 +258,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException A problem occurred performing the checking
*/
boolean isDirty(Object oldState, Object currentState, boolean[] checkable, SharedSessionContractImplementor session)
boolean isDirty(@Nullable Object oldState, @Nullable Object currentState, boolean[] checkable, SharedSessionContractImplementor session)
throws HibernateException;
/**
@ -277,8 +277,8 @@ public interface Type extends Serializable {
* @throws HibernateException A problem occurred performing the checking
*/
boolean isModified(
Object dbState,
Object currentState,
@Nullable Object dbState,
@Nullable Object currentState,
boolean[] checkable,
SharedSessionContractImplementor session)
throws HibernateException;
@ -300,7 +300,7 @@ public interface Type extends Serializable {
*/
void nullSafeSet(
PreparedStatement st,
Object value,
@Nullable Object value,
int index,
boolean[] settable,
SharedSessionContractImplementor session)
@ -320,7 +320,7 @@ public interface Type extends Serializable {
* @throws HibernateException An error from Hibernate
* @throws SQLException An error from the JDBC driver
*/
void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
void nullSafeSet(PreparedStatement st, @Nullable Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException;
/**
@ -353,7 +353,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException An error from Hibernate
*/
Object deepCopy(Object value, SessionFactoryImplementor factory)
@Nullable Object deepCopy(@Nullable Object value, SessionFactoryImplementor factory)
throws HibernateException;
/**
@ -392,7 +392,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException An error from Hibernate
*/
default Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) throws HibernateException {
default @Nullable Serializable disassemble(@Nullable Object value, SessionFactoryImplementor sessionFactory) throws HibernateException {
return disassemble( value, null, null );
}
@ -410,7 +410,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException An error from Hibernate
*/
Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner) throws HibernateException;
@Nullable Serializable disassemble(@Nullable Object value, @Nullable SharedSessionContractImplementor session, @Nullable Object owner) throws HibernateException;
/**
* Reconstruct the object from its disassembled state. This function is the inverse of
@ -424,7 +424,7 @@ public interface Type extends Serializable {
*
* @throws HibernateException An error from Hibernate
*/
Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException;
@Nullable Object assemble(@Nullable Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException;
/**
* Called before assembling a query result set from the query cache, to allow batch
@ -432,7 +432,9 @@ public interface Type extends Serializable {
*
* @param cached The key
* @param session The originating session
* @deprecated Is not called anymore
*/
@Deprecated(forRemoval = true, since = "6.6")
void beforeAssemble(Serializable cached, SharedSessionContractImplementor session);
/**
@ -452,9 +454,9 @@ public interface Type extends Serializable {
*
* @throws HibernateException An error from Hibernate
*/
Object replace(
Object original,
Object target,
@Nullable Object replace(
@Nullable Object original,
@Nullable Object target,
SharedSessionContractImplementor session,
Object owner,
Map<Object, Object> copyCache) throws HibernateException;
@ -477,9 +479,9 @@ public interface Type extends Serializable {
*
* @throws HibernateException An error from Hibernate
*/
Object replace(
Object original,
Object target,
@Nullable Object replace(
@Nullable Object original,
@Nullable Object target,
SharedSessionContractImplementor session,
Object owner,
Map<Object, Object> copyCache,
@ -494,5 +496,5 @@ public interface Type extends Serializable {
*
* @return array indicating column nullness for a value instance
*/
boolean[] toColumnNullness(Object value, Mapping mapping);
boolean[] toColumnNullness(@Nullable Object value, Mapping mapping);
}

View File

@ -26,6 +26,8 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.spi.TypeConfiguration;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Descriptor for the Java side of a value mapping. A {@code JavaType} is always
* coupled with a {@link JdbcType} to describe the typing aspects of an attribute
@ -228,12 +230,12 @@ public interface JavaType<T> extends Serializable {
*
* @return The loggable representation
*/
default String extractLoggableRepresentation(T value) {
return toString( value );
default String extractLoggableRepresentation(@Nullable T value) {
return value == null ? "null" : toString( value );
}
default String toString(T value) {
return value == null ? "null" : value.toString();
return value.toString();
}
T fromString(CharSequence string);

View File

@ -10,6 +10,8 @@ import java.io.Serializable;
import org.hibernate.SharedSessionContract;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Describes the mutability aspects of a given Java type.
* <p>
@ -65,7 +67,7 @@ public interface MutabilityPlan<T> extends Serializable {
*
* @return The deep copy.
*/
T deepCopy(T value);
@Nullable T deepCopy(@Nullable T value);
/**
* Return a disassembled representation of the value.
@ -76,7 +78,7 @@ public interface MutabilityPlan<T> extends Serializable {
*
* @see #assemble
*/
Serializable disassemble(T value, SharedSessionContract session);
@Nullable Serializable disassemble(@Nullable T value, SharedSessionContract session);
/**
* Assemble a previously {@linkplain #disassemble disassembled} value.
@ -87,5 +89,5 @@ public interface MutabilityPlan<T> extends Serializable {
*
* @see #disassemble
*/
T assemble(Serializable cached, SharedSessionContract session);
@Nullable T assemble(@Nullable Serializable cached, SharedSessionContract session);
}

View File

@ -30,15 +30,25 @@ public class ObjectArrayJavaType extends AbstractClassJavaType<Object[]> {
public String toString(Object[] value) {
final StringBuilder sb = new StringBuilder();
sb.append( '(' );
sb.append( components[0].toString( value[0] ) );
append( sb, components, value, 0 );
for ( int i = 1; i < components.length; i++ ) {
sb.append( ", " );
sb.append( components[i].toString( value[i] ) );
append( sb, components, value, i );
}
sb.append( ')' );
return sb.toString();
}
private void append(StringBuilder sb, JavaType[] components, Object[] value, int i) {
final Object o = value[i];
if (o == null ) {
sb.append( "null" );
}
else {
sb.append( components[i].toString( o ) );
}
}
@Override
public boolean areEqual(Object[] one, Object[] another) {
if ( one == another ) {

View File

@ -160,7 +160,7 @@ public class BasicCollectionJavaType<C extends Collection<E>, E> extends Abstrac
sb.append( '[' );
do {
final E element = iterator.next();
sb.append( componentJavaType.toString( element ) );
sb.append( componentJavaType.extractLoggableRepresentation( element ) );
if ( !iterator.hasNext() ) {
return sb.append( ']' ).toString();
}

View File

@ -24,6 +24,7 @@ import org.hibernate.type.spi.TypeConfiguration;
* or {@link org.hibernate.type.SqlTypes#SQLXML} mapped types.
*
* @author Christian Beikov
* @author Yanming Zhou
*/
@Incubating
public abstract class FormatMapperBasedJavaType<T> extends AbstractJavaType<T> implements MutabilityPlan<T> {
@ -107,16 +108,16 @@ public abstract class FormatMapperBasedJavaType<T> extends AbstractJavaType<T> i
@Override
public T deepCopy(T value) {
return fromString( toString( value ) );
return value == null ? null : fromString( toString( value ) );
}
@Override
public Serializable disassemble(T value, SharedSessionContract session) {
return toString( value );
return value == null ? null : toString( value );
}
@Override
public T assemble(Serializable cached, SharedSessionContract session) {
return fromString( (CharSequence) cached );
return cached == null ? null : fromString( (CharSequence) cached );
}
}

View File

@ -16,7 +16,9 @@ import org.hibernate.sql.ast.spi.SqlAppender;
* '{@code ? format json}' write expression for H2.
*
* @author Marco Belladelli
* @deprecated Use {@link org.hibernate.dialect.H2JsonJdbcType} instead
*/
@Deprecated(forRemoval = true, since = "6.6")
public class H2FormatJsonJdbcType extends JsonJdbcType {
/**
* Singleton access

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.type.format;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import java.lang.reflect.Type;
/**
* @author Yanming Zhou
*/
public abstract class AbstractJsonFormatMapper implements FormatMapper {
@SuppressWarnings("unchecked")
@Override
public final <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
final Type type = javaType.getJavaType();
if ( type == String.class || type == Object.class ) {
return (T) charSequence.toString();
}
return fromString( charSequence, type );
}
@Override
public final <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
final Type type = javaType.getJavaType();
if ( type == String.class || type == Object.class ) {
return (String) value;
}
return toString( value, type );
}
protected abstract <T> T fromString(CharSequence charSequence, Type type);
protected abstract <T> String toString(T value, Type type);
}

View File

@ -6,17 +6,18 @@
*/
package org.hibernate.type.format.jackson;
import org.hibernate.type.format.FormatMapper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.format.AbstractJsonFormatMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.Type;
/**
* @author Christian Beikov
* @author Yanming Zhou
*/
public final class JacksonJsonFormatMapper implements FormatMapper {
public final class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
public static final String SHORT_NAME = "jackson";
@ -31,29 +32,22 @@ public final class JacksonJsonFormatMapper implements FormatMapper {
}
@Override
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
return (T) charSequence.toString();
}
public <T> T fromString(CharSequence charSequence, Type type) {
try {
return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( javaType.getJavaType() ) );
return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( type ) );
}
catch (JsonProcessingException e) {
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
throw new IllegalArgumentException( "Could not deserialize string to java type: " + type, e );
}
}
@Override
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
return (String) value;
}
public <T> String toString(T value, Type type) {
try {
return objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) )
.writeValueAsString( value );
return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
}
catch (JsonProcessingException e) {
throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
throw new IllegalArgumentException( "Could not serialize object of java type: " + type, e );
}
}
}

View File

@ -6,18 +6,19 @@
*/
package org.hibernate.type.format.jakartajson;
import org.hibernate.type.format.FormatMapper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.format.AbstractJsonFormatMapper;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.JsonbException;
import java.lang.reflect.Type;
/**
* @author Christian Beikov
* @author Yanming Zhou
*/
public final class JsonBJsonFormatMapper implements FormatMapper {
public final class JsonBJsonFormatMapper extends AbstractJsonFormatMapper {
public static final String SHORT_NAME = "jsonb";
@ -32,28 +33,22 @@ public final class JsonBJsonFormatMapper implements FormatMapper {
}
@Override
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
return (T) charSequence.toString();
}
public <T> T fromString(CharSequence charSequence, Type type) {
try {
return jsonb.fromJson( charSequence.toString(), javaType.getJavaType() );
return jsonb.fromJson( charSequence.toString(), type );
}
catch (JsonbException e) {
throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
throw new IllegalArgumentException( "Could not deserialize string to java type: " + type, e );
}
}
@Override
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
return (String) value;
}
public <T> String toString(T value, Type type) {
try {
return jsonb.toJson( value, javaType.getJavaType() );
return jsonb.toJson( value, type );
}
catch (JsonbException e) {
throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
throw new IllegalArgumentException( "Could not serialize object of java type: " + type, e );
}
}
}

View File

@ -12,16 +12,20 @@ import java.sql.Clob;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.json.JsonValue;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.community.dialect.AltibaseDialect;
import org.hibernate.dialect.AbstractHANADialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
@ -47,9 +51,11 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isOneOf;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
/**
* @author Christian Beikov
* @author Yanming Zhou
*/
@DomainModel(annotatedClasses = JsonMappingTests.EntityWithJson.class)
@SessionFactory
@ -140,6 +146,29 @@ public abstract class JsonMappingTests {
assertThat( entityWithJson.stringMap, is( stringMap ) );
assertThat( entityWithJson.objectMap, is( objectMap ) );
assertThat( entityWithJson.list, is( list ) );
assertThat( entityWithJson.jsonNode, is( nullValue() ));
assertThat( entityWithJson.jsonValue, is( nullValue() ));
}
);
}
@Test
public void verifyMergeWorks(SessionFactoryScope scope) {
scope.inTransaction(
(session) -> {
session.merge( new EntityWithJson( 2, null, null, null, null ) );
}
);
scope.inTransaction(
(session) -> {
EntityWithJson entityWithJson = session.find( EntityWithJson.class, 2 );
assertThat( entityWithJson.stringMap, is( nullValue() ) );
assertThat( entityWithJson.objectMap, is( nullValue() ) );
assertThat( entityWithJson.list, is( nullValue() ) );
assertThat( entityWithJson.jsonString, is( nullValue() ) );
assertThat( entityWithJson.jsonNode, is( nullValue() ));
assertThat( entityWithJson.jsonValue, is( nullValue() ));
}
);
}
@ -234,6 +263,12 @@ public abstract class JsonMappingTests {
@JdbcTypeCode( SqlTypes.JSON )
private String jsonString;
@JdbcTypeCode( SqlTypes.JSON )
private JsonNode jsonNode;
@JdbcTypeCode( SqlTypes.JSON )
private JsonValue jsonValue;
public EntityWithJson() {
}