Skip to content

HHH-19362 JsonHelper improvements to handle more mapping types #10481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,17 @@
import org.hibernate.dialect.type.OracleJsonArrayJdbcType;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JsonHelper;
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JsonGeneratingVisitor;
import org.hibernate.type.format.OsonDocumentReader;
import org.hibernate.type.format.OsonDocumentWriter;

Expand Down Expand Up @@ -78,20 +76,7 @@ private <T> byte[] toOsonStream(T value, JavaType<T> javaType, WrapperOptions op
}
else {
final OsonDocumentWriter writer = new OsonDocumentWriter( generator );
if ( getElementJdbcType() instanceof JsonJdbcType jsonElementJdbcType ) {
final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
JsonHelper.serializeArray( embeddableMappingType, domainObjects, options, writer );
}
else {
assert !(getElementJdbcType() instanceof AggregateJdbcType);
JsonHelper.serializeArray(
elementJavaType,
getElementJdbcType(),
domainObjects,
options,
writer
);
}
JsonGeneratingVisitor.INSTANCE.visitArray( elementJavaType, getElementJdbcType(), domainObjects, options, writer );
}
}
return out.toByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
import org.hibernate.type.descriptor.jdbc.JsonHelper;
import org.hibernate.type.descriptor.jdbc.spi.JsonGeneratingVisitor;
import org.hibernate.type.format.OsonDocumentReader;
import org.hibernate.type.format.OsonDocumentWriter;

Expand Down Expand Up @@ -79,7 +80,7 @@ private <T> byte[] toOson(T value, JavaType<T> javaType, WrapperOptions options)
if ( getEmbeddableMappingType() != null ) {
// OracleJsonFactory is used and not OracleOsonFactory as Jackson is not involved here
try (OracleJsonGenerator generator = OSON_JSON_FACTORY.createJsonBinaryGenerator( out )) {
JsonHelper.serialize(
JsonGeneratingVisitor.INSTANCE.visit(
getEmbeddableMappingType(),
value,
options,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import org.hibernate.type.descriptor.jdbc.StructuredJdbcType;
import org.hibernate.type.spi.TypeConfiguration;

import static org.hibernate.type.descriptor.jdbc.StructHelper.getEmbeddedPart;
import static org.hibernate.type.descriptor.jdbc.StructHelper.getSubPart;
import static org.hibernate.type.descriptor.jdbc.StructHelper.instantiate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
Expand Down Expand Up @@ -999,7 +999,7 @@ private SelectableMapping getJdbcValueSelectable(int jdbcValueSelectableIndex) {
final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
int count = 0;
for ( int i = 0; i < size; i++ ) {
final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, orderMapping[i] );
final ValuedModelPart modelPart = getSubPart( embeddableMappingType, orderMapping[i] );
if ( modelPart.getMappedType() instanceof EmbeddableMappingType embeddableMappingType ) {
final SelectableMapping aggregateMapping = embeddableMappingType.getAggregateMapping();
if ( aggregateMapping == null ) {
Expand Down Expand Up @@ -1378,7 +1378,7 @@ private StructAttributeValues getAttributeValues(
attributeIndex = orderMapping[i];
}
jdbcIndex += injectAttributeValue(
getEmbeddedPart( embeddableMappingType, attributeIndex ),
getSubPart( embeddableMappingType, attributeIndex ),
attributeValues,
attributeIndex,
rawJdbcValues,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
import java.sql.ResultSet;
import java.sql.SQLException;

import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
import org.hibernate.type.descriptor.jdbc.spi.JsonGeneratingVisitor;
import org.hibernate.type.format.StringJsonDocumentWriter;

/**
Expand Down Expand Up @@ -74,17 +74,9 @@ protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions opti
return options.getJsonFormatMapper().toString( value, javaType, options);
}
else {
final JdbcType elementJdbcType = getElementJdbcType();
final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
final StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) {
final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
JsonHelper.serializeArray( embeddableMappingType, domainObjects, options, writer );
}
else {
assert !(elementJdbcType instanceof AggregateJdbcType);
JsonHelper.serializeArray( elementJavaType, elementJdbcType, domainObjects, options, writer );
}
JsonGeneratingVisitor.INSTANCE.visitArray( elementJavaType, getElementJdbcType(), domainObjects, options, writer );
return writer.getJson();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
package org.hibernate.type.descriptor.jdbc;


import java.io.IOException;
import java.lang.reflect.Array;
import java.sql.SQLException;
import java.util.AbstractCollection;
import java.util.ArrayList;
Expand All @@ -24,20 +22,14 @@
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.format.JsonDocumentItemType;
import org.hibernate.type.format.JsonDocumentReader;
import org.hibernate.type.format.JsonDocumentWriter;
import static org.hibernate.type.descriptor.jdbc.StructHelper.getEmbeddedPart;

import static org.hibernate.type.descriptor.jdbc.StructHelper.instantiate;
import org.hibernate.type.format.JsonValueJDBCTypeAdapter;
import org.hibernate.type.format.JsonValueJDBCTypeAdapterFactory;
Expand All @@ -52,182 +44,14 @@
@Internal
public class JsonHelper {

/**
* Serializes an array of values into JSON object/array
* @param elementMappingType the type definitions
* @param values the values to be serialized
* @param options wrapping options
* @param writer the document writer used for serialization
*/
public static void serializeArray(MappingType elementMappingType, Object[] values, WrapperOptions options, JsonDocumentWriter writer) {
writer.startArray();
if ( values.length == 0 ) {
writer.endArray();
return;
}
for ( Object value : values ) {
try {
serialize(elementMappingType, value, options, writer);
}
catch (IOException e) {
throw new IllegalArgumentException( "Could not serialize JSON array value" , e );
}
}
writer.endArray();
}

/**
* Serializes an array of values into JSON object/array
* @param elementJavaType the array element type
* @param elementJdbcType the JDBC type
* @param values values to be serialized
* @param options wrapping options
* @param writer the document writer used for serialization
*/
public static void serializeArray(JavaType<?> elementJavaType, JdbcType elementJdbcType, Object[] values, WrapperOptions options, JsonDocumentWriter writer) {
writer.startArray();
if ( values.length == 0 ) {
writer.endArray();
return;
}
for ( Object value : values ) {
if (value == null) {
writer.nullValue();
}
else {
writer.serializeJsonValue( value ,(JavaType<?>) elementJavaType,elementJdbcType,options);
}
}
writer.endArray();
}

/**
* Checks that a <code>JDBCType</code> is assignable to an array
* @param type the jdbc type
* @return <code>true</code> if types is of array kind <code>false</code> otherwise.
*/
private static boolean isArrayType(JdbcType type) {
return (type.getDefaultSqlTypeCode() == SqlTypes.ARRAY ||
type.getDefaultSqlTypeCode() == SqlTypes.JSON_ARRAY);
}

/**
* Serialized an Object value to JSON object using a document writer.
*
* @param embeddableMappingType the embeddable mapping definition of the given value.
* @param domainValue the value to be serialized.
* @param options wrapping options
* @param writer the document writer
* @throws IOException if the underlying writer failed to serialize a mpped value or failed to perform need I/O.
*/
public static void serialize(EmbeddableMappingType embeddableMappingType,
Object domainValue, WrapperOptions options, JsonDocumentWriter writer) throws IOException {
writer.startObject();
serializeMapping(embeddableMappingType, domainValue, options, writer);
writer.endObject();
}

private static void serialize(MappingType mappedType, Object value, WrapperOptions options, JsonDocumentWriter writer)
throws IOException {
if ( value == null ) {
writer.nullValue();
}
else if ( mappedType instanceof EmbeddableMappingType ) {
serialize( (EmbeddableMappingType) mappedType, value, options, writer );
}
else if ( mappedType instanceof BasicType<?> basicType) {
if ( isArrayType(basicType.getJdbcType())) {
final int length = Array.getLength( value );
writer.startArray();
if ( length != 0 ) {
final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) basicType.getJdbcJavaType() ).getElementJavaType();
final JdbcType elementJdbcType = ( (ArrayJdbcType) basicType.getJdbcType() ).getElementJdbcType();
final Object domainArray = basicType.convertToRelationalValue( value );
for ( int j = 0; j < length; j++ ) {
writer.serializeJsonValue(Array.get(domainArray,j), elementJavaType, elementJdbcType, options);
}
}
writer.endArray();
}
else {
writer.serializeJsonValue(basicType.convertToRelationalValue( value),
(JavaType<Object>)basicType.getJdbcJavaType(),basicType.getJdbcType(), options);
}
}
else {
throw new UnsupportedOperationException( "Support for mapping type not yet implemented: " + mappedType.getClass().getName() );
}
}

/**
* JSON object attirbute serialization
* @see #serialize(EmbeddableMappingType, Object, WrapperOptions, JsonDocumentWriter)
* @param embeddableMappingType the embeddable mapping definition of the given value.
* @param domainValue the value to be serialized.
* @param options wrapping options
* @param writer the document writer
* @throws IOException if an error occurred while writing to an underlying writer
*/
private static void serializeMapping(EmbeddableMappingType embeddableMappingType,
Object domainValue, WrapperOptions options, JsonDocumentWriter writer) throws IOException {
final Object[] values = embeddableMappingType.getValues( domainValue );
for ( int i = 0; i < values.length; i++ ) {
final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
if ( attributeMapping instanceof SelectableMapping ) {
final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
writer.objectKey( name );

if ( attributeMapping.getMappedType() instanceof EmbeddableMappingType ) {
writer.startObject();
serializeMapping( (EmbeddableMappingType)attributeMapping.getMappedType(), values[i], options,writer);
writer.endObject();
}
else {
serialize(attributeMapping.getMappedType(), values[i], options, writer);
}

}
else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
if ( values[i] == null ) {
continue;
}
final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
if (aggregateMapping == null) {
serializeMapping(
mappingType,
values[i],
options,
writer );
}
else {
final String name = aggregateMapping.getSelectableName();
writer.objectKey( name );
writer.startObject();
serializeMapping(
mappingType,
values[i],
options,
writer);
writer.endObject();

}
}
else {
throw new UnsupportedOperationException( "Support for attribute mapping type not yet implemented: " + attributeMapping.getClass().getName() );
}

}
}

/**
* Consumes Json document items from a document reader and return the serialized Objects
* @param reader the document reader
* @param embeddableMappingType the type definitions
* @param returnEmbeddable do we return an Embeddable object or array of Objects ?
* @param options wrapping options
* @return serialized values
* @param <X>
* @param <X> the type of the returned value
* @throws SQLException if error occured during mapping of types
*/
private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, EmbeddableMappingType embeddableMappingType, boolean returnEmbeddable, WrapperOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.spi.JsonGeneratingVisitor;
import org.hibernate.type.format.StringJsonDocumentReader;
import org.hibernate.type.format.StringJsonDocumentWriter;

Expand Down Expand Up @@ -92,7 +93,7 @@ public Object createJdbcValue(Object domainValue, WrapperOptions options) throws
assert embeddableMappingType != null;
final StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
try {
JsonHelper.serialize( embeddableMappingType, domainValue, options, writer );
JsonGeneratingVisitor.INSTANCE.visit( embeddableMappingType, domainValue, options, writer );
return writer.getJson();
}
catch (IOException e) {
Expand All @@ -110,7 +111,7 @@ protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions opti
if ( embeddableMappingType != null ) {
try {
final StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
JsonHelper.serialize( embeddableMappingType, value, options, writer );
JsonGeneratingVisitor.INSTANCE.visit( embeddableMappingType, value, options, writer );
return writer.getJson();
}
catch (IOException e) {
Expand Down
Loading