HHH-17840 Fix inconsistency of read/write null JsonNode/JsonValue
This commit is contained in:
parent
c972208fbc
commit
1d87db4e4f
|
@ -69,7 +69,6 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2
|
|||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.TimeAsTimestampWithTimeZoneJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
|
||||
|
@ -287,7 +286,7 @@ public class H2LegacyDialect extends Dialect {
|
|||
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2FormatJsonJdbcType.INSTANCE );
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2JsonJdbcType.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,8 +70,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
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
|||
import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||
import org.hibernate.type.descriptor.jdbc.H2FormatJsonJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsInstantJdbcType;
|
||||
|
@ -235,7 +234,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 );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -290,7 +290,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() {
|
||||
|
|
|
@ -143,7 +143,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",
|
||||
|
@ -162,7 +162,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",
|
||||
|
@ -182,7 +182,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
|
||||
|
@ -218,9 +218,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?
|
||||
|
@ -233,7 +233,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?
|
||||
|
@ -247,7 +247,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;
|
||||
|
||||
/**
|
||||
|
@ -266,8 +266,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;
|
||||
|
@ -289,7 +289,7 @@ public interface Type extends Serializable {
|
|||
*/
|
||||
void nullSafeSet(
|
||||
PreparedStatement st,
|
||||
Object value,
|
||||
@Nullable Object value,
|
||||
int index,
|
||||
boolean[] settable,
|
||||
SharedSessionContractImplementor session)
|
||||
|
@ -309,7 +309,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;
|
||||
|
||||
/**
|
||||
|
@ -342,7 +342,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;
|
||||
|
||||
/**
|
||||
|
@ -381,7 +381,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 );
|
||||
}
|
||||
|
||||
|
@ -399,7 +399,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
|
||||
|
@ -413,7 +413,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
|
||||
|
@ -421,7 +421,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);
|
||||
|
||||
/**
|
||||
|
@ -441,9 +443,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;
|
||||
|
@ -466,9 +468,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,
|
||||
|
@ -483,5 +485,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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -154,7 +154,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();
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.5")
|
||||
public class H2FormatJsonJdbcType extends JsonJdbcType {
|
||||
/**
|
||||
* Singleton access
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue