From 601bdc6a52b8a83d7ad07923c5d08633ed7b183b Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Fri, 13 Dec 2024 01:45:50 +0100
Subject: [PATCH 01/81] HHH-17404 : Support reading/writing from/to special
 source/target in FormatMapper

---
 .../dialect/OracleLegacyDialect.java          |   2 +-
 .../java/org/hibernate/dialect/Dialect.java   |  13 +
 .../org/hibernate/dialect/OracleDialect.java  |  46 +++-
 .../dialect/OracleDurationJdbcType.java       |  28 +++
 .../OracleOsonArrayJdbcTypeConstructor.java   |  43 ++++
 .../OracleOsonJacksonArrayJdbcType.java       | 201 +++++++++++++++
 .../dialect/OracleOsonJacksonJdbcType.java    | 232 ++++++++++++++++++
 .../aggregate/OracleAggregateSupport.java     |  89 ++++---
 .../dialect/type/OracleJsonJdbcType.java      |   2 +-
 .../type/format/AbstractJsonFormatMapper.java |  21 ++
 .../hibernate/type/format/FormatMapper.java   |  20 ++
 .../format/jackson/JacksonIntegration.java    |  21 +-
 .../jackson/JacksonJsonFormatMapper.java      |  33 +++
 .../jackson/JacksonXmlFormatMapper.java       |  22 ++
 .../jakartajson/JsonBJsonFormatMapper.java    |   1 +
 .../type/format/jaxb/JaxbXmlFormatMapper.java |  22 ++
 16 files changed, 761 insertions(+), 35 deletions(-)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java

diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java
index 07e85788a6fa..2670fb217ba1 100644
--- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java
+++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java
@@ -992,7 +992,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this );
+		return OracleAggregateSupport.valueOf( this ,true);
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
index f566d03caf27..3988cfa22505 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
@@ -5473,6 +5473,19 @@ public Boolean supportsRefCursors() {
 		return null;
 	}
 
+
+	/**
+	 * Whether this Dialect supports jakarta Temporal annotation while
+	 * serialising or deserialising JSON
+	 *
+	 * @return {@code true} indicates it does; {@code false} indicates it does not
+	 *
+	 * @see org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData#supportsRefCursors
+	 */
+	public Boolean getSupportsJakartaTemporalAnnotationInEmbeddable() {
+		return Boolean.TRUE;
+	}
+
 	/**
 	 * Pluggable strategy for determining the {@link Size} to use for
 	 * columns of a given SQL type.
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index da62a303db34..10586d03cac4 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -104,6 +104,7 @@
 import org.hibernate.type.descriptor.sql.internal.NamedNativeEnumDdlTypeImpl;
 import org.hibernate.type.descriptor.sql.internal.NamedNativeOrdinalEnumDdlTypeImpl;
 import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
+import org.hibernate.type.format.jackson.JacksonIntegration;
 import org.hibernate.type.spi.TypeConfiguration;
 
 import java.sql.CallableStatement;
@@ -116,9 +117,11 @@
 import java.util.TimeZone;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import org.jboss.logging.Logger;
 
 import static java.util.regex.Pattern.CASE_INSENSITIVE;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS;
+import static org.hibernate.dialect.DialectLogging.DIALECT_MESSAGE_LOGGER;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getArrayJdbcTypeConstructor;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getNestedTableJdbcTypeConstructor;
 import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
@@ -192,6 +195,9 @@ public class OracleDialect extends Dialect {
 
 	private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 19 );
 
+	private static final String JACKSON_MAPPER_NAME = "jackson";
+	private static boolean OracleOsonExtensionUsed = false;
+
 	private final OracleUserDefinedTypeExporter userDefinedTypeExporter = new OracleUserDefinedTypeExporter( this );
 	private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this);
 	private final SequenceSupport oracleSequenceSupport = OracleSequenceSupport.getInstance(this);
@@ -1007,9 +1013,32 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 			);
 		}
 
+		final String mapperName = configurationService.getSetting( "hibernate.type.json_format_mapper",
+				StandardConverters.STRING,JACKSON_MAPPER_NAME);
+
 		if ( getVersion().isSameOrAfter( 21 ) ) {
-			typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
-			typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE );
+			if ( JacksonIntegration.isOracleOsonExtensionAvailable() && JACKSON_MAPPER_NAME.equalsIgnoreCase( mapperName )) {
+				// We must check that that extension is available and actually used.
+				typeContributions.contributeJdbcType( OracleOsonJacksonJdbcType.INSTANCE );
+				typeContributions.contributeJdbcTypeConstructor( OracleOsonArrayJdbcTypeConstructor.INSTANCE );
+				DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
+						"Oracle OSON Jackson extension used" );
+				// as we speak this is not supported by OSON extension
+				OracleOsonExtensionUsed = true;
+			}
+			else {
+				if (DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.isDebugEnabled()) {
+					DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
+							"Oracle OSON Jackson extension not used" );
+					DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
+							"JacksonIntegration.isOracleOsonExtensionAvailable(): " +
+									JacksonIntegration.isOracleOsonExtensionAvailable());
+					DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
+							"hibernate.type.json_format_mapper : " + mapperName);
+				}
+				typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
+				typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE );
+			}
 		}
 		else {
 			typeContributions.contributeJdbcType( OracleJsonBlobJdbcType.INSTANCE );
@@ -1028,6 +1057,8 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 		// Oracle requires a custom binder for binding untyped nulls with the NULL type
 		typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
 		typeContributions.contributeJdbcType( ObjectNullAsNullTypeJdbcType.INSTANCE );
+		// Oracle Stores the duration is ISO-8601 format.
+		typeContributions.contributeJdbcType( OracleDurationJdbcType.INSTANCE );
 
 		// Until we remove StandardBasicTypes, we have to keep this
 		typeContributions.contributeType(
@@ -1053,10 +1084,19 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 			jdbcTypeRegistry.addDescriptor( OracleOrdinalEnumJdbcType.INSTANCE );
 		}
 	}
+	@Override
+	public Boolean getSupportsJakartaTemporalAnnotationInEmbeddable() {
+		return OracleOsonExtensionUsed == false;
+	}
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this );
+		if (OracleOsonExtensionUsed) {
+			return OracleAggregateSupport.valueOf( this, false );
+		}
+		else {
+			return OracleAggregateSupport.valueOf( this, true );
+		}
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
new file mode 100644
index 000000000000..dfbf38ab4140
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -0,0 +1,28 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.dialect;
+
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
+
+import java.sql.Types;
+
+public class OracleDurationJdbcType extends VarcharJdbcType {
+
+	public static final OracleDurationJdbcType INSTANCE = new OracleDurationJdbcType();
+
+	public OracleDurationJdbcType() {
+	}
+
+	@Override
+	public int getDdlTypeCode() {
+		return Types.VARCHAR;
+	}
+
+	@Override
+	public int getDefaultSqlTypeCode() {
+		return SqlTypes.DURATION;
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
new file mode 100644
index 000000000000..b91c6a86f931
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
@@ -0,0 +1,43 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.dialect;
+
+import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
+import org.hibernate.type.BasicType;
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
+import org.hibernate.type.spi.TypeConfiguration;
+
+/**
+ * Factory for {@link OracleOsonJacksonArrayJdbcType}.
+ * @author Emmanuel Jannetti
+ */
+public class OracleOsonArrayJdbcTypeConstructor implements JdbcTypeConstructor {
+	public static final JdbcTypeConstructor INSTANCE = new OracleOsonArrayJdbcTypeConstructor();
+
+	@Override
+	public JdbcType resolveType(
+			TypeConfiguration typeConfiguration,
+			Dialect dialect,
+			BasicType<?> elementType,
+			ColumnTypeInformation columnTypeInformation) {
+		return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
+	}
+
+	@Override
+	public JdbcType resolveType(
+			TypeConfiguration typeConfiguration,
+			Dialect dialect,
+			JdbcType elementType,
+			ColumnTypeInformation columnTypeInformation) {
+		return new OracleOsonJacksonArrayJdbcType( elementType );
+	}
+
+	@Override
+	public int getDefaultSqlTypeCode() {
+		return SqlTypes.JSON_ARRAY;
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
new file mode 100644
index 000000000000..1bf468b37ab4
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -0,0 +1,201 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.dialect;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import oracle.sql.json.OracleJsonDatum;
+import org.hibernate.type.descriptor.ValueBinder;
+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.BasicBinder;
+import org.hibernate.type.descriptor.jdbc.BasicExtractor;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
+
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * @author Emmanuel Jannetti
+ */
+public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
+	/**
+	 * Singleton access
+	 */
+	//public static final OracleOsonJacksonArrayJdbcType INSTANCE = new OracleOsonJacksonArrayJdbcType();
+
+	private static Method jacksonOsonObjectMapperGetter = null;
+
+	static {
+		try {
+			Class jacksonOsonConverter = OracleOsonJacksonJdbcType.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.JacksonOsonConverter" );
+			jacksonOsonObjectMapperGetter = jacksonOsonConverter.getMethod( "getObjectMapper" );
+		}
+		catch (ClassNotFoundException | LinkageError | NoSuchMethodException e) {
+			// should not happen as OracleOsonJacksonJdbcType is loaded
+			// only when Oracle OSON JDBC extension is present
+			// see OracleDialect class.
+			throw new ExceptionInInitializerError( "OracleOsonJacksonArrayJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
+		}
+	}
+
+	public OracleOsonJacksonArrayJdbcType(JdbcType elementJdbcType) {
+		super(elementJdbcType);
+	}
+
+
+	@Override
+	public String toString() {
+		return "OracleOsonJacksonArrayJdbcType";
+	}
+
+	@Override
+	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
+
+		final ObjectMapper objectMapper;
+		try {
+			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
+		}
+		catch (IllegalAccessException | InvocationTargetException e) {
+			// should not happen
+			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
+		}
+
+		return new BasicBinder<>( javaType, this ) {
+
+			private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+				PipedOutputStream out = new PipedOutputStream();
+				PipedInputStream in = new PipedInputStream(out);
+				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
+				mapper.writeToTarget( value, javaType, osonGen, options );
+				osonGen.close();
+				return in;
+			}
+			@Override
+			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
+					throws SQLException {
+				try {
+					st.setBinaryStream( index, toOson( value, getJavaType(), options ) );
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+
+			@Override
+			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
+					throws SQLException {
+				try {
+					st.setBinaryStream( name, toOson( value, getJavaType(), options ) );
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+		};
+	}
+
+	@Override
+	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
+
+		final ObjectMapper objectMapper;
+		try {
+			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
+		}
+		catch (IllegalAccessException | InvocationTargetException e) {
+			// should not happen
+			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
+		}
+
+		return new BasicExtractor<>( javaType, this ) {
+
+			private X fromOson(byte[] osonBytes, FormatMapper mapper, WrapperOptions options) throws Exception {
+				return mapper.readFromSource(  getJavaType(), osonBytes, options);
+			}
+
+			@Override
+			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+				if ( ojd == null ) {
+					return null;
+				}
+				byte[] osonBytes = ojd.shareBytes();
+
+				try {
+					return fromOson( osonBytes, mapper ,options);
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+
+			@Override
+			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
+				if ( ojd == null ) {
+					return null;
+				}
+				byte[] osonBytes = ojd.shareBytes();
+				try {
+					return fromOson( osonBytes, mapper ,options);
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+
+			@Override
+			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
+					throws SQLException {
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+
+				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
+				if ( ojd == null ) {
+					return null;
+				}
+				byte[] osonBytes = ojd.shareBytes();
+				try {
+					return fromOson( osonBytes, mapper ,options);
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+		};
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
new file mode 100644
index 000000000000..7dc9f047560d
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -0,0 +1,232 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.dialect;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import oracle.sql.json.OracleJsonDatum;
+import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
+import org.hibernate.type.descriptor.ValueBinder;
+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.AggregateJdbcType;
+import org.hibernate.type.descriptor.jdbc.BasicBinder;
+import org.hibernate.type.descriptor.jdbc.BasicExtractor;
+import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
+
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * @author Emmanuel Jannetti
+ */
+public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
+	public static final OracleOsonJacksonJdbcType INSTANCE = new OracleOsonJacksonJdbcType( null );
+	private static Method jacksonOsonObjectMapperGetter = null;
+
+	static {
+		try {
+			Class jacksonOsonConverter = OracleOsonJacksonJdbcType.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.JacksonOsonConverter" );
+			jacksonOsonObjectMapperGetter = jacksonOsonConverter.getMethod( "getObjectMapper" );
+		}
+		catch (ClassNotFoundException | LinkageError | NoSuchMethodException e) {
+			// should not happen as OracleOsonJacksonJdbcType is loaded
+			// only when Oracle OSON JDBC extension is present
+			// see OracleDialect class.
+			throw new ExceptionInInitializerError( "OracleOsonJacksonJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
+		}
+	}
+	private OracleOsonJacksonJdbcType(EmbeddableMappingType embeddableMappingType) {
+		super( embeddableMappingType );
+	}
+
+	@Override
+	public String toString() {
+		return "OracleOsonJacksonJdbcType";
+	}
+
+	@Override
+	public AggregateJdbcType resolveAggregateJdbcType(
+			EmbeddableMappingType mappingType,
+			String sqlType,
+			RuntimeModelCreationContext creationContext) {
+		return new OracleOsonJacksonJdbcType( mappingType );
+	}
+
+	@Override
+	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
+
+		final ObjectMapper objectMapper;
+		try {
+			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
+		}
+		catch (IllegalAccessException | InvocationTargetException e) {
+			// should not happen
+			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
+		}
+
+		return new BasicBinder<>( javaType, this ) {
+
+			private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
+
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+				PipedOutputStream out = new PipedOutputStream();
+				PipedInputStream in = new PipedInputStream(out);
+				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
+				mapper.writeToTarget( value, javaType, osonGen, options );
+				osonGen.close();
+				return in;
+			}
+
+			@Override
+			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
+					throws SQLException {
+				try {
+					st.setBinaryStream( index, toOson( value, getJavaType(), options ) );
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+
+			@Override
+			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
+					throws SQLException {
+				try {
+					st.setBinaryStream( name, toOson( value, getJavaType(), options ) );
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+		};
+	}
+
+	@Override
+	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
+
+		final ObjectMapper objectMapper;
+		try {
+			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
+		}
+		catch (IllegalAccessException | InvocationTargetException e) {
+			// should not happen
+			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
+		}
+
+		return new BasicExtractor<>( javaType, this ) {
+
+			private X fromOson(byte[] osonBytes, FormatMapper mapper, WrapperOptions options) throws Exception {
+
+				if (getEmbeddableMappingType() != null &&
+						getJavaType().getJavaTypeClass() == Object[].class) {
+					// We are dealing with embeddable (@Embeddable) and we request
+					// an array of objects. We use JsonParser to fetch values
+					// and build the array.(as opposed to let Jackson do it as we do not
+					// have proper obejct definition at that stage).
+
+				}
+
+				JavaType <X> type = getJavaType();
+				if (getEmbeddableMappingType() != null) {
+					// We are dealing with embeddable (@Embeddable)
+					type = (JavaType<X>) getEmbeddableMappingType().getJavaType();
+				}
+				return mapper.readFromSource( type, osonBytes, options );
+			}
+
+			@Override
+			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
+
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+				if ( ojd == null ) {
+					return null;
+				}
+				byte[] osonBytes = ojd.shareBytes();
+
+
+
+				try {
+					return fromOson( osonBytes, mapper ,options);
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+
+			@Override
+			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
+
+
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+
+				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
+				if ( ojd == null ) {
+					return null;
+				}
+				byte[] osonBytes = ojd.shareBytes();
+
+				try {
+					return fromOson( osonBytes, mapper ,options);
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+
+			@Override
+			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
+					throws SQLException {
+
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
+				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
+				if ( ojd == null ) {
+					return null;
+				}
+				byte[] osonBytes = ojd.shareBytes();
+				try {
+					return fromOson( osonBytes, mapper ,options);
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
+
+		};
+	}
+
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index 8aac39ab3bf1..f69cc49ba888 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -49,12 +49,14 @@
 
 public class OracleAggregateSupport extends AggregateSupportImpl {
 
-	private static final AggregateSupport V23_INSTANCE = new OracleAggregateSupport( true, JsonSupport.OSON );
-	private static final AggregateSupport V21_INSTANCE = new OracleAggregateSupport( false, JsonSupport.OSON );
-	private static final AggregateSupport V19_INSTANCE = new OracleAggregateSupport( false, JsonSupport.MERGEPATCH );
-	private static final AggregateSupport V18_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY_AND_PATH );
-	private static final AggregateSupport V12_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY );
-	private static final AggregateSupport LEGACY_INSTANCE = new OracleAggregateSupport( false, JsonSupport.NONE );
+	protected static final AggregateSupport V23_INSTANCE = new OracleAggregateSupport( true, JsonSupport.OSON, true );
+	// Special instance used when an Oracle OSON extension is available and used
+	protected static final AggregateSupport V23_OSON_EXT_INSTANCE = new OracleAggregateSupport( true, JsonSupport.OSON,false);
+	protected static final AggregateSupport V21_INSTANCE = new OracleAggregateSupport( false, JsonSupport.OSON, true );
+	protected static final AggregateSupport V19_INSTANCE = new OracleAggregateSupport( false, JsonSupport.MERGEPATCH , true);
+	protected static final AggregateSupport V18_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY_AND_PATH, true );
+	protected static final AggregateSupport V12_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY , true);
+	protected static final AggregateSupport LEGACY_INSTANCE = new OracleAggregateSupport( false, JsonSupport.NONE , true);
 
 	private static final String JSON_QUERY_START = "json_query(";
 	private static final String JSON_QUERY_JSON_END = "' returning json)";
@@ -68,13 +70,15 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
 
 	private final boolean checkConstraintSupport;
 	private final JsonSupport jsonSupport;
+	private final boolean dateTypesStoreAsString;
 
-	private OracleAggregateSupport(boolean checkConstraintSupport, JsonSupport jsonSupport) {
+	OracleAggregateSupport(boolean checkConstraintSupport, JsonSupport jsonSupport, boolean dateTypesStoreAsString) {
 		this.checkConstraintSupport = checkConstraintSupport;
 		this.jsonSupport = jsonSupport;
+		this.dateTypesStoreAsString = dateTypesStoreAsString;
 	}
 
-	public static AggregateSupport valueOf(Dialect dialect) {
+	public static AggregateSupport valueOf(Dialect dialect, boolean useDateStoredAsString) {
 		final DatabaseVersion version = dialect.getVersion();
 		return switch ( version.getMajor() ) {
 			case 12, 13, 14, 15, 16, 17 -> V12_INSTANCE;
@@ -82,8 +86,9 @@ public static AggregateSupport valueOf(Dialect dialect) {
 			case 19, 20 -> V19_INSTANCE;
 			case 21, 22 -> V21_INSTANCE;
 			default -> version.isSameOrAfter( 23 )
-					? OracleAggregateSupport.V23_INSTANCE
-					: OracleAggregateSupport.LEGACY_INSTANCE;
+				? useDateStoredAsString?OracleAggregateSupport.V23_INSTANCE:
+				OracleAggregateSupport.V23_OSON_EXT_INSTANCE
+				: OracleAggregateSupport.LEGACY_INSTANCE;
 		};
 	}
 
@@ -139,31 +144,54 @@ public String aggregateComponentCustomReadExpression(
 										"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
 								);
 							case DATE:
-								return template.replace(
-										placeholder,
-										"to_date(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD')"
-								);
+								if (this.dateTypesStoreAsString) {
+									return template.replace(
+											placeholder,
+											"to_date(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD')"
+									);
+								}
+								else {
+									// Oracle OSON extension is used, value is not stored as string
+									return template.replace(
+											placeholder,
+											"json_value(" + parentPartExpression + columnExpression + "' returning date)"
+									);
+								}
+
 							case TIME:
 								return template.replace(
 										placeholder,
 										"to_timestamp(json_value(" + parentPartExpression + columnExpression + "'),'hh24:mi:ss')"
 								);
 							case TIMESTAMP:
-								return template.replace(
-										placeholder,
-										"to_timestamp(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9')"
-								);
+								if (this.dateTypesStoreAsString) {
+									return template.replace(
+											placeholder,
+											"to_timestamp(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9')"
+									);
+								}
+								else {
+
+									return template.replace(
+											placeholder,
+											"json_value(" + parentPartExpression + columnExpression + "' returning timestamp)"
+									);
+								}
 							case TIMESTAMP_WITH_TIMEZONE:
 							case TIMESTAMP_UTC:
-								return template.replace(
-										placeholder,
-										"to_timestamp_tz(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9TZH:TZM')"
-								);
-							case UUID:
-								return template.replace(
-										placeholder,
-										"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
-								);
+								if (this.dateTypesStoreAsString) {
+									return template.replace(
+											placeholder,
+											"to_timestamp_tz(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9TZH:TZM')"
+									);
+								}
+								else {
+									// Oracle OSON extension is used, value is not stored as string
+									return template.replace(
+											placeholder,
+											"json_value(" + parentPartExpression + columnExpression + "')"
+									);
+								}
 							case BINARY:
 							case VARBINARY:
 							case LONG32VARBINARY:
@@ -187,8 +215,13 @@ public String aggregateComponentCustomReadExpression(
 								final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) column.getJdbcMapping();
 								final OracleArrayJdbcType jdbcType = (OracleArrayJdbcType) pluralType.getJdbcType();
 								switch ( jdbcType.getElementJdbcType().getDefaultSqlTypeCode() ) {
-									case BOOLEAN:
+
 									case DATE:
+										return template.replace(
+												placeholder,
+												"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
+										);
+									case BOOLEAN:
 									case TIME:
 									case TIMESTAMP:
 									case TIMESTAMP_WITH_TIMEZONE:
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java
index 1d1c3055d190..36bc1ed331a0 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java
@@ -24,7 +24,7 @@ public class OracleJsonJdbcType extends OracleJsonBlobJdbcType {
 	 */
 	public static final OracleJsonJdbcType INSTANCE = new OracleJsonJdbcType( null );
 
-	private OracleJsonJdbcType(EmbeddableMappingType embeddableMappingType) {
+	OracleJsonJdbcType(EmbeddableMappingType embeddableMappingType) {
 		super( embeddableMappingType );
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
index d32252791a67..c315efa64916 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
@@ -7,6 +7,7 @@
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
 
+import java.io.IOException;
 import java.lang.reflect.Type;
 
 /**
@@ -36,4 +37,24 @@ public final <T> String toString(T value, JavaType<T> javaType, WrapperOptions w
 	protected abstract <T> T fromString(CharSequence charSequence, Type type);
 
 	protected abstract <T> String toString(T value, Type type);
+
+	@Override
+	public boolean supportsSourceType(Class<?> sourceType) {
+		return false;
+	}
+
+	@Override
+	public boolean supportsTargetType(Class<?> targetType) {
+		return false;
+	}
+
+	@Override
+	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
+			throws IOException {
+	}
+
+	@Override
+	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
+		return null;
+	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java
index e22355f6e2e3..bff9726cb0b5 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java
@@ -8,6 +8,8 @@
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
 
+import java.io.IOException;
+
 /**
  * A mapper for mapping objects to and from a format.
  * <ul>
@@ -41,4 +43,22 @@ public interface FormatMapper {
 	 * Serializes the object to a string.
 	 */
 	<T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions);
+
+	/**
+	 * Checks that this mapper supports a type as a source type.
+	 * @param sourceType the source type
+	 * @return <code>true</code> if the type is supported, false otherwise.
+	 */
+	boolean supportsSourceType(Class<?> sourceType);
+
+	/**
+	 * Checks that this mapper supports a type as a target type.
+	 * @param targetType the target type
+	 * @return <code>true</code> if the type is supported, false otherwise.
+	 */
+	boolean supportsTargetType(Class<?> targetType);
+
+	<T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options) throws IOException;
+
+	<T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException;
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
index 5b63787b881b..b0285fb2c49b 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
@@ -12,6 +12,7 @@ public final class JacksonIntegration {
 	// when GraalVM native image is initializing them.
 	private static final boolean JACKSON_XML_AVAILABLE = ableToLoadJacksonXMLMapper();
 	private static final boolean JACKSON_JSON_AVAILABLE = ableToLoadJacksonJSONMapper();
+	private static final boolean JACKSON_OSON_AVAILABLE = ableToLoadJacksonOSONGenerator();
 	private static final JacksonXmlFormatMapper XML_FORMAT_MAPPER = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper() : null;
 	private static final JacksonXmlFormatMapper XML_FORMAT_MAPPER_PORTABLE = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper( false ) : null;
 	private static final JacksonJsonFormatMapper JSON_FORMAT_MAPPER = JACKSON_JSON_AVAILABLE ? new JacksonJsonFormatMapper() : null;
@@ -28,8 +29,14 @@ private static boolean ableToLoadJacksonXMLMapper() {
 		return canLoad( "com.fasterxml.jackson.dataformat.xml.XmlMapper" );
 	}
 
-	public static FormatMapper getXMLJacksonFormatMapperOrNull() {
-		return XML_FORMAT_MAPPER;
+	/**
+	 * Checks that Jackson is available and that we have the Oracle OSON extension available
+	 * in the classpath.
+	 * @return true if we can load the OSON support, false otherwise.
+	 */
+	private static boolean ableToLoadJacksonOSONGenerator() {
+		return ableToLoadJacksonJSONMapper() &&
+				canLoad( "oracle.jdbc.provider.oson.OsonGenerator" );
 	}
 
 	public static FormatMapper getXMLJacksonFormatMapperOrNull(boolean legacyFormat) {
@@ -40,6 +47,16 @@ public static FormatMapper getJsonJacksonFormatMapperOrNull() {
 		return JSON_FORMAT_MAPPER;
 	}
 
+	/**
+	 * Checks that Oracle OSON extension available
+	 *
+	 * @return true if we can load the OSON support, false otherwise.
+	 */
+	public static boolean isOracleOsonExtensionAvailable() {
+		return JACKSON_OSON_AVAILABLE;
+	}
+
+
 	private static boolean canLoad(String name) {
 		try {
 			//N.B. intentionally not using the context classloader
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index d9ecf79a9a4c..0c2045d89adc 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -4,11 +4,16 @@
  */
 package org.hibernate.type.format.jackson;
 
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+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.io.IOException;
 import java.lang.reflect.Type;
 
 /**
@@ -48,4 +53,32 @@ public <T> String toString(T value, Type type) {
 			throw new IllegalArgumentException( "Could not serialize object of java type: " + type, e );
 		}
 	}
+
+	@Override
+	public boolean supportsSourceType(Class<?> sourceType) {
+		return false;
+	}
+
+	@Override
+	public boolean supportsTargetType(Class<?> targetType) {
+		return false;
+	}
+
+	@Override
+	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
+			throws IOException {
+
+		objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) ).writeValue( (JsonGenerator) target, value);
+
+	}
+
+	@Override
+	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
+
+		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
+
+		T t = objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
+
+		return t;
+	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
index 170a683d2480..01d2d156cc49 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
@@ -40,6 +40,7 @@
 
 /**
  * @author Christian Beikov
+ * @author Emmanuel Jannetti
  */
 public final class JacksonXmlFormatMapper implements FormatMapper {
 
@@ -203,6 +204,27 @@ else if ( javaType.getJavaTypeClass().isArray() ) {
 		return writeValueAsString( value, javaType, javaType.getJavaType() );
 	}
 
+	@Override
+	public boolean supportsSourceType(Class<?> sourceType) {
+		return false;
+	}
+
+	@Override
+	public boolean supportsTargetType(Class<?> targetType) {
+		return false;
+	}
+
+	@Override
+	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
+			throws IOException {
+
+	}
+
+	@Override
+	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
+		return null;
+	}
+
 	private <T> String writeValueAsString(Object value, JavaType<T> javaType, Type type) {
 		try {
 			return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jakartajson/JsonBJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jakartajson/JsonBJsonFormatMapper.java
index 515c3bb7fc14..67404b2becc5 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jakartajson/JsonBJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jakartajson/JsonBJsonFormatMapper.java
@@ -15,6 +15,7 @@
 /**
  * @author Christian Beikov
  * @author Yanming Zhou
+ * @author Emmanuel Jannetti
  */
 public final class JsonBJsonFormatMapper extends AbstractJsonFormatMapper {
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java
index c0264691252f..4aff790faf84 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jaxb/JaxbXmlFormatMapper.java
@@ -4,6 +4,7 @@
  */
 package org.hibernate.type.format.jaxb;
 
+import java.io.IOException;
 import java.io.StringReader;
 import java.io.StringWriter;
 import java.lang.reflect.Array;
@@ -497,6 +498,27 @@ else if ( javaType.getJavaTypeClass().isArray() ) {
 		}
 	}
 
+	@Override
+	public boolean supportsSourceType(Class<?> sourceType) {
+		return false;
+	}
+
+	@Override
+	public boolean supportsTargetType(Class<?> targetType) {
+		return false;
+	}
+
+	@Override
+	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
+			throws IOException {
+
+	}
+
+	@Override
+	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
+		return null;
+	}
+
 	private JAXBElementTransformer createTransformer(
 			StringBuilderSqlAppender appender,
 			Class<?> elementClass,

From 2cf095ac0d025066a6c4373428374832ace4c3ed Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Tue, 17 Dec 2024 14:52:23 +0530
Subject: [PATCH 02/81] HHH-17404: Jakarta annotation souuport for Jackson

---
 .../dialect/OracleOsonJacksonJdbcType.java    |  10 +-
 .../aggregate/OracleAggregateSupport.java     |   7 +-
 .../type/OracleUserDefinedTypeExporter.java   |   2 +-
 .../JacksonJakartaAnnotationIntrospector.java | 107 ++++++++++++++++++
 .../jackson/JacksonJsonFormatMapper.java      |   5 +-
 .../jackson/JacksonOsonFormatMapper.java      |  79 +++++++++++++
 6 files changed, 198 insertions(+), 12 deletions(-)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 7dc9f047560d..ef7e7aa5a7a2 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -17,7 +17,7 @@
 import org.hibernate.type.descriptor.jdbc.BasicBinder;
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
 import org.hibernate.type.format.FormatMapper;
-import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
+import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.InputStream;
 import java.io.PipedInputStream;
@@ -85,7 +85,7 @@ private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions opt
 				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				//
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType(),getJavaType());
 
 				PipedOutputStream out = new PipedOutputStream();
 				PipedInputStream in = new PipedInputStream(out);
@@ -159,7 +159,7 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				//
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper, getEmbeddableMappingType(),getJavaType());
 
 				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
 				if ( ojd == null ) {
@@ -185,7 +185,7 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				//
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType(),getJavaType());
 
 
 				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
@@ -210,7 +210,7 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o
 				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				//
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType(),getJavaType());
 
 				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
 				if ( ojd == null ) {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index f69cc49ba888..23f6a81a1ee8 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -195,6 +195,7 @@ public String aggregateComponentCustomReadExpression(
 							case BINARY:
 							case VARBINARY:
 							case LONG32VARBINARY:
+							case UUID:
 								// We encode binary data as hex, so we have to decode here
 								if ( determineLength( column ) * 2 < 4000L ) {
 									return template.replace(
@@ -230,14 +231,10 @@ public String aggregateComponentCustomReadExpression(
 									case VARBINARY:
 									case LONG32VARBINARY:
 									case UUID:
-										return template.replace(
-												placeholder,
-												jdbcType.getSqlTypeName() + "_from_json(json_query(" + parentPartExpression + columnExpression + "' returning " + jsonTypeName + "))"
-										);
 									default:
 										return template.replace(
 												placeholder,
-												"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
+												jdbcType.getSqlTypeName() + "_from_json(json_query(" + parentPartExpression + columnExpression + "' returning " + jsonTypeName + "))"
 										);
 								}
 							case JSON:
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java
index 4808017f80ce..4c37624d4344 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java
@@ -337,7 +337,7 @@ private String determineValueExpression(String expression, int elementSqlTypeCod
 			case DATE:
 				return "to_date(" + expression + ",'YYYY-MM-DD')";
 			case TIME:
-				return "to_timestamp(" + expression + ",'hh24:mi:ss')";
+				return "to_timestamp(CONCAT('1970-01-01 ', \" + expression + \"),'YYYY-MM-DD hh24:mi:ss')";
 			case TIMESTAMP:
 				return "to_timestamp(" + expression + ",'YYYY-MM-DD\"T\"hh24:mi:ss.FF9')";
 			case TIMESTAMP_WITH_TIMEZONE:
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
new file mode 100644
index 000000000000..adc17745dcd5
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
@@ -0,0 +1,107 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format.jackson;
+
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import org.hibernate.metamodel.mapping.AttributeMapping;
+import org.hibernate.metamodel.mapping.AttributeMappingsList;
+import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+import org.hibernate.metamodel.mapping.SelectableMapping;
+import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class JacksonJakartaAnnotationIntrospector extends JacksonAnnotationIntrospector {
+
+	private final EmbeddableMappingType mappingType;
+	private final Map<String, EmbeddableMappingTypeWithFlattening> mappingTypeMap = new HashMap<>();
+
+	public JacksonJakartaAnnotationIntrospector(EmbeddableMappingType mappingType) {
+		this.mappingType = mappingType;
+		resolveEmbeddableTypes( this.mappingType );
+	}
+
+	@Override
+	public PropertyName findNameForSerialization(Annotated a) {
+		Column column = _findAnnotation(a, Column.class);
+		if (column != null && !column.name().isEmpty()) {
+			return PropertyName.construct(column.name());
+		}
+		return super.findNameForSerialization(a);
+	}
+
+	@Override
+	public PropertyName findNameForDeserialization(Annotated a) {
+		Column column = _findAnnotation(a, Column.class);
+		if (column != null && !column.name().isEmpty()) {
+			return PropertyName.construct(column.name());
+		}
+		return super.findNameForDeserialization(a);
+	}
+
+	@Override
+	public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) {
+		if(member instanceof AnnotatedField ) {
+			Embeddable embeddable = member.getType().getRawClass().getAnnotation(Embeddable.class);
+			if (embeddable != null) {
+				String propName = member.getName();
+				if(mappingTypeMap.get(propName) != null){
+					EmbeddableMappingTypeWithFlattening embeddableMappingTypeWithFlattening =
+							mappingTypeMap.get( propName );
+					if(embeddableMappingTypeWithFlattening.isShouldFlatten()) {
+						return NameTransformer.simpleTransformer( "","" );
+					}
+				}
+			}
+		}
+		return super.findUnwrappingNameTransformer(member);
+	}
+// theJson
+	private void resolveEmbeddableTypes(EmbeddableMappingType embeddableMappingType) {
+		AttributeMappingsList attributeMappings = embeddableMappingType.getAttributeMappings();
+
+		for (int i = 0; i < attributeMappings.size(); i++){
+			AttributeMapping attributeMapping = attributeMappings.get(i);
+			if ( attributeMapping instanceof EmbeddedAttributeMapping embeddedAttributeMapping ) {
+
+				EmbeddableMappingType attributeEmbeddableMappingType = embeddedAttributeMapping.getMappedType();
+				SelectableMapping aggregateMapping = attributeEmbeddableMappingType.getAggregateMapping();
+
+				mappingTypeMap.put( attributeMapping.getAttributeName(),
+						new EmbeddableMappingTypeWithFlattening(
+								attributeEmbeddableMappingType,
+								aggregateMapping == null ));
+
+				resolveEmbeddableTypes( attributeEmbeddableMappingType);
+			}
+		}
+	}
+
+	static class EmbeddableMappingTypeWithFlattening {
+		private final EmbeddableMappingType embeddableMappingType;
+		private final boolean shouldFlatten;
+
+		public EmbeddableMappingTypeWithFlattening(EmbeddableMappingType embeddableMappingType, boolean shouldFlatten) {
+			this.embeddableMappingType = embeddableMappingType;
+			this.shouldFlatten = shouldFlatten;
+		}
+
+		public EmbeddableMappingType getEmbeddableMappingType() {
+			return embeddableMappingType;
+		}
+
+		public boolean isShouldFlatten() {
+			return shouldFlatten;
+		}
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index 0c2045d89adc..09c365be0b7f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -6,6 +6,7 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.ObjectWriter;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
 import org.hibernate.type.format.AbstractJsonFormatMapper;
@@ -68,7 +69,9 @@ public boolean supportsTargetType(Class<?> targetType) {
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
 
-		objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) ).writeValue( (JsonGenerator) target, value);
+		ObjectWriter writer = objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) );
+		writer.writeValue( (JsonGenerator) target, value);
+
 
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
new file mode 100644
index 000000000000..55055bc43180
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -0,0 +1,79 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format.jackson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.format.AbstractJsonFormatMapper;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+public class JacksonOsonFormatMapper extends AbstractJsonFormatMapper {
+
+	public static final String SHORT_NAME = "jackson";
+
+	private final ObjectMapper objectMapper;
+	private final EmbeddableMappingType embeddableMappingType;
+
+
+
+	public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
+		this.objectMapper = objectMapper;
+		this.embeddableMappingType = embeddableMappingType;
+		this.objectMapper.setAnnotationIntrospector( new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
+
+	}
+
+	public <T> JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType, JavaType<T> javaType) {
+		this.objectMapper = objectMapper;
+		this.embeddableMappingType = embeddableMappingType;
+		this.objectMapper.setAnnotationIntrospector( new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
+
+	}
+
+	@Override
+	public <T> T fromString(CharSequence charSequence, Type type) {
+		try {
+			return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( type ) );
+		}
+		catch (JsonProcessingException e) {
+			throw new IllegalArgumentException( "Could not deserialize string to java type: " + type, e );
+		}
+	}
+
+	@Override
+	public <T> String toString(T value, Type type) {
+		try {
+			return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
+		}
+		catch (JsonProcessingException e) {
+			throw new IllegalArgumentException( "Could not serialize object of java type: " + type, e );
+		}
+	}
+
+	@Override
+	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
+			throws IOException {
+
+		ObjectWriter writer = objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) );
+		writer.writeValue( (JsonGenerator) target, value);
+	}
+
+	@Override
+	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
+		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
+
+		T t = objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
+
+		return t;
+	}
+}

From 2ae32e5cdc0cb9f00f1f16f1a41d73be1237fb07 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Mon, 16 Dec 2024 16:27:27 +0100
Subject: [PATCH 03/81] HHH-17404: align JDBC dependencies version

---
 .../test/mapping/embeddable/Aggregate.java    |  4 ++
 .../embeddable/NestedJsonEmbeddableTest.java  | 40 +++++++++++++++++++
 hibernate-testing/hibernate-testing.gradle    |  8 ++++
 3 files changed, 52 insertions(+)

diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java
index c761c25904af..c0d229411aa8 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java
@@ -149,6 +149,10 @@ public void setConvertedGender(EntityOfBasics.Gender convertedGender) {
 		this.convertedGender = convertedGender;
 	}
 
+	public Boolean getTheBoolean() {
+		return theBoolean;
+	}
+
 	@Column(name = "ordinal_gender")
 	public EntityOfBasics.Gender getOrdinalGender() {
 		return ordinalGender;
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
index 2f5d880bcad5..2e4415a4db56 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
@@ -453,6 +453,30 @@ public static class TheJson {
 		public TheJson() {
 		}
 
+		public String getStringField() {
+			return stringField;
+		}
+
+		public void setStringField(String stringField) {
+			this.stringField = stringField;
+		}
+
+		public SimpleEmbeddable getSimpleEmbeddable() {
+			return simpleEmbeddable;
+		}
+
+		public void setSimpleEmbeddable(SimpleEmbeddable simpleEmbeddable) {
+			this.simpleEmbeddable = simpleEmbeddable;
+		}
+
+		public EmbeddableAggregate getNested() {
+			return nested;
+		}
+
+		public void setNested(EmbeddableAggregate nested) {
+			this.nested = nested;
+		}
+
 		public TheJson(String stringField, Integer integerField, String leaf, EmbeddableAggregate nested) {
 			this.stringField = stringField;
 			this.simpleEmbeddable = new SimpleEmbeddable( integerField, leaf );
@@ -472,6 +496,22 @@ public static class SimpleEmbeddable {
 		@JdbcTypeCode(SqlTypes.JSON)
 		private DoubleNested doubleNested;
 
+		public Integer getIntegerField() {
+			return integerField;
+		}
+
+		public void setIntegerField(Integer integerField) {
+			this.integerField = integerField;
+		}
+
+		public DoubleNested getDoubleNested() {
+			return doubleNested;
+		}
+
+		public void setDoubleNested(DoubleNested doubleNested) {
+			this.doubleNested = doubleNested;
+		}
+
 		public SimpleEmbeddable() {
 		}
 
diff --git a/hibernate-testing/hibernate-testing.gradle b/hibernate-testing/hibernate-testing.gradle
index f64f1e44418c..0c13cdefd35c 100644
--- a/hibernate-testing/hibernate-testing.gradle
+++ b/hibernate-testing/hibernate-testing.gradle
@@ -46,5 +46,13 @@ dependencies {
     implementation testLibs.junit5Launcher
 
     annotationProcessor project( ':hibernate-processor' )
+
+
+
+    runtimeOnly ('com.oracle.database.jdbc:ojdbc-provider-jackson-oson:1.0.2') {
+        exclude group: 'com.oracle.database.jdbc', module: 'ojdbc8'
+    }
+
 }
 
+

From 98fa356e125a8798567af4dba86b97900114833c Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Tue, 17 Dec 2024 16:03:43 +0100
Subject: [PATCH 04/81] HHH-17404 : use OsonEvent to deal with temporal

---
 .../dialect/OracleOsonJacksonJdbcType.java    |   5 +-
 .../jackson/JacksonJsonFormatMapper.java      |   2 +-
 .../jackson/JacksonOsonFormatMapper.java      | 169 +++++++++++++++++-
 .../embeddable/EmbeddableAggregate.java       |   8 +-
 4 files changed, 172 insertions(+), 12 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index ef7e7aa5a7a2..54392574d4cb 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -140,8 +140,9 @@ private X fromOson(byte[] osonBytes, FormatMapper mapper, WrapperOptions options
 					// We are dealing with embeddable (@Embeddable) and we request
 					// an array of objects. We use JsonParser to fetch values
 					// and build the array.(as opposed to let Jackson do it as we do not
-					// have proper obejct definition at that stage).
-
+					// have a proper object definition at that stage).
+					return ((JacksonOsonFormatMapper)mapper).readToArray(
+							getEmbeddableMappingType(), osonBytes, options );
 				}
 
 				JavaType <X> type = getJavaType();
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index 09c365be0b7f..b6d77ffcfeb8 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -21,7 +21,7 @@
  * @author Christian Beikov
  * @author Yanming Zhou
  */
-public final class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
+public class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
 
 	public static final String SHORT_NAME = "jackson";
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 55055bc43180..bee886b91a3d 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -7,26 +7,56 @@
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
+import oracle.sql.json.OracleJsonParser;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+import org.hibernate.metamodel.mapping.SelectableMapping;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
-import org.hibernate.type.format.AbstractJsonFormatMapper;
+import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
 
-public class JacksonOsonFormatMapper extends AbstractJsonFormatMapper {
+
+public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 
 	public static final String SHORT_NAME = "jackson";
 
 	private final ObjectMapper objectMapper;
 	private final EmbeddableMappingType embeddableMappingType;
-
-
-
+	private static final Class OsonParserKlass;
+	private static Method OsonParserKlassCurrentEventMethod = null;
+	private static Method OsonParserKlassGetLocalDateTimeMethod = null;
+	private static Method OsonParserKlassReadDurationMethod = null;
+	private static Method OsonParserKlassReadOffsetDateTimeMethod = null;
+		static {
+	try {
+
+		OsonParserKlass = JacksonOsonFormatMapper.class.getClassLoader()
+				.loadClass( "oracle.jdbc.provider.oson.OsonParser" );
+		OsonParserKlassCurrentEventMethod = OsonParserKlass.getMethod( "currentOsonEvent" );
+		OsonParserKlassGetLocalDateTimeMethod = OsonParserKlass.getMethod( "getLocalDateTime" );
+		OsonParserKlassReadDurationMethod = OsonParserKlass.getMethod( "readDuration" );
+		OsonParserKlassReadOffsetDateTimeMethod = OsonParserKlass.getMethod("readOffsetDateTime" );
+	}
+	catch (ClassNotFoundException | LinkageError | NoSuchMethodException e) {
+		// should not happen as OracleOsonJacksonJdbcType is loaded
+		// only when Oracle OSON JDBC extension is present
+		// see OracleDialect class.
+		throw new ExceptionInInitializerError(
+				"JacksonOsonFormatMapper class loaded without OSON extension: " + e.getClass() + " " + e.getMessage() );
+	}
+}
 	public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
+		super(objectMapper);
 		this.objectMapper = objectMapper;
 		this.embeddableMappingType = embeddableMappingType;
 		this.objectMapper.setAnnotationIntrospector( new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
@@ -34,12 +64,141 @@ public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType
 	}
 
 	public <T> JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType, JavaType<T> javaType) {
+		super(objectMapper);
 		this.objectMapper = objectMapper;
 		this.embeddableMappingType = embeddableMappingType;
 		this.objectMapper.setAnnotationIntrospector( new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
 
 	}
 
+
+
+	private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, Object [] finalResult, EmbeddableMappingType embeddableMappingType, WrapperOptions options)
+			throws IOException {
+
+		JsonToken token = currentToken;
+
+		int selectableIndex = -1;
+		SelectableMapping mapping = null;
+		while ( true ) {
+			if ( token == null ) {
+				break;
+			}
+			switch ( token ) {
+				case JsonToken.START_ARRAY:
+					int i = 0;
+					break;
+				case JsonToken.VALUE_STRING:
+					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
+					//selectableIndex = embeddableMappingType.getSelectableIndex(currentFieldName );
+					//AttributeMapping am = embeddableMappingType.findAttributeMapping( currentFieldName );
+					assert selectableIndex >= 0;
+					mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
+					//result.add(osonParser.getText());
+					//					result.add(mapping.getJdbcMapping().convertToDomainValue(
+					//							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getText(), options ) ));
+
+					try {
+						OracleJsonParser.Event event =
+								(OracleJsonParser.Event)OsonParserKlassCurrentEventMethod.invoke( osonParser );
+						switch(event) {
+							case OracleJsonParser.Event.VALUE_DATE :
+
+								break;
+								case OracleJsonParser.Event.VALUE_TIMESTAMP:
+									LocalDateTime local =
+											(LocalDateTime)OsonParserKlassGetLocalDateTimeMethod.invoke( osonParser );
+									finalResult[selectableIndex] = Timestamp.valueOf(local);
+									break;
+								case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
+									OffsetDateTime offsetDT = (OffsetDateTime)OsonParserKlassReadOffsetDateTimeMethod.invoke( osonParser );
+									finalResult[selectableIndex] = offsetDT;
+									break;
+								case OracleJsonParser.Event.VALUE_INTERVALDS:
+									Duration duration = (Duration) OsonParserKlassReadDurationMethod.invoke(osonParser);
+									finalResult[selectableIndex] = duration;
+									break;
+								case OracleJsonParser.Event.VALUE_INTERVALYM:
+									//break;  TODO should not be like that
+								default :
+									finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+											mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() ) );
+							}
+					}
+					catch (Exception e) {
+						throw new IOException( e );
+					}
+					break;
+				case JsonToken.VALUE_TRUE:
+					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
+					finalResult[selectableIndex] = Boolean.TRUE.toString();
+					break;
+				case JsonToken.VALUE_FALSE:
+					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
+					finalResult[selectableIndex] = Boolean.TRUE.toString();
+					break;
+				case JsonToken.VALUE_NULL:
+					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
+					finalResult[selectableIndex] = null;
+					break;
+				case JsonToken.VALUE_NUMBER_INT:
+					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
+					//selectableIndex = embeddableMappingType.getSelectableIndex(currentFieldName );
+					assert selectableIndex >= 0;
+					mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
+					finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+						mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getIntValue(), options ) );
+					break;
+				case JsonToken.VALUE_NUMBER_FLOAT:
+					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
+					//selectableIndex = embeddableMappingType.getSelectableIndex(currentFieldName );
+					assert selectableIndex >= 0;
+					mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
+					finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloatValue(), options ) );
+					break;
+				case JsonToken.VALUE_EMBEDDED_OBJECT:
+					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
+					// TODO: think twice about this conversion
+					finalResult[selectableIndex] =  new String( osonParser.getBinaryValue() );
+					break;
+				case JsonToken.START_OBJECT:
+					if ( osonParser.currentName() == null ) {
+						// that's the root
+						consumeValuedToken( osonParser, osonParser.nextToken(), finalResult,
+								embeddableMappingType,
+								options  );
+					}
+					else {
+						selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
+						if ( selectableIndex != -1 ) {
+							final SelectableMapping selectable = embeddableMappingType.getJdbcValueSelectable(
+									selectableIndex );
+							final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
+									.getJdbcType();
+							final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
+							consumeValuedToken( osonParser, osonParser.nextToken(), finalResult,
+									subMappingType,
+									options );
+						}
+					}
+					break;
+				case JsonToken.END_OBJECT:
+					return;
+			}
+			token = osonParser.nextToken();
+		}
+
+	}
+
+	public <T> T readToArray(EmbeddableMappingType embeddableMappingType, Object source, WrapperOptions options) throws IOException {
+		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
+		Object []finalResult = new Object[embeddableMappingType.getJdbcValueCount()];
+		consumeValuedToken(osonParser, osonParser.nextToken(), finalResult, embeddableMappingType, options);
+		return (T)finalResult;
+	}
+
+
 	@Override
 	public <T> T fromString(CharSequence charSequence, Type type) {
 		try {
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
index 069ed1ac1559..01d63dcb166a 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
@@ -60,7 +60,7 @@ public class EmbeddableAggregate {
 	private byte[] theBinary;
 	private Date theDate;
 	private Date theTime;
-	private Date theTimestamp;
+	private Timestamp theTimestamp;
 	private Instant theInstant;
 	private UUID theUuid;
 	private EntityOfBasics.Gender gender;
@@ -183,12 +183,12 @@ public void setTheTime(Date theTime) {
 		this.theTime = theTime;
 	}
 
-	@Temporal( TemporalType.TIMESTAMP )
-	public Date getTheTimestamp() {
+	//@Temporal( TemporalType.TIMESTAMP )
+	public Timestamp getTheTimestamp() {
 		return theTimestamp;
 	}
 
-	public void setTheTimestamp(Date theTimestamp) {
+	public void setTheTimestamp(Timestamp theTimestamp) {
 		this.theTimestamp = theTimestamp;
 	}
 

From c5b744073cb83cfc18ecbaacc9c33b240c0538a9 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Tue, 17 Dec 2024 22:37:02 +0100
Subject: [PATCH 05/81] HHH-17404 : fix Boolean convertion

---
 .../JacksonJakartaAnnotationIntrospector.java | 26 ++++++++++++++++--
 .../jackson/JacksonOsonFormatMapper.java      | 27 +++++++++++++------
 .../embeddable/NestedJsonEmbeddableTest.java  | 24 +++++++++++++++++
 3 files changed, 67 insertions(+), 10 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
index adc17745dcd5..57c81dedf670 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
@@ -8,6 +8,7 @@
 import com.fasterxml.jackson.databind.introspect.Annotated;
 import com.fasterxml.jackson.databind.introspect.AnnotatedField;
 import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
 import com.fasterxml.jackson.databind.util.NameTransformer;
 import jakarta.persistence.Column;
@@ -48,10 +49,19 @@ public PropertyName findNameForDeserialization(Annotated a) {
 		}
 		return super.findNameForDeserialization(a);
 	}
-
+	private String getFieldNameFromGetterName(String fieldName) {
+		// we assume that method is get|set<camelCase Nme>
+		// 1. we strip out get|set
+		// 2. lowercase on first letter
+		assert fieldName != null;
+		assert fieldName.substring( 0,3 ).equalsIgnoreCase( "get" ) ||
+			fieldName.substring( 0,3 ).equalsIgnoreCase( "set" );
+		fieldName = fieldName.substring( 3 );
+		return Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);
+	}
 	@Override
 	public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) {
-		if(member instanceof AnnotatedField ) {
+		if(member instanceof AnnotatedField) {
 			Embeddable embeddable = member.getType().getRawClass().getAnnotation(Embeddable.class);
 			if (embeddable != null) {
 				String propName = member.getName();
@@ -63,6 +73,18 @@ public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) {
 					}
 				}
 			}
+		} else if (member instanceof AnnotatedMethod) {
+			Embeddable embeddable = member.getType().getRawClass().getAnnotation(Embeddable.class);
+			if (embeddable != null) {
+				String propName = getFieldNameFromGetterName(member.getName());
+				if(mappingTypeMap.get(propName) != null){
+					EmbeddableMappingTypeWithFlattening embeddableMappingTypeWithFlattening =
+							mappingTypeMap.get( propName );
+					if(embeddableMappingTypeWithFlattening.isShouldFlatten()) {
+						return NameTransformer.simpleTransformer( "","" );
+					}
+				}
+			}
 		}
 		return super.findUnwrappingNameTransformer(member);
 	}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index bee886b91a3d..8c6657b786c4 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -25,7 +25,10 @@
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 
-
+/**
+ * @author Emmanuel Jannetti
+ * @auther Bidyadhar Mohanty
+ */
 public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 
 	public static final String SHORT_NAME = "jackson";
@@ -121,8 +124,15 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 								case OracleJsonParser.Event.VALUE_INTERVALYM:
 									//break;  TODO should not be like that
 								default :
-									finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-											mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() ) );
+//									finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+//											mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() ) );
+//									Object f1 = mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() );
+//									Object f2 = mapping.getJdbcMapping().convertToDomainValue(mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() ));
+//									String s1 = osonParser.getText();
+//									Object o1 = mapping.getJdbcMapping().convertToDomainValue( osonParser.getText()  );
+//									Object o2 = mapping.getJdbcMapping().convertToRelationalValue( osonParser.getText()  );
+//									finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue( osonParser.getText()  );
+									finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() );
 							}
 					}
 					catch (Exception e) {
@@ -131,11 +141,11 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 					break;
 				case JsonToken.VALUE_TRUE:
 					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
-					finalResult[selectableIndex] = Boolean.TRUE.toString();
+					finalResult[selectableIndex] = Boolean.TRUE;
 					break;
 				case JsonToken.VALUE_FALSE:
 					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
-					finalResult[selectableIndex] = Boolean.TRUE.toString();
+					finalResult[selectableIndex] = Boolean.FALSE;
 					break;
 				case JsonToken.VALUE_NULL:
 					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
@@ -159,8 +169,7 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 					break;
 				case JsonToken.VALUE_EMBEDDED_OBJECT:
 					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
-					// TODO: think twice about this conversion
-					finalResult[selectableIndex] =  new String( osonParser.getBinaryValue() );
+					finalResult[selectableIndex] = osonParser.getBinaryValue() ;
 					break;
 				case JsonToken.START_OBJECT:
 					if ( osonParser.currentName() == null ) {
@@ -177,7 +186,9 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 							final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
 									.getJdbcType();
 							final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-							consumeValuedToken( osonParser, osonParser.nextToken(), finalResult,
+							finalResult[selectableIndex] = new Object[subMappingType.getJdbcValueCount()];
+							consumeValuedToken( osonParser, osonParser.nextToken(),
+									(Object[]) finalResult[selectableIndex],
 									subMappingType,
 									options );
 						}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
index 2e4415a4db56..a1f25a98d226 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
@@ -557,6 +557,14 @@ public DoubleNested(String leaf) {
 			this.theNested = new Nested( leaf );
 		}
 
+		public Nested getTheNested() {
+			return theNested;
+		}
+
+		public void setTheNested(Nested theNested) {
+			this.theNested = theNested;
+		}
+
 		@Override
 		public boolean equals(Object o) {
 			if ( this == o ) {
@@ -589,6 +597,14 @@ public Nested(String stringField) {
 			this.theLeaf = new Leaf( stringField );
 		}
 
+		public Leaf getTheLeaf() {
+			return theLeaf;
+		}
+
+		public void setTheLeaf(Leaf theLeaf) {
+			this.theLeaf = theLeaf;
+		}
+
 		@Override
 		public boolean equals(Object o) {
 			if ( this == o ) {
@@ -620,6 +636,14 @@ public Leaf(String stringField) {
 			this.stringField = stringField;
 		}
 
+		public String getStringField() {
+			return stringField;
+		}
+
+		public void setStringField(String stringField) {
+			this.stringField = stringField;
+		}
+
 		@Override
 		public boolean equals(Object o) {
 			if ( this == o ) {

From 578bbd45238c135a569a86934e0ef3c9e5965f90 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 18 Dec 2024 09:03:45 +0100
Subject: [PATCH 06/81] HHH-17404 : fix UUID deserialization

---
 .../dialect/OracleOsonJacksonJdbcType.java    |  7 +++--
 .../jackson/JacksonOsonFormatMapper.java      | 29 ++++++++++++++++---
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 54392574d4cb..d7a4e8eaa2b4 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -19,6 +19,8 @@
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
@@ -87,11 +89,12 @@ private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions opt
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
 				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType(),getJavaType());
 
-				PipedOutputStream out = new PipedOutputStream();
-				PipedInputStream in = new PipedInputStream(out);
+				ByteArrayOutputStream out = new ByteArrayOutputStream();
+                //TODO : really use streams
 				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
 				osonGen.close();
+				ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 				return in;
 			}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 8c6657b786c4..edb82349fe9a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -20,10 +20,12 @@
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.nio.ByteBuffer;
 import java.sql.Timestamp;
 import java.time.Duration;
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
+import java.util.UUID;
 
 /**
  * @author Emmanuel Jannetti
@@ -106,15 +108,24 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 								(OracleJsonParser.Event)OsonParserKlassCurrentEventMethod.invoke( osonParser );
 						switch(event) {
 							case OracleJsonParser.Event.VALUE_DATE :
-
+									LocalDateTime localDate =
+										(LocalDateTime)OsonParserKlassGetLocalDateTimeMethod.invoke( osonParser );
+									finalResult[selectableIndex] =  java.sql.Date.valueOf(localDate.toLocalDate());
 								break;
 								case OracleJsonParser.Event.VALUE_TIMESTAMP:
 									LocalDateTime local =
 											(LocalDateTime)OsonParserKlassGetLocalDateTimeMethod.invoke( osonParser );
-									finalResult[selectableIndex] = Timestamp.valueOf(local);
+									if ("java.sql.Timestamp".equals(
+											embeddableMappingType.getJdbcValueSelectable( selectableIndex )
+													.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName())) {
+										finalResult[selectableIndex] = Timestamp.valueOf( local );
+									} else {
+										finalResult[selectableIndex] = local;
+									}
 									break;
 								case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
-									OffsetDateTime offsetDT = (OffsetDateTime)OsonParserKlassReadOffsetDateTimeMethod.invoke( osonParser );
+									OffsetDateTime offsetDT =
+											(OffsetDateTime)OsonParserKlassReadOffsetDateTimeMethod.invoke( osonParser );
 									finalResult[selectableIndex] = offsetDT;
 									break;
 								case OracleJsonParser.Event.VALUE_INTERVALDS:
@@ -169,7 +180,17 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 					break;
 				case JsonToken.VALUE_EMBEDDED_OBJECT:
 					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
-					finalResult[selectableIndex] = osonParser.getBinaryValue() ;
+					byte[] bytes = osonParser.getBinaryValue();
+					if ("java.util.UUID".equals(
+							embeddableMappingType.getJdbcValueSelectable( selectableIndex )
+									.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName())) {
+						ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+						long mostSignificantBits = byteBuffer.getLong();
+						long leastSignificantBits = byteBuffer.getLong();
+						finalResult[selectableIndex] = new UUID(mostSignificantBits, leastSignificantBits);
+					} else {
+						finalResult[selectableIndex] = bytes;
+					}
 					break;
 				case JsonToken.START_OBJECT:
 					if ( osonParser.currentName() == null ) {

From 77a7fc67efaf252291cbcb1936618f51327da73d Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 18 Dec 2024 17:10:41 +0100
Subject: [PATCH 07/81] HHH-17404 : move to OracleJSONParser

---
 .../OracleOsonJacksonArrayJdbcType.java       |  75 ++----
 .../dialect/OracleOsonJacksonJdbcType.java    |  81 ++----
 .../jackson/JacksonOsonFormatMapper.java      | 232 ++++++++----------
 3 files changed, 151 insertions(+), 237 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index 1bf468b37ab4..b59c680c8651 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -16,10 +16,11 @@
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
+import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.CallableStatement;
@@ -31,10 +32,6 @@
  * @author Emmanuel Jannetti
  */
 public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
-	/**
-	 * Singleton access
-	 */
-	//public static final OracleOsonJacksonArrayJdbcType INSTANCE = new OracleOsonJacksonArrayJdbcType();
 
 	private static Method jacksonOsonObjectMapperGetter = null;
 
@@ -82,11 +79,12 @@ private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions opt
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
 				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
 
-				PipedOutputStream out = new PipedOutputStream();
-				PipedInputStream in = new PipedInputStream(out);
+				ByteArrayOutputStream out = new ByteArrayOutputStream();
+
 				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
 				osonGen.close();
+				ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 				return in;
 			}
 			@Override
@@ -127,74 +125,49 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 		return new BasicExtractor<>( javaType, this ) {
 
-			private X fromOson(byte[] osonBytes, FormatMapper mapper, WrapperOptions options) throws Exception {
-				return mapper.readFromSource(  getJavaType(), osonBytes, options);
-			}
-
-			@Override
-			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
+			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 				// TODO : We should rely on
 				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				//
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper);
+				return mapper.readFromSource(  getJavaType(), osonBytes, options);
+			}
 
-				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-				if ( ojd == null ) {
+			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
+				if ( datum == null ) {
 					return null;
 				}
-				byte[] osonBytes = ojd.shareBytes();
-
+				byte[] osonBytes = datum.shareBytes();
 				try {
-					return fromOson( osonBytes, mapper ,options);
+					return fromOson( osonBytes ,options);
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
 				}
 			}
 
+			@Override
+			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
+
+				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+				return doExtraction( ojd, options);
+			}
+
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+
 
 				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
-				if ( ojd == null ) {
-					return null;
-				}
-				byte[] osonBytes = ojd.shareBytes();
-				try {
-					return fromOson( osonBytes, mapper ,options);
-				}
-				catch (Exception e) {
-					throw new SQLException( e );
-				}
+				return doExtraction( ojd, options);
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
-
 
 				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
-				if ( ojd == null ) {
-					return null;
-				}
-				byte[] osonBytes = ojd.shareBytes();
-				try {
-					return fromOson( osonBytes, mapper ,options);
-				}
-				catch (Exception e) {
-					throw new SQLException( e );
-				}
+				return doExtraction( ojd, options);
 			}
 		};
 	}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index d7a4e8eaa2b4..e1ccdab50a2e 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -22,8 +22,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.CallableStatement;
@@ -87,10 +85,10 @@ private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions opt
 				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				//
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType(),getJavaType());
+				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType());
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
-                //TODO : really use streams
+				//TODO : really use streams
 				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
 				osonGen.close();
@@ -136,7 +134,12 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 		return new BasicExtractor<>( javaType, this ) {
 
-			private X fromOson(byte[] osonBytes, FormatMapper mapper, WrapperOptions options) throws Exception {
+			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
+				// TODO : We should rely on
+				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				//
+				//     But this do not let use inject our ObjectMapper. For now create our own instance
+				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper, getEmbeddableMappingType());
 
 				if (getEmbeddableMappingType() != null &&
 						getJavaType().getJavaTypeClass() == Object[].class) {
@@ -156,25 +159,13 @@ private X fromOson(byte[] osonBytes, FormatMapper mapper, WrapperOptions options
 				return mapper.readFromSource( type, osonBytes, options );
 			}
 
-			@Override
-			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper, getEmbeddableMappingType(),getJavaType());
-
-				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-				if ( ojd == null ) {
+			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
+				if ( datum == null ) {
 					return null;
 				}
-				byte[] osonBytes = ojd.shareBytes();
-
-
-
+				byte[] osonBytes = datum.shareBytes();
 				try {
-					return fromOson( osonBytes, mapper ,options);
+					return fromOson( osonBytes ,options);
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -182,51 +173,25 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 			}
 
 			@Override
-			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-
-
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType(),getJavaType());
-
+			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
+				// can I use rs.getBinaryStream( paramIndex); ?
+				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+				return doExtraction(ojd,options);
+			}
 
+			@Override
+			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
+				// can I use rs.getBinaryStream( paramIndex); ?
 				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
-				if ( ojd == null ) {
-					return null;
-				}
-				byte[] osonBytes = ojd.shareBytes();
-
-				try {
-					return fromOson( osonBytes, mapper ,options);
-				}
-				catch (Exception e) {
-					throw new SQLException( e );
-				}
+				return doExtraction(ojd,options);
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
-
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType(),getJavaType());
-
+				// can I use rs.getBinaryStream( paramIndex); ?
 				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
-				if ( ojd == null ) {
-					return null;
-				}
-				byte[] osonBytes = ojd.shareBytes();
-				try {
-					return fromOson( osonBytes, mapper ,options);
-				}
-				catch (Exception e) {
-					throw new SQLException( e );
-				}
+				return doExtraction(ojd,options);
 			}
 
 		};
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index edb82349fe9a..71edacba1234 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -7,9 +7,9 @@
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
+import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonParser;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.mapping.SelectableMapping;
@@ -18,13 +18,11 @@
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 
 import java.io.IOException;
-import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.nio.ByteBuffer;
 import java.sql.Timestamp;
 import java.time.Duration;
 import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
 import java.util.UUID;
 
 /**
@@ -37,150 +35,117 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 
 	private final ObjectMapper objectMapper;
 	private final EmbeddableMappingType embeddableMappingType;
-	private static final Class OsonParserKlass;
-	private static Method OsonParserKlassCurrentEventMethod = null;
-	private static Method OsonParserKlassGetLocalDateTimeMethod = null;
-	private static Method OsonParserKlassReadDurationMethod = null;
-	private static Method OsonParserKlassReadOffsetDateTimeMethod = null;
-		static {
-	try {
-
-		OsonParserKlass = JacksonOsonFormatMapper.class.getClassLoader()
-				.loadClass( "oracle.jdbc.provider.oson.OsonParser" );
-		OsonParserKlassCurrentEventMethod = OsonParserKlass.getMethod( "currentOsonEvent" );
-		OsonParserKlassGetLocalDateTimeMethod = OsonParserKlass.getMethod( "getLocalDateTime" );
-		OsonParserKlassReadDurationMethod = OsonParserKlass.getMethod( "readDuration" );
-		OsonParserKlassReadOffsetDateTimeMethod = OsonParserKlass.getMethod("readOffsetDateTime" );
-	}
-	catch (ClassNotFoundException | LinkageError | NoSuchMethodException e) {
-		// should not happen as OracleOsonJacksonJdbcType is loaded
-		// only when Oracle OSON JDBC extension is present
-		// see OracleDialect class.
-		throw new ExceptionInInitializerError(
-				"JacksonOsonFormatMapper class loaded without OSON extension: " + e.getClass() + " " + e.getMessage() );
-	}
-}
-	public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
-		super(objectMapper);
-		this.objectMapper = objectMapper;
-		this.embeddableMappingType = embeddableMappingType;
-		this.objectMapper.setAnnotationIntrospector( new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
 
+	/**
+	 * Creates a new JacksonOsonFormatMapper
+	 * @param objectMapper the Jackson object mapper
+	 * same as JacksonOsonFormatMapper(objectMapper, null)
+	 */
+	public JacksonOsonFormatMapper(ObjectMapper objectMapper) {
+		this(objectMapper, null);
 	}
 
-	public <T> JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType, JavaType<T> javaType) {
+	/**
+	 * Creates a new JacksonOsonFormatMapper
+	 * @param objectMapper the Jackson object mapper
+	 * @param embeddableMappingType the embeddable mapping definitions
+	 */
+	public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
 		super(objectMapper);
 		this.objectMapper = objectMapper;
 		this.embeddableMappingType = embeddableMappingType;
-		this.objectMapper.setAnnotationIntrospector( new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
-
+		if (this.embeddableMappingType != null) {
+			this.objectMapper.setAnnotationIntrospector(
+					new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
+		}
 	}
 
-
-
-	private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, Object [] finalResult, EmbeddableMappingType embeddableMappingType, WrapperOptions options)
+	/**
+	 * Process OSON parser tokens
+	 * @param osonParser the OSON parser
+	 * @param currentEvent the current of the parser
+	 * @param finalResult the populated object array
+	 * @param embeddableMappingType the embeddable mapping definitions
+	 * @param options the wrapping options
+	 * @throws IOException
+	 */
+	private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Event currentEvent, Object [] finalResult, EmbeddableMappingType embeddableMappingType, WrapperOptions options)
 			throws IOException {
 
-		JsonToken token = currentToken;
+		OracleJsonParser.Event event = currentEvent;
 
 		int selectableIndex = -1;
 		SelectableMapping mapping = null;
+		String currentKeyName = null;
 		while ( true ) {
-			if ( token == null ) {
+			if ( event == null ) {
 				break;
 			}
-			switch ( token ) {
-				case JsonToken.START_ARRAY:
+			switch ( event ) {
+				case OracleJsonParser.Event.KEY_NAME:
+					currentKeyName = osonParser.getString();
+					selectableIndex = embeddableMappingType.getSelectableIndex( currentKeyName);
+					if (selectableIndex >= 0) {
+						// we may not have a selectable mapping for that key
+						mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
+					}
+					break;
+				case OracleJsonParser.Event.START_ARRAY:
+				case OracleJsonParser.Event.END_ARRAY:
 					int i = 0;
 					break;
-				case JsonToken.VALUE_STRING:
-					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
-					//selectableIndex = embeddableMappingType.getSelectableIndex(currentFieldName );
-					//AttributeMapping am = embeddableMappingType.findAttributeMapping( currentFieldName );
-					assert selectableIndex >= 0;
-					mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
-					//result.add(osonParser.getText());
-					//					result.add(mapping.getJdbcMapping().convertToDomainValue(
-					//							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getText(), options ) ));
-
-					try {
-						OracleJsonParser.Event event =
-								(OracleJsonParser.Event)OsonParserKlassCurrentEventMethod.invoke( osonParser );
-						switch(event) {
-							case OracleJsonParser.Event.VALUE_DATE :
-									LocalDateTime localDate =
-										(LocalDateTime)OsonParserKlassGetLocalDateTimeMethod.invoke( osonParser );
-									finalResult[selectableIndex] =  java.sql.Date.valueOf(localDate.toLocalDate());
-								break;
-								case OracleJsonParser.Event.VALUE_TIMESTAMP:
-									LocalDateTime local =
-											(LocalDateTime)OsonParserKlassGetLocalDateTimeMethod.invoke( osonParser );
-									if ("java.sql.Timestamp".equals(
-											embeddableMappingType.getJdbcValueSelectable( selectableIndex )
-													.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName())) {
-										finalResult[selectableIndex] = Timestamp.valueOf( local );
-									} else {
-										finalResult[selectableIndex] = local;
-									}
-									break;
-								case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
-									OffsetDateTime offsetDT =
-											(OffsetDateTime)OsonParserKlassReadOffsetDateTimeMethod.invoke( osonParser );
-									finalResult[selectableIndex] = offsetDT;
-									break;
-								case OracleJsonParser.Event.VALUE_INTERVALDS:
-									Duration duration = (Duration) OsonParserKlassReadDurationMethod.invoke(osonParser);
-									finalResult[selectableIndex] = duration;
-									break;
-								case OracleJsonParser.Event.VALUE_INTERVALYM:
-									//break;  TODO should not be like that
-								default :
-//									finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-//											mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() ) );
-//									Object f1 = mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() );
-//									Object f2 = mapping.getJdbcMapping().convertToDomainValue(mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() ));
-//									String s1 = osonParser.getText();
-//									Object o1 = mapping.getJdbcMapping().convertToDomainValue( osonParser.getText()  );
-//									Object o2 = mapping.getJdbcMapping().convertToRelationalValue( osonParser.getText()  );
-//									finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue( osonParser.getText()  );
-									finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getText() );
-							}
-					}
-					catch (Exception e) {
-						throw new IOException( e );
+				case OracleJsonParser.Event.VALUE_DATE :
+					LocalDateTime localDate = osonParser.getLocalDateTime();
+					finalResult[selectableIndex] =  java.sql.Date.valueOf(localDate.toLocalDate());
+					break;
+				case OracleJsonParser.Event.VALUE_TIMESTAMP:
+					LocalDateTime local = osonParser.getLocalDateTime();
+					if ("java.sql.Timestamp".equals(
+							embeddableMappingType.getJdbcValueSelectable( selectableIndex )
+									.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName())) {
+						finalResult[selectableIndex] = Timestamp.valueOf( local );
+					} else {
+						finalResult[selectableIndex] = local;
 					}
 					break;
-				case JsonToken.VALUE_TRUE:
-					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
+				case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
+					finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getOffsetDateTime(), options ) );
+					break;
+				case OracleJsonParser.Event.VALUE_INTERVALDS:
+				case OracleJsonParser.Event.VALUE_INTERVALYM:
+					Duration duration = osonParser.getDuration();
+					finalResult[selectableIndex] = duration;
+					break;
+				case OracleJsonParser.Event.VALUE_STRING:
+					finalResult[selectableIndex] =
+							mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getString() );
+					break;
+				case OracleJsonParser.Event.VALUE_TRUE:
 					finalResult[selectableIndex] = Boolean.TRUE;
 					break;
-				case JsonToken.VALUE_FALSE:
-					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
+				case OracleJsonParser.Event.VALUE_FALSE:
 					finalResult[selectableIndex] = Boolean.FALSE;
 					break;
-				case JsonToken.VALUE_NULL:
-					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName());
+				case OracleJsonParser.Event.VALUE_NULL:
 					finalResult[selectableIndex] = null;
 					break;
-				case JsonToken.VALUE_NUMBER_INT:
-					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
-					//selectableIndex = embeddableMappingType.getSelectableIndex(currentFieldName );
-					assert selectableIndex >= 0;
-					mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
-					finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-						mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getIntValue(), options ) );
+				case OracleJsonParser.Event.VALUE_DECIMAL:
+					if (osonParser.isIntegralNumber()) {
+						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getInt(), options ) );
+					} else {
+						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
+					}
 					break;
-				case JsonToken.VALUE_NUMBER_FLOAT:
-					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
-					//selectableIndex = embeddableMappingType.getSelectableIndex(currentFieldName );
-					assert selectableIndex >= 0;
-					mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
+				case OracleJsonParser.Event.VALUE_DOUBLE:
+				case OracleJsonParser.Event.VALUE_FLOAT:
 					finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloatValue(), options ) );
+							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
 					break;
-				case JsonToken.VALUE_EMBEDDED_OBJECT:
-					selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
-					byte[] bytes = osonParser.getBinaryValue();
+				case OracleJsonParser.Event.VALUE_BINARY:
+					byte[] bytes = osonParser.getBytes();
 					if ("java.util.UUID".equals(
 							embeddableMappingType.getJdbcValueSelectable( selectableIndex )
 									.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName())) {
@@ -192,15 +157,15 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 						finalResult[selectableIndex] = bytes;
 					}
 					break;
-				case JsonToken.START_OBJECT:
-					if ( osonParser.currentName() == null ) {
+				case OracleJsonParser.Event.START_OBJECT:
+					if ( currentKeyName == null ) {
 						// that's the root
-						consumeValuedToken( osonParser, osonParser.nextToken(), finalResult,
+						consumeOsonTokens( osonParser, osonParser.next(), finalResult,
 								embeddableMappingType,
 								options  );
 					}
 					else {
-						selectableIndex = embeddableMappingType.getSelectableIndex( osonParser.currentName() );
+						selectableIndex = embeddableMappingType.getSelectableIndex( currentKeyName );
 						if ( selectableIndex != -1 ) {
 							final SelectableMapping selectable = embeddableMappingType.getJdbcValueSelectable(
 									selectableIndex );
@@ -208,29 +173,40 @@ private void consumeValuedToken(JsonParser osonParser, JsonToken currentToken, O
 									.getJdbcType();
 							final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
 							finalResult[selectableIndex] = new Object[subMappingType.getJdbcValueCount()];
-							consumeValuedToken( osonParser, osonParser.nextToken(),
+							consumeOsonTokens( osonParser, osonParser.next(),
 									(Object[]) finalResult[selectableIndex],
 									subMappingType,
 									options );
 						}
 					}
 					break;
-				case JsonToken.END_OBJECT:
+				case OracleJsonParser.Event.END_OBJECT:
 					return;
+				default:
+					throw new IOException("Unknown OSON event " + event);
+
 			}
-			token = osonParser.nextToken();
+			event = osonParser.next();
 		}
 
 	}
 
+	/**
+	 * Consumes OSON bytes and populate an Object array as described in the embeddable mapping definitions.
+	 * @param embeddableMappingType the embeddable mapping definitions
+	 * @param source the OSON bytes as <code>byte[]</code>
+	 * @param options the wrapping options
+	 * @return the Object array
+	 * @param <T> return type i.e object array
+	 * @throws IOException OSON parsing has failed
+	 */
 	public <T> T readToArray(EmbeddableMappingType embeddableMappingType, Object source, WrapperOptions options) throws IOException {
-		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
 		Object []finalResult = new Object[embeddableMappingType.getJdbcValueCount()];
-		consumeValuedToken(osonParser, osonParser.nextToken(), finalResult, embeddableMappingType, options);
+		OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( ByteBuffer.wrap( (byte[])source ) );
+		consumeOsonTokens(osonParser, osonParser.next(), finalResult, embeddableMappingType, options);
 		return (T)finalResult;
 	}
 
-
 	@Override
 	public <T> T fromString(CharSequence charSequence, Type type) {
 		try {

From 02f52ea09280b6c87d2d8cd43123eac75996555c Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 18 Dec 2024 23:47:43 +0100
Subject: [PATCH 08/81] HHH-17404 : implement array csae in embeddable
 deserialization

---
 .../dialect/OracleOsonJacksonJdbcType.java    |   2 +-
 .../jackson/JacksonOsonFormatMapper.java      | 196 +++++++++++++-----
 2 files changed, 145 insertions(+), 53 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index e1ccdab50a2e..d189d3633668 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -147,7 +147,7 @@ private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 					// an array of objects. We use JsonParser to fetch values
 					// and build the array.(as opposed to let Jackson do it as we do not
 					// have a proper object definition at that stage).
-					return ((JacksonOsonFormatMapper)mapper).readToArray(
+					return ((JacksonOsonFormatMapper)mapper).toObjectArray(
 							getEmbeddableMappingType(), osonBytes, options );
 				}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 71edacba1234..38d77329fb3f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -13,21 +13,24 @@
 import oracle.sql.json.OracleJsonParser;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.mapping.SelectableMapping;
+import org.hibernate.type.BasicPluralType;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.UUIDJavaType;
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.nio.ByteBuffer;
+import java.sql.Date;
 import java.sql.Timestamp;
-import java.time.Duration;
 import java.time.LocalDateTime;
-import java.util.UUID;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @author Emmanuel Jannetti
- * @auther Bidyadhar Mohanty
+ * @author Bidyadhar Mohanty
  */
 public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 
@@ -60,6 +63,7 @@ public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType
 		}
 	}
 
+
 	/**
 	 * Process OSON parser tokens
 	 * @param osonParser the OSON parser
@@ -67,7 +71,7 @@ public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType
 	 * @param finalResult the populated object array
 	 * @param embeddableMappingType the embeddable mapping definitions
 	 * @param options the wrapping options
-	 * @throws IOException
+	 * @throws IOException error while reading from underlying parser
 	 */
 	private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Event currentEvent, Object [] finalResult, EmbeddableMappingType embeddableMappingType, WrapperOptions options)
 			throws IOException {
@@ -77,84 +81,174 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 		int selectableIndex = -1;
 		SelectableMapping mapping = null;
 		String currentKeyName = null;
-		while ( true ) {
-			if ( event == null ) {
-				break;
-			}
+		List<Object> subArrayList = null;
+		BasicPluralType<?, ?> pluralType = null;
+		while ( event != null ) {
 			switch ( event ) {
 				case OracleJsonParser.Event.KEY_NAME:
 					currentKeyName = osonParser.getString();
-					selectableIndex = embeddableMappingType.getSelectableIndex( currentKeyName);
-					if (selectableIndex >= 0) {
+					selectableIndex = embeddableMappingType.getSelectableIndex( currentKeyName );
+					if ( selectableIndex >= 0 ) {
 						// we may not have a selectable mapping for that key
 						mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
 					}
 					break;
 				case OracleJsonParser.Event.START_ARRAY:
+					// initialize array to gather values
+					subArrayList = new ArrayList<>();
+					assert (mapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
+							: "Array event received for non plural type";
+					// initialize array's element type
+					pluralType = (BasicPluralType<?, ?>) mapping.getJdbcMapping();
+					break;
 				case OracleJsonParser.Event.END_ARRAY:
-					int i = 0;
+					assert (subArrayList != null && pluralType != null) : "Wrong event ordering";
+					// flush array values
+					finalResult[selectableIndex] = pluralType.getJdbcJavaType().wrap( subArrayList, options );
+					// reset until we encounter next array elem
+					subArrayList = null;
+					pluralType = null;
 					break;
-				case OracleJsonParser.Event.VALUE_DATE :
+				case OracleJsonParser.Event.VALUE_DATE:
 					LocalDateTime localDate = osonParser.getLocalDateTime();
-					finalResult[selectableIndex] =  java.sql.Date.valueOf(localDate.toLocalDate());
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( Date.valueOf( localDate.toLocalDate() ) );
+					}
+					else {
+						finalResult[selectableIndex] = Date.valueOf( localDate.toLocalDate() );
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_TIMESTAMP:
 					LocalDateTime local = osonParser.getLocalDateTime();
-					if ("java.sql.Timestamp".equals(
+					Object theOne;
+					if ( "java.sql.Timestamp".equals(
 							embeddableMappingType.getJdbcValueSelectable( selectableIndex )
-									.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName())) {
-						finalResult[selectableIndex] = Timestamp.valueOf( local );
-					} else {
-						finalResult[selectableIndex] = local;
+									.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() ) ) {
+						theOne = Timestamp.valueOf( local );
+					}
+					else {
+						theOne = local;
+					}
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( theOne );
+					}
+					else {
+						finalResult[selectableIndex] = theOne;
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
-					finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getOffsetDateTime(), options ) );
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( osonParser.getOffsetDateTime() );
+					}
+					else {
+						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+								mapping.getJdbcMapping().getJdbcJavaType()
+										.wrap( osonParser.getOffsetDateTime(), options ) );
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_INTERVALDS:
 				case OracleJsonParser.Event.VALUE_INTERVALYM:
-					Duration duration = osonParser.getDuration();
-					finalResult[selectableIndex] = duration;
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( osonParser.getDuration() );
+					}
+					else {
+						finalResult[selectableIndex] = osonParser.getDuration();
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_STRING:
-					finalResult[selectableIndex] =
-							mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getString() );
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add(
+								pluralType.getElementType().getJdbcJavaType().fromString( osonParser.getString() ) );
+					}
+					else {
+						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType()
+								.fromString( osonParser.getString() );
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_TRUE:
-					finalResult[selectableIndex] = Boolean.TRUE;
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( Boolean.TRUE );
+					}
+					else {
+						finalResult[selectableIndex] = Boolean.TRUE;
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_FALSE:
-					finalResult[selectableIndex] = Boolean.FALSE;
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( Boolean.FALSE );
+					}
+					else {
+						finalResult[selectableIndex] = Boolean.FALSE;
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_NULL:
-					finalResult[selectableIndex] = null;
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( null );
+					}
+					else {
+						finalResult[selectableIndex] = null;
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_DECIMAL:
-					if (osonParser.isIntegralNumber()) {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getInt(), options ) );
-					} else {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( osonParser.isIntegralNumber() ? osonParser.getInt() : osonParser.getFloat() );
+					}
+					else {
+						// not array case: wrap value directly
+						if ( osonParser.isIntegralNumber() ) {
+							theOne = mapping.getJdbcMapping().convertToDomainValue(
+									mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getInt(), options ) );
+						}
+						else {
+							theOne = mapping.getJdbcMapping().convertToDomainValue(
+									mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
+						}
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_DOUBLE:
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( osonParser.getDouble() );
+					}
+					else {
+						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getDouble(), options ) );
+					}
+					break;
 				case OracleJsonParser.Event.VALUE_FLOAT:
-					finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-							mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( osonParser.getFloat() );
+					}
+					else {
+						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
+								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
+					}
 					break;
 				case OracleJsonParser.Event.VALUE_BINARY:
 					byte[] bytes = osonParser.getBytes();
-					if ("java.util.UUID".equals(
-							embeddableMappingType.getJdbcValueSelectable( selectableIndex )
-									.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName())) {
-						ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
-						long mostSignificantBits = byteBuffer.getLong();
-						long leastSignificantBits = byteBuffer.getLong();
-						finalResult[selectableIndex] = new UUID(mostSignificantBits, leastSignificantBits);
-					} else {
-						finalResult[selectableIndex] = bytes;
+					if ( "java.util.UUID".equals(
+							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() ) ) {
+						theOne = UUIDJavaType.INSTANCE.wrap( osonParser.getBytes(), options );
+					}
+					else {
+						theOne = bytes;
+					}
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( theOne );
+					}
+					else {
+						finalResult[selectableIndex] = theOne;
 					}
 					break;
 				case OracleJsonParser.Event.START_OBJECT:
@@ -162,7 +256,7 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 						// that's the root
 						consumeOsonTokens( osonParser, osonParser.next(), finalResult,
 								embeddableMappingType,
-								options  );
+								options );
 					}
 					else {
 						selectableIndex = embeddableMappingType.getSelectableIndex( currentKeyName );
@@ -183,14 +277,15 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 				case OracleJsonParser.Event.END_OBJECT:
 					return;
 				default:
-					throw new IOException("Unknown OSON event " + event);
+					throw new IOException( "Unknown OSON event " + event );
 
 			}
-			event = osonParser.next();
+			event = osonParser.hasNext() ? osonParser.next() : null;
 		}
 
 	}
 
+
 	/**
 	 * Consumes OSON bytes and populate an Object array as described in the embeddable mapping definitions.
 	 * @param embeddableMappingType the embeddable mapping definitions
@@ -200,7 +295,7 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 	 * @param <T> return type i.e object array
 	 * @throws IOException OSON parsing has failed
 	 */
-	public <T> T readToArray(EmbeddableMappingType embeddableMappingType, Object source, WrapperOptions options) throws IOException {
+	public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object source, WrapperOptions options) throws IOException {
 		Object []finalResult = new Object[embeddableMappingType.getJdbcValueCount()];
 		OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( ByteBuffer.wrap( (byte[])source ) );
 		consumeOsonTokens(osonParser, osonParser.next(), finalResult, embeddableMappingType, options);
@@ -238,9 +333,6 @@ public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, Wrap
 	@Override
 	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
 		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
-
-		T t = objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
-
-		return t;
+		return  objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
 	}
 }

From e85f51b34cecbee046ad67f0fbb30c087075e0b4 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Thu, 19 Dec 2024 08:51:52 +0100
Subject: [PATCH 09/81] HHH-17404 : fix NPE while parsing DECIMAL

---
 .../format/jackson/JacksonOsonFormatMapper.java   | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 38d77329fb3f..90ba1760a845 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -110,13 +110,21 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 					pluralType = null;
 					break;
 				case OracleJsonParser.Event.VALUE_DATE:
-					LocalDateTime localDate = osonParser.getLocalDateTime();
+					LocalDateTime localDateTime = osonParser.getLocalDateTime();
+					// check
+//					if (date) {
+//						finalResult[selectableIndex] = Date.valueOf( localDateTime.toLocalDate() );
+//					}
+//					else if(LocalDate)
+//					{
+//						finalResult[selectableIndex]= localDateTime.toLocalDate();
+//					}
 					if ( pluralType != null ) {
 						// dealing with arrays
-						subArrayList.add( Date.valueOf( localDate.toLocalDate() ) );
+						subArrayList.add( Date.valueOf( localDateTime.toLocalDate() ) );
 					}
 					else {
-						finalResult[selectableIndex] = Date.valueOf( localDate.toLocalDate() );
+						finalResult[selectableIndex] = Date.valueOf( localDateTime.toLocalDate() );
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_TIMESTAMP:
@@ -212,6 +220,7 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 							theOne = mapping.getJdbcMapping().convertToDomainValue(
 									mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
 						}
+						finalResult[selectableIndex] = theOne;
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_DOUBLE:

From efbe98bfe108e81281bf15fdb5ac2194f94ef37b Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Thu, 19 Dec 2024 17:58:47 +0100
Subject: [PATCH 10/81] HHH-17404 : fix dates and URLs

---
 .../dialect/OracleOsonJacksonJdbcType.java    | 15 +++---
 .../jackson/JacksonOsonFormatMapper.java      | 47 +++++++++++--------
 2 files changed, 34 insertions(+), 28 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index d189d3633668..747a80ec5541 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -6,6 +6,7 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import oracle.jdbc.OracleType;
 import oracle.sql.json.OracleJsonDatum;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
@@ -19,9 +20,7 @@
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.CallableStatement;
@@ -79,7 +78,7 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 		return new BasicBinder<>( javaType, this ) {
 
-			private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
+			private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
 
 				// TODO : We should rely on
 				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
@@ -88,19 +87,17 @@ private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions opt
 				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType());
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
-				//TODO : really use streams
 				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
-				osonGen.close();
-				ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
-				return in;
+				osonGen.close(); // until now
+				return out.toByteArray();
 			}
 
 			@Override
 			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setBinaryStream( index, toOson( value, getJavaType(), options ) );
+					st.setObject( index, toOson( value, getJavaType(), options ), OracleType.JSON);
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -111,7 +108,7 @@ protected void doBind(PreparedStatement st, X value, int index, WrapperOptions o
 			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setBinaryStream( name, toOson( value, getJavaType(), options ) );
+					st.setObject( name, toOson( value, getJavaType(), options ), OracleType.JSON);
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 90ba1760a845..b208ba0f9302 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -83,6 +83,7 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 		String currentKeyName = null;
 		List<Object> subArrayList = null;
 		BasicPluralType<?, ?> pluralType = null;
+		Object theOne;
 		while ( event != null ) {
 			switch ( event ) {
 				case OracleJsonParser.Event.KEY_NAME:
@@ -111,28 +112,31 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 					break;
 				case OracleJsonParser.Event.VALUE_DATE:
 					LocalDateTime localDateTime = osonParser.getLocalDateTime();
-					// check
-//					if (date) {
-//						finalResult[selectableIndex] = Date.valueOf( localDateTime.toLocalDate() );
-//					}
-//					else if(LocalDate)
-//					{
-//						finalResult[selectableIndex]= localDateTime.toLocalDate();
-//					}
+					Class underlyingType = null;
+					if(pluralType!=null) {
+						underlyingType = pluralType.getElementType().getJavaType();
+					} else {
+						underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
+					}
+					if (java.sql.Date.class.isAssignableFrom( underlyingType )) {
+						theOne = Date.valueOf( localDateTime.toLocalDate());
+					} else if (java.time.LocalDate.class.isAssignableFrom( underlyingType )) {
+						theOne = localDateTime.toLocalDate();
+					} else {
+						throw new IllegalArgumentException("unexpected date type " + underlyingType);
+					}
 					if ( pluralType != null ) {
 						// dealing with arrays
-						subArrayList.add( Date.valueOf( localDateTime.toLocalDate() ) );
+						subArrayList.add( theOne );
 					}
 					else {
-						finalResult[selectableIndex] = Date.valueOf( localDateTime.toLocalDate() );
+						finalResult[selectableIndex] = theOne;
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_TIMESTAMP:
 					LocalDateTime local = osonParser.getLocalDateTime();
-					Object theOne;
 					if ( "java.sql.Timestamp".equals(
-							embeddableMappingType.getJdbcValueSelectable( selectableIndex )
-									.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() ) ) {
+							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() ) ) {
 						theOne = Timestamp.valueOf( local );
 					}
 					else {
@@ -174,8 +178,7 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 								pluralType.getElementType().getJdbcJavaType().fromString( osonParser.getString() ) );
 					}
 					else {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType()
-								.fromString( osonParser.getString() );
+						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getString(),options );
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_TRUE:
@@ -244,14 +247,20 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_BINARY:
-					byte[] bytes = osonParser.getBytes();
-					if ( "java.util.UUID".equals(
-							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() ) ) {
+					if(pluralType!=null) {
+						underlyingType = pluralType.getElementType().getJavaType();
+					}
+					else {
+						underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
+					}
+
+					if (java.util.UUID.class.isAssignableFrom( underlyingType ))  {
 						theOne = UUIDJavaType.INSTANCE.wrap( osonParser.getBytes(), options );
 					}
 					else {
-						theOne = bytes;
+						theOne = osonParser.getBytes();
 					}
+
 					if ( pluralType != null ) {
 						// dealing with arrays
 						subArrayList.add( theOne );

From 8652de996553b29feb2b4d9d8c73513bcd6db3ad Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Fri, 20 Dec 2024 14:28:42 +0100
Subject: [PATCH 11/81] HHH-17404 : fix handle of VALUE_INTERVALYM

---
 .../type/format/jackson/JacksonOsonFormatMapper.java | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index b208ba0f9302..948ff79acd6b 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -162,15 +162,25 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_INTERVALDS:
-				case OracleJsonParser.Event.VALUE_INTERVALYM:
 					if ( pluralType != null ) {
 						// dealing with arrays
 						subArrayList.add( osonParser.getDuration() );
 					}
 					else {
+						// TODO: shall I use mapping.getJdbcMapping().getJdbcJavaType().wrap(...) ?
 						finalResult[selectableIndex] = osonParser.getDuration();
 					}
 					break;
+				case OracleJsonParser.Event.VALUE_INTERVALYM:
+					if ( pluralType != null ) {
+						// dealing with arrays
+						subArrayList.add( osonParser.getPeriod() );
+					}
+					else {
+						// TODO: shall I use mapping.getJdbcMapping().getJdbcJavaType().wrap(...) ?
+						finalResult[selectableIndex] = osonParser.getPeriod();
+					}
+					break;
 				case OracleJsonParser.Event.VALUE_STRING:
 					if ( pluralType != null ) {
 						// dealing with arrays

From 3ef01dce0504d825cf941d2eb168c97da043a003 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Mon, 6 Jan 2025 14:02:28 +0530
Subject: [PATCH 12/81] HHH-17404: Add serializer for Embeddable and
 OracleDurationJdbcType

---
 .../org/hibernate/dialect/OracleDialect.java  |   3 +
 .../dialect/OracleDurationJdbcType.java       |  76 +++-
 .../dialect/OracleOsonJacksonJdbcType.java    |   3 +
 .../aggregate/OracleAggregateSupport.java     |  43 +-
 .../jackson/JacksonOsonFormatMapper.java      | 421 +++++++++++++++++-
 5 files changed, 522 insertions(+), 24 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 10586d03cac4..738fab6ff7b3 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -142,6 +142,7 @@
 import static org.hibernate.type.SqlTypes.DATE;
 import static org.hibernate.type.SqlTypes.DECIMAL;
 import static org.hibernate.type.SqlTypes.DOUBLE;
+import static org.hibernate.type.SqlTypes.DURATION;
 import static org.hibernate.type.SqlTypes.FLOAT;
 import static org.hibernate.type.SqlTypes.GEOMETRY;
 import static org.hibernate.type.SqlTypes.INTEGER;
@@ -851,6 +852,7 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
 		}
 		// We need the DDL type during runtime to produce the proper encoding in certain functions
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( BIT, "number(1,0)", this ) );
+		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( DURATION, "interval day to second", this ) );
 	}
 
 	@Override
@@ -1021,6 +1023,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 				// We must check that that extension is available and actually used.
 				typeContributions.contributeJdbcType( OracleOsonJacksonJdbcType.INSTANCE );
 				typeContributions.contributeJdbcTypeConstructor( OracleOsonArrayJdbcTypeConstructor.INSTANCE );
+
 				DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
 						"Oracle OSON Jackson extension used" );
 				// as we speak this is not supported by OSON extension
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
index dfbf38ab4140..b6a9323aaebb 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -5,20 +5,84 @@
 package org.hibernate.dialect;
 
 import org.hibernate.type.SqlTypes;
-import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
+import org.hibernate.type.descriptor.ValueBinder;
+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.BasicBinder;
+import org.hibernate.type.descriptor.jdbc.BasicExtractor;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
 
-import java.sql.Types;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.Duration;
 
-public class OracleDurationJdbcType extends VarcharJdbcType {
+public class OracleDurationJdbcType implements JdbcType {
 
 	public static final OracleDurationJdbcType INSTANCE = new OracleDurationJdbcType();
 
-	public OracleDurationJdbcType() {
+	public OracleDurationJdbcType() {}
+
+	@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 {
+				st.setObject( index, javaType.unwrap( value, Duration.class, options ) );
+			}
+
+			@Override
+			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
+					throws SQLException {
+				st.setObject( name, javaType.unwrap( value, Duration.class, options ) );
+			}
+
+
+		};
 	}
 
 	@Override
-	public int getDdlTypeCode() {
-		return Types.VARCHAR;
+	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
+		return new BasicExtractor<>( javaType, this ) {
+			@Override
+			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
+				// Handle the fact that a duration could also come as number of nanoseconds
+				final Object nativeValue = rs.getObject( paramIndex );
+				if ( nativeValue instanceof Number ) {
+					return javaType.wrap( nativeValue, options );
+				}
+				return javaType.wrap( rs.getObject( paramIndex, Duration.class ), options );
+			}
+
+			@Override
+			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
+				// Handle the fact that a duration could also come as number of nanoseconds
+				final Object nativeValue = statement.getObject( index );
+				if ( nativeValue instanceof Number ) {
+					return javaType.wrap( nativeValue, options );
+				}
+				return javaType.wrap( statement.getObject( index, Duration.class ), options );
+			}
+
+			@Override
+			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
+					throws SQLException {
+				// Handle the fact that a duration could also come as number of nanoseconds
+				final Object nativeValue = statement.getObject( name );
+				if ( nativeValue instanceof Number ) {
+					return javaType.wrap( nativeValue, options );
+				}
+				return javaType.wrap( statement.getObject( name, Duration.class ), options );
+			}
+		};
+	}
+
+	@Override
+	public int getJdbcTypeCode() {
+		return SqlTypes.DURATION;
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 747a80ec5541..98112a8b291e 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -86,6 +86,9 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 				//     But this do not let use inject our ObjectMapper. For now create our own instance
 				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType());
 
+				if(getEmbeddableMappingType()!= null) {
+					return ((JacksonOsonFormatMapper)mapper).toOson(value,javaType,options);
+				}
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
 				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index 23f6a81a1ee8..bea29441e55b 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -22,6 +22,7 @@
 import org.hibernate.metamodel.mapping.SelectableMapping;
 import org.hibernate.metamodel.mapping.SelectablePath;
 import org.hibernate.metamodel.mapping.SqlTypedMapping;
+import org.hibernate.query.sqm.CastType;
 import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
 import org.hibernate.sql.ast.SqlAstTranslator;
 import org.hibernate.sql.ast.spi.SqlAppender;
@@ -88,7 +89,7 @@ public static AggregateSupport valueOf(Dialect dialect, boolean useDateStoredAsS
 			default -> version.isSameOrAfter( 23 )
 				? useDateStoredAsString?OracleAggregateSupport.V23_INSTANCE:
 				OracleAggregateSupport.V23_OSON_EXT_INSTANCE
-				: OracleAggregateSupport.LEGACY_INSTANCE;
+                                : OracleAggregateSupport.LEGACY_INSTANCE;
 		};
 	}
 
@@ -139,10 +140,22 @@ public String aggregateComponentCustomReadExpression(
 							case BIGINT:
 							case CLOB:
 							case NCLOB:
-								return template.replace(
-										placeholder,
-										"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
-								);
+								CastType castType = column.getJdbcMapping().getCastType();
+								switch ( castType ) {
+									case INTEGER_BOOLEAN:
+										if (!dateTypesStoreAsString) {
+											return template.replace(
+													placeholder,
+													"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0,null)"
+											);
+										}
+									default:
+										return template.replace(
+												placeholder,
+												"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
+										);
+								}
+
 							case DATE:
 								if (this.dateTypesStoreAsString) {
 									return template.replace(
@@ -244,10 +257,22 @@ public String aggregateComponentCustomReadExpression(
 										"json_query(" + parentPartExpression + columnExpression + "' returning " + jsonTypeName + ")"
 								);
 							default:
-								return template.replace(
-										placeholder,
-										"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getColumnDefinition() + ')'
-								);
+								CastType castTypeCharBoolean = column.getJdbcMapping().getCastType();
+								switch ( castTypeCharBoolean ){
+									case YN_BOOLEAN:
+										if (!dateTypesStoreAsString) {
+											return template.replace(
+													placeholder,
+													"decode(json_value(" + parentPartExpression + columnExpression + "'),'true','Y','false','N',null)"
+											);
+										}
+									default:
+										return template.replace(
+												placeholder,
+												"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getColumnDefinition() + ')'
+										);
+								}
+
 						}
 					case NONE:
 						throw new UnsupportedOperationException( "The Oracle version doesn't support JSON aggregates!" );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 948ff79acd6b..cdc5b7b8ce49 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -7,26 +7,67 @@
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
+import com.fasterxml.jackson.databind.ser.PropertyWriter;
+import oracle.jdbc.driver.json.tree.OracleJsonDateImpl;
+import oracle.jdbc.driver.json.tree.OracleJsonTimestampImpl;
+import oracle.sql.DATE;
+import oracle.sql.TIMESTAMP;
+import oracle.sql.json.OracleJsonDate;
 import oracle.sql.json.OracleJsonFactory;
+import oracle.sql.json.OracleJsonGenerator;
 import oracle.sql.json.OracleJsonParser;
+import oracle.sql.json.OracleJsonTimestamp;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.mapping.SelectableMapping;
+import org.hibernate.metamodel.mapping.ValuedModelPart;
+import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
+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.converter.spi.BasicValueConverter;
+import org.hibernate.type.descriptor.java.BasicPluralJavaType;
 import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.JdbcDateJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
 import org.hibernate.type.descriptor.java.UUIDJavaType;
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
+import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.lang.reflect.Array;
 import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.Time;
 import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.Instant;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.logging.Level;
+
+import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
 
 /**
  * @author Emmanuel Jannetti
@@ -39,6 +80,9 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 	private final ObjectMapper objectMapper;
 	private final EmbeddableMappingType embeddableMappingType;
 
+	// fields/Methods to retrieve serializer data
+
+
 	/**
 	 * Creates a new JacksonOsonFormatMapper
 	 * @param objectMapper the Jackson object mapper
@@ -57,10 +101,6 @@ public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType
 		super(objectMapper);
 		this.objectMapper = objectMapper;
 		this.embeddableMappingType = embeddableMappingType;
-		if (this.embeddableMappingType != null) {
-			this.objectMapper.setAnnotationIntrospector(
-					new JacksonJakartaAnnotationIntrospector( this.embeddableMappingType ) );
-		}
 	}
 
 
@@ -139,6 +179,14 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() ) ) {
 						theOne = Timestamp.valueOf( local );
 					}
+					else if ( "java.time.LocalTime".equals(
+							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() )) {
+						theOne = local.toLocalTime();
+					}
+					else if ( "java.sql.Time".equals(
+							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() )) {
+						theOne = Time.valueOf( local.toLocalTime() );
+					}
 					else {
 						theOne = local;
 					}
@@ -188,7 +236,9 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 								pluralType.getElementType().getJdbcJavaType().fromString( osonParser.getString() ) );
 					}
 					else {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getString(),options );
+//						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getString(),options );
+//						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue( mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getString(),options ));
+						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().fromEncodedString( osonParser.getString(),0,osonParser.getString().length() );
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_TRUE:
@@ -225,11 +275,20 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 					}
 					else {
 						// not array case: wrap value directly
+
 						if ( osonParser.isIntegralNumber() ) {
-							theOne = mapping.getJdbcMapping().convertToDomainValue(
-									mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getInt(), options ) );
+							if("java.lang.Double".equals( mapping.getJdbcMapping().getJdbcJavaType().getTypeName() ) ) {
+								theOne = mapping.getJdbcMapping().convertToDomainValue(
+										mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getDouble(), options ) );
+
+							} else {
+								theOne = mapping.getJdbcMapping().convertToDomainValue(
+										mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getInt(), options ) );
+
+							}
 						}
 						else {
+
 							theOne = mapping.getJdbcMapping().convertToDomainValue(
 									mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
 						}
@@ -350,12 +409,353 @@ public <T> String toString(T value, Type type) {
 		}
 	}
 
+
+	public <X>byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options) {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
+		serializetoOsonApproach2( value,generator,javaType,options);
+		generator.close();
+		return out.toByteArray();
+	}
+
+	private <X> void serializetoOsonApproach2(X value, OracleJsonGenerator generator, JavaType<X> javaType, WrapperOptions options) {
+		generator.writeStartObject();
+		serializetoOsonApproach2Util( value, generator, javaType, options,embeddableMappingType );
+		generator.writeEnd();
+	}
+
+	private <X> void serializetoOsonApproach2Util(X value,
+												  OracleJsonGenerator generator,
+												  JavaType<X> javaType,
+												  WrapperOptions options,
+												  EmbeddableMappingType embeddableMappingType) {
+
+		final Object[] values = embeddableMappingType.getValues( value );
+		for ( int i = 0; i < values.length; i++ ) {
+			final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
+			if ( attributeMapping instanceof SelectableMapping ) {
+				final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
+				final BasicType<Object> basicType = (BasicType<Object>) attributeMapping.getMappedType();
+
+				generator.writeKey( name );
+				serializeValue( basicType.convertToRelationalValue( values[i] ), (JavaType<Object>) basicType.getJdbcJavaType(),basicType.getJdbcType(), options,generator);
+
+			}
+			else if (attributeMapping instanceof EmbeddedAttributeMapping) {
+				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
+				final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
+				if ( values[i] == null ) {
+					// Skipping the update of the separator is on purpose
+					continue;
+				}
+				if (aggregateMapping == null) {
+					// flattened case
+					serializetoOsonApproach2Util( (X) values[i],
+							generator,
+							javaType,
+							options,
+							embeddableMappingType );
+				}
+				else {
+					// non flattened case
+					final String name = aggregateMapping.getSelectableName();
+					generator.writeKey( name );
+					generator.writeStartObject();
+					serializetoOsonApproach2Util( (X) values[i],
+							generator,
+							javaType,
+							options,
+							embeddableMappingType);
+					generator.writeEnd();
+
+				}
+
+			}
+		}
+	}
+
+	private void serializeValue(Object value,
+								JavaType<Object> javaType,
+								JdbcType jdbcType,
+								WrapperOptions options,
+								OracleJsonGenerator generator) {
+		switch ( jdbcType.getDefaultSqlTypeCode() ) {
+			case SqlTypes.TINYINT:
+			case SqlTypes.SMALLINT:
+			case SqlTypes.INTEGER:
+				if ( value instanceof Boolean ) {
+					// BooleanJavaType has this as an implicit conversion
+					int i = ((Boolean) value) ? 1 : 0;
+					generator.write( i );
+					break;
+				}
+				if ( value instanceof Enum ) {
+					generator.write( ((Enum<?>) value ).ordinal() );
+					break;
+				}
+				generator.write( javaType.unwrap( value,Integer.class,options ) );
+				break;
+			case SqlTypes.BOOLEAN:
+				generator.write( (Boolean) value );
+				break;
+			case SqlTypes.BIT:
+				generator.write( (Integer) value );
+				break;
+			case SqlTypes.BIGINT:
+				generator.write( (BigInteger) value );
+				break;
+			case SqlTypes.FLOAT:
+				generator.write( (Float) value );
+				break;
+			case SqlTypes.REAL:
+			case SqlTypes.DOUBLE:
+				generator.write( (Double) value );
+				break;
+			case SqlTypes.CHAR:
+			case SqlTypes.NCHAR:
+			case SqlTypes.VARCHAR:
+			case SqlTypes.NVARCHAR:
+				if ( value instanceof Boolean ) {
+					String c = ((Boolean) value) ? "Y" : "N";
+					generator.write( c );
+					break;
+				}
+			case SqlTypes.LONGVARCHAR:
+			case SqlTypes.LONGNVARCHAR:
+			case SqlTypes.LONG32VARCHAR:
+			case SqlTypes.LONG32NVARCHAR:
+			case SqlTypes.CLOB:
+			case SqlTypes.MATERIALIZED_CLOB:
+			case SqlTypes.NCLOB:
+			case SqlTypes.MATERIALIZED_NCLOB:
+			case SqlTypes.ENUM:
+			case SqlTypes.NAMED_ENUM:
+				// correct?
+				generator.write( javaType.toString(value) );
+				break;
+			case SqlTypes.DATE:
+				DATE dd = new DATE(javaType.unwrap( value,java.sql.Date.class,options ));
+				OracleJsonDate jsonDate = new OracleJsonDateImpl(dd.shareBytes());
+				generator.write(jsonDate);
+				break;
+			case SqlTypes.TIME:
+			case SqlTypes.TIME_WITH_TIMEZONE:
+			case SqlTypes.TIME_UTC:
+				generator.write( javaType.toString(value) );
+				break;
+			case SqlTypes.TIMESTAMP:
+				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( value, Timestamp.class, options ));
+				OracleJsonTimestamp writeTimeStamp = new OracleJsonTimestampImpl(TS.shareBytes());
+				generator.write(writeTimeStamp);
+				break;
+			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
+				try {
+					OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
+					generator.write( dateTime );
+				}
+				catch (Exception e) {
+					Timestamp tswtz = javaType.unwrap( value, Timestamp.class, options );
+					TIMESTAMP TSWTZ = new TIMESTAMP(tswtz);
+					OracleJsonTimestamp writeTimeStampWTZ = new OracleJsonTimestampImpl(TSWTZ.shareBytes());
+					generator.write(writeTimeStampWTZ);
+				}
+				break;
+			case SqlTypes.TIMESTAMP_UTC:
+				if( value instanceof OffsetDateTime ) {
+					OffsetDateTime odt = javaType.unwrap( value, OffsetDateTime.class, options );
+					generator.write( odt );
+					break;
+				}
+				else if (value instanceof Instant ) {
+					Instant instant = javaType.unwrap( value, Instant.class, options );
+					generator.write(instant.atOffset( ZoneOffset.UTC )  );
+					break;
+				}
+				generator.write( javaType.toString(value) );
+				break;
+			case SqlTypes.NUMERIC:
+			case SqlTypes.DECIMAL:
+				BigDecimal bd = javaType.unwrap( value, BigDecimal.class, options );
+				generator.write( bd );
+				break;
+
+			case SqlTypes.DURATION:
+				Duration duration = javaType.unwrap( value, Duration.class, options );
+				generator.write( duration );
+				break;
+			case SqlTypes.UUID:
+				UUID uuid = javaType.unwrap( value, UUID.class, options );
+				byte[] bytes = _asBytes( uuid );
+				generator.write( bytes );
+				break;
+			case SqlTypes.BINARY:
+			case SqlTypes.VARBINARY:
+			case SqlTypes.LONGVARBINARY:
+			case SqlTypes.LONG32VARBINARY:
+			case SqlTypes.BLOB:
+			case SqlTypes.MATERIALIZED_BLOB:
+
+				break;
+			case SqlTypes.ARRAY:
+			case SqlTypes.JSON_ARRAY:
+				final int length = Array.getLength( value );
+				generator.writeStartArray();
+				if ( length != 0 ) {
+					//noinspection unchecked
+					final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) javaType ).getElementJavaType();
+					final JdbcType elementJdbcType = ( (ArrayJdbcType) jdbcType ).getElementJdbcType();
+
+					for ( int i = 0; i < length; i++ ) {
+						Object arrayElement = Array.get( value, i );
+						serializeValue( arrayElement,elementJavaType, elementJdbcType, options, generator );
+					}
+				}
+				generator.writeEnd();
+				break;
+			default:
+				throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
+		}
+
+	}
+	private byte[] _asBytes(UUID uuid)
+	{
+		byte[] buffer = new byte[16];
+		long hi = uuid.getMostSignificantBits();
+		long lo = uuid.getLeastSignificantBits();
+		_appendInt((int) (hi >> 32), buffer, 0);
+		_appendInt((int) hi, buffer, 4);
+		_appendInt((int) (lo >> 32), buffer, 8);
+		_appendInt((int) lo, buffer, 12);
+		return buffer;
+	}
+
+	private void _appendInt(int value, byte[] buffer, int offset)
+	{
+		buffer[offset] = (byte) (value >> 24);
+		buffer[++offset] = (byte) (value >> 16);
+		buffer[++offset] = (byte) (value >> 8);
+		buffer[++offset] = (byte) value;
+	}
+
+
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
+		com.fasterxml.jackson.databind.JavaType jacksonJavaType = objectMapper.constructType( javaType.getJavaType() );
+
+		if(embeddableMappingType == null ) {
+			ObjectWriter writer = objectMapper.writerFor( jacksonJavaType );
+			writer.writeValue( (JsonGenerator) target, value);
+			return;
+		}
+
+		DefaultSerializerProvider provider =((DefaultSerializerProvider)objectMapper.getSerializerProvider())
+				.createInstance( objectMapper.getSerializationConfig(),objectMapper.getSerializerFactory() );
+		JsonSerializer<Object> valueSerializer = provider
+				.findTypedValueSerializer( jacksonJavaType,true, null );
+		serializetoOson(value,valueSerializer,embeddableMappingType,(JsonGenerator)target,objectMapper,provider);
+		((JsonGenerator)target).flush();
+
+	}
+
+	private <T> void serializetoOson(T value, JsonSerializer<Object> valueSerializer, EmbeddableMappingType embeddableMappingType, JsonGenerator target, ObjectMapper objectMapper, DefaultSerializerProvider provider)
+			throws IOException {
+		target.writeStartObject();
+		serializetoOsonUtil(value,valueSerializer,embeddableMappingType,target,objectMapper,provider);
+		target.writeEndObject();
+
+	}
+
+	private <T> void serializetoOsonUtil(T value,
+										JsonSerializer<Object> valueSerializer,
+										EmbeddableMappingType embeddableMappingType,
+										JsonGenerator generator,
+										ObjectMapper mapper,
+										DefaultSerializerProvider provider) throws IOException {
+		final Object[] values = embeddableMappingType.getValues( value );
+
+		Map<String, BeanPropertyWriter> beanPropertyWriterMap = buildBeanPropertyMap(valueSerializer.properties());
+		for ( int i = 0; i < values.length; i++ ) {
+			final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
+			if ( attributeMapping instanceof SelectableMapping ) {
+				// basic attribute ??
+				final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
+
+				final BasicType<Object> basicType = (BasicType<Object>) attributeMapping.getMappedType();
+				BasicValueConverter<?, ?> valueConverter = basicType.getValueConverter();
+				JavaType<?> javaType =
+						valueConverter!=null ? valueConverter.getRelationalJavaType() : attributeMapping.getJavaType();
+				generator.writeFieldName( name );
+
+				BeanPropertyWriter writer = beanPropertyWriterMap.get( ( (BasicAttributeMapping) attributeMapping ).getAttributeName() );
+				JsonSerializer<Object> serializer =
+						provider.findValueSerializer( objectMapper.constructType( javaType.getJavaType() ), writer );
+				JsonSerializer<Object> nullSerializer = provider.findNullValueSerializer( null );
+
+				try {
+					assert serializer != null;
+					if ( values[i] == null ) {
+						nullSerializer.serialize( null, generator, provider );
+					}
+					else {
+						serializer.serialize( basicType.convertToRelationalValue( values[i] ),generator,provider);
+					}
+
+				}
+				catch (Exception e) {
+					throw new RuntimeException( e );
+				}
+			}
+			else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
+				if ( values[i] == null ) {
+					// Skipping the update of the separator is on purpose
+					continue;
+				}
+				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
+				final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
+				if ( aggregateMapping == null ){
+
+					JsonSerializer<Object> serializer = provider
+							.findTypedValueSerializer( objectMapper.constructType( attributeMapping.getJavaType().getJavaType() ),true,null );
+					// flattened case
+					serializetoOsonUtil( (T) values[i],
+							serializer,
+							mappingType,
+							generator,
+							mapper,
+							provider);
+				}
+				else {
+					// non flattened case
+					final String name = aggregateMapping.getSelectableName();
+					generator.writeFieldName( name );
+					generator.writeStartObject();
+					JsonSerializer<Object> serializer = provider
+							.findTypedValueSerializer(
+									objectMapper.constructType( attributeMapping.getJavaType().getJavaType() ),
+									true,null );
+					serializetoOsonUtil( (T)values[i],
+							serializer,
+							mappingType,
+							generator,
+							mapper,
+							provider);
+					generator.writeEndObject();
+				}
+
+			}
+
+		}
 
-		ObjectWriter writer = objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) );
-		writer.writeValue( (JsonGenerator) target, value);
+	}
+
+	private Map<String, BeanPropertyWriter> buildBeanPropertyMap(Iterator<PropertyWriter> properties) {
+		Map<String,BeanPropertyWriter> result = new HashMap<String,BeanPropertyWriter>();
+		while ( properties.hasNext() ) {
+			BeanPropertyWriter writer = (BeanPropertyWriter) properties.next();
+			result.put( writer.getName(), writer );
+		}
+		return result;
 	}
 
 	@Override
@@ -363,4 +763,7 @@ public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions
 		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
 		return  objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
 	}
+
+
+
 }

From 05795597d501c68ecb9ba43a8abcb4925cb881bb Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Mon, 23 Dec 2024 12:35:41 +0100
Subject: [PATCH 13/81] HHH-17404 : move to fromString() in case of string
 event received

---
 .../type/format/jackson/JacksonOsonFormatMapper.java          | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index cdc5b7b8ce49..08bb2c6e875c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -236,9 +236,7 @@ else if ( "java.sql.Time".equals(
 								pluralType.getElementType().getJdbcJavaType().fromString( osonParser.getString() ) );
 					}
 					else {
-//						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getString(),options );
-//						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue( mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getString(),options ));
-						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().fromEncodedString( osonParser.getString(),0,osonParser.getString().length() );
+						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getString());
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_TRUE:

From da633142a75f1072e8b01e943494ce46f33bb3a1 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Fri, 3 Jan 2025 19:51:22 +0100
Subject: [PATCH 14/81] HHH-17404 : Add OSON document handler for embeddabe
 processing

---
 .../SessionFactoryOptionsBuilder.java         |  17 +-
 .../internal/StrategySelectorBuilder.java     |   6 +
 .../OracleOsonJacksonArrayJdbcType.java       |  24 +-
 .../dialect/OracleOsonJacksonJdbcType.java    |  23 +-
 .../type/format/AbstractJsonFormatMapper.java |   4 +-
 .../type/format/JsonDocumentHandler.java      |  60 ++++
 .../format/jackson/JacksonIntegration.java    |   5 +
 .../JacksonJakartaAnnotationIntrospector.java |   3 +-
 .../jackson/JacksonJsonFormatMapper.java      |  33 +-
 .../jackson/JacksonOsonFormatMapper.java      | 324 ++++--------------
 .../jackson/JacksonXmlFormatMapper.java       |   8 +-
 .../ObjectArrayOsonDocumentHandler.java       | 243 +++++++++++++
 12 files changed, 441 insertions(+), 309 deletions(-)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
index b6e451464448..50b4aefc85b1 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
@@ -79,6 +79,7 @@
 import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
 import org.hibernate.stat.Statistics;
 import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.jackson.JacksonIntegration;
 import org.hibernate.type.format.jaxb.JaxbXmlFormatMapper;
 
 import jakarta.persistence.criteria.Nulls;
@@ -112,6 +113,7 @@
 import static org.hibernate.jpa.internal.util.CacheModeHelper.interpretCacheMode;
 import static org.hibernate.jpa.internal.util.ConfigurationHelper.getFlushMode;
 import static org.hibernate.type.format.jackson.JacksonIntegration.getJsonJacksonFormatMapperOrNull;
+import static org.hibernate.type.format.jackson.JacksonIntegration.getOsonJacksonFormatMapperOrNull;
 import static org.hibernate.type.format.jackson.JacksonIntegration.getXMLJacksonFormatMapperOrNull;
 import static org.hibernate.type.format.jakartajson.JakartaJsonIntegration.getJakartaJsonBFormatMapperOrNull;
 
@@ -787,12 +789,23 @@ private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
 						.getDefaultConnectionHandlingMode();
 	}
 
-	private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector) {
+	private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector, ConfigurationService configurationService) {
 		return strategySelector.resolveDefaultableStrategy(
 				FormatMapper.class,
 				setting,
 				(Callable<FormatMapper>) () -> {
-					final FormatMapper jsonJacksonFormatMapper = getJsonJacksonFormatMapperOrNull();
+					FormatMapper jsonJacksonFormatMapper = null;
+					configurationService.getSetting(
+							SESSION_FACTORY_NAME_IS_JNDI,
+							BOOLEAN,
+							true
+					);
+					if (JacksonIntegration.isOracleOsonExtensionAvailable()) {
+						jsonJacksonFormatMapper = getOsonJacksonFormatMapperOrNull();
+					}
+					else {
+						jsonJacksonFormatMapper = getJsonJacksonFormatMapperOrNull();
+					}
 					return jsonJacksonFormatMapper != null ? jsonJacksonFormatMapper : getJakartaJsonBFormatMapperOrNull();
 				}
 		);
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
index f2e4c3381516..77150de87f70 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
@@ -46,6 +46,7 @@
 import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
+import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 import org.hibernate.type.format.jackson.JacksonXmlFormatMapper;
 import org.hibernate.type.format.jaxb.JaxbXmlFormatMapper;
 import org.hibernate.type.format.jakartajson.JsonBJsonFormatMapper;
@@ -311,6 +312,11 @@ private static void addJsonFormatMappers(StrategySelectorImpl strategySelector)
 				JsonBJsonFormatMapper.SHORT_NAME,
 				JsonBJsonFormatMapper.class
 		);
+		strategySelector.registerStrategyImplementor(
+				FormatMapper.class,
+				JacksonOsonFormatMapper.SHORT_NAME,
+				JacksonOsonFormatMapper.class
+		);
 	}
 
 	private static void addXmlFormatMappers(StrategySelectorImpl strategySelector) {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index b59c680c8651..f75f6977f07a 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -15,7 +15,6 @@
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.format.FormatMapper;
-import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayInputStream;
@@ -73,11 +72,12 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 		return new BasicBinder<>( javaType, this ) {
 
 			private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonJsonFormatMapper(objectMapper);
+				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				// TODO : we should not have to do this.
+				//    for now we have to inject the objectMapper.
+				//    As this is not a validated architectural decision, we do not
+				//     modify the interface yet.
+				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, null );
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
 
@@ -126,11 +126,13 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper);
+				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				// TODO : we should not have to do this.
+				//    for now we have to inject the objectMapper.
+				//    As this is not a validated architectural decision, we do not
+				//     modify the interface yet.
+				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, null );
+
 				return mapper.readFromSource(  getJavaType(), osonBytes, options);
 			}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 98112a8b291e..f9d16ec55d96 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -80,11 +80,12 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 			private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
 
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper,getEmbeddableMappingType());
+				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				// TODO : we should not have to do this.
+				//    for now we have to inject the objectMapper.
+				//    As this is not a validated architectural decision, we do not
+				//     modify the interface yet.
+				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, getEmbeddableMappingType());
 
 				if(getEmbeddableMappingType()!= null) {
 					return ((JacksonOsonFormatMapper)mapper).toOson(value,javaType,options);
@@ -135,11 +136,13 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
-				// TODO : We should rely on
-				//       FormatMapper fm = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				//
-				//     But this do not let use inject our ObjectMapper. For now create our own instance
-				FormatMapper mapper = new JacksonOsonFormatMapper(objectMapper, getEmbeddableMappingType());
+
+				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				// TODO : we should not have to do this.
+				//    for now we have to inject the objectMapper.
+				//    As this is not a validated architectural decision, we do not
+				//     modify the interface yet.
+				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, getEmbeddableMappingType() );
 
 				if (getEmbeddableMappingType() != null &&
 						getJavaType().getJavaTypeClass() == Object[].class) {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
index c315efa64916..c6cd1a0b5819 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
@@ -40,12 +40,12 @@ public final <T> String toString(T value, JavaType<T> javaType, WrapperOptions w
 
 	@Override
 	public boolean supportsSourceType(Class<?> sourceType) {
-		return false;
+		return CharSequence.class.isAssignableFrom(sourceType);
 	}
 
 	@Override
 	public boolean supportsTargetType(Class<?> targetType) {
-		return false;
+		return String.class.isAssignableFrom( targetType );
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
new file mode 100644
index 000000000000..497985faa064
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
@@ -0,0 +1,60 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+
+/**
+ * JSON document handler.
+ * Used to parse JSON documents. Implementors of this will define
+ * proper callback implementations.
+ *
+ * @author Emmanuel Jannetti
+ */
+
+public interface JsonDocumentHandler {
+	/**
+	 * Callback to be called when the start of an JSON object is encountered.
+	 */
+	void startObject();
+
+	/**
+	 * Callback to be called when the end of an JSON object is encountered.
+	 */
+	void endObject();
+
+	/**
+	 * Callback to be called when the start of an array is encountered.
+	 */
+	void startArray();
+
+	/**
+	 * Callback to be called when the end of an array is encountered.
+	 */
+	void endArray();
+
+	/**
+	 * Callback to be called when the key of JSON attribute is encountered.
+	 */
+	void onObjectKey(String key);
+
+	/**
+	 * Callback to be called when null value is encountered.
+	 */
+	void onNullValue();
+
+	/**
+	 * Callback to be called when boolean value is encountered.
+	 */
+	void onBooleanValue(boolean value);
+
+	/**
+	 * Callback to be called when string value is encountered.
+	 */
+	void onStringValue(String value);
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
index b0285fb2c49b..d3efc479f628 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
@@ -16,6 +16,8 @@ public final class JacksonIntegration {
 	private static final JacksonXmlFormatMapper XML_FORMAT_MAPPER = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper() : null;
 	private static final JacksonXmlFormatMapper XML_FORMAT_MAPPER_PORTABLE = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper( false ) : null;
 	private static final JacksonJsonFormatMapper JSON_FORMAT_MAPPER = JACKSON_JSON_AVAILABLE ? new JacksonJsonFormatMapper() : null;
+	private static final JacksonJsonFormatMapper OSON_FORMAT_MAPPER = JACKSON_OSON_AVAILABLE ? new JacksonOsonFormatMapper() : null;
+
 
 	private JacksonIntegration() {
 		//To not be instantiated: static helpers only
@@ -46,6 +48,9 @@ public static FormatMapper getXMLJacksonFormatMapperOrNull(boolean legacyFormat)
 	public static FormatMapper getJsonJacksonFormatMapperOrNull() {
 		return JSON_FORMAT_MAPPER;
 	}
+	public static FormatMapper getOsonJacksonFormatMapperOrNull() {
+		return OSON_FORMAT_MAPPER;
+	}
 
 	/**
 	 * Checks that Oracle OSON extension available
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
index 57c81dedf670..3d1375a856c2 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
@@ -73,7 +73,8 @@ public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) {
 					}
 				}
 			}
-		} else if (member instanceof AnnotatedMethod) {
+		}
+		else if (member instanceof AnnotatedMethod) {
 			Embeddable embeddable = member.getType().getRawClass().getAnnotation(Embeddable.class);
 			if (embeddable != null) {
 				String propName = getFieldNameFromGetterName(member.getName());
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index b6d77ffcfeb8..85f82149b196 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -4,9 +4,6 @@
  */
 package org.hibernate.type.format.jackson;
 
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.ObjectWriter;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
 import org.hibernate.type.format.AbstractJsonFormatMapper;
@@ -55,33 +52,29 @@ public <T> String toString(T value, Type type) {
 		}
 	}
 
-	@Override
-	public boolean supportsSourceType(Class<?> sourceType) {
-		return false;
-	}
-
-	@Override
-	public boolean supportsTargetType(Class<?> targetType) {
-		return false;
-	}
-
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
 
-		ObjectWriter writer = objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) );
-		writer.writeValue( (JsonGenerator) target, value);
-
+		try {
+			objectMapper.writerFor(
+					objectMapper.constructType( javaType.getJavaType() ) ).writeValueAsString( value );
+		}
+		catch (JsonProcessingException e) {
+			throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType.getJavaType(), e );
+		}
 
 	}
 
 	@Override
 	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
 
-		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
-
-		T t = objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
+		try {
+			return objectMapper.readValue( ((CharSequence)source).toString(), objectMapper.constructType( javaType.getJavaType() ) );
+		}
+		catch (JsonProcessingException e) {
+			throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType.getJavaType(), e );
+		}
 
-		return t;
 	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 08bb2c6e875c..4c858b3a10cb 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -23,24 +23,11 @@
 import oracle.sql.json.OracleJsonParser;
 import oracle.sql.json.OracleJsonTimestamp;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
-import org.hibernate.metamodel.mapping.SelectableMapping;
-import org.hibernate.metamodel.mapping.ValuedModelPart;
-import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
-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.converter.spi.BasicValueConverter;
 import org.hibernate.type.descriptor.java.BasicPluralJavaType;
 import org.hibernate.type.descriptor.java.JavaType;
-import org.hibernate.type.descriptor.java.JdbcDateJavaType;
-import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
-import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
-import org.hibernate.type.descriptor.java.UUIDJavaType;
-import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
-import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
-import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.format.JsonDocumentHandler;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -49,25 +36,7 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
-import java.sql.Date;
-import java.sql.SQLException;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.logging.Level;
-
-import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
+
 
 /**
  * @author Emmanuel Jannetti
@@ -77,8 +46,7 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 
 	public static final String SHORT_NAME = "jackson";
 
-	private final ObjectMapper objectMapper;
-	private final EmbeddableMappingType embeddableMappingType;
+	private  ObjectMapper objectMapper;
 
 	// fields/Methods to retrieve serializer data
 
@@ -89,277 +57,91 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 	 * same as JacksonOsonFormatMapper(objectMapper, null)
 	 */
 	public JacksonOsonFormatMapper(ObjectMapper objectMapper) {
-		this(objectMapper, null);
-	}
-
-	/**
-	 * Creates a new JacksonOsonFormatMapper
-	 * @param objectMapper the Jackson object mapper
-	 * @param embeddableMappingType the embeddable mapping definitions
-	 */
-	public JacksonOsonFormatMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
 		super(objectMapper);
 		this.objectMapper = objectMapper;
-		this.embeddableMappingType = embeddableMappingType;
 	}
-
+	public JacksonOsonFormatMapper() {
+		super();
+	}
 
 	/**
 	 * Process OSON parser tokens
 	 * @param osonParser the OSON parser
 	 * @param currentEvent the current of the parser
-	 * @param finalResult the populated object array
-	 * @param embeddableMappingType the embeddable mapping definitions
-	 * @param options the wrapping options
 	 * @throws IOException error while reading from underlying parser
 	 */
-	private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Event currentEvent, Object [] finalResult, EmbeddableMappingType embeddableMappingType, WrapperOptions options)
+	private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Event currentEvent, JsonDocumentHandler handler)
 			throws IOException {
 
 		OracleJsonParser.Event event = currentEvent;
 
-		int selectableIndex = -1;
-		SelectableMapping mapping = null;
-		String currentKeyName = null;
-		List<Object> subArrayList = null;
-		BasicPluralType<?, ?> pluralType = null;
-		Object theOne;
 		while ( event != null ) {
 			switch ( event ) {
 				case OracleJsonParser.Event.KEY_NAME:
-					currentKeyName = osonParser.getString();
-					selectableIndex = embeddableMappingType.getSelectableIndex( currentKeyName );
-					if ( selectableIndex >= 0 ) {
-						// we may not have a selectable mapping for that key
-						mapping = embeddableMappingType.getJdbcValueSelectable( selectableIndex );
-					}
+					handler.onObjectKey( osonParser.getString() );
 					break;
 				case OracleJsonParser.Event.START_ARRAY:
-					// initialize array to gather values
-					subArrayList = new ArrayList<>();
-					assert (mapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
-							: "Array event received for non plural type";
-					// initialize array's element type
-					pluralType = (BasicPluralType<?, ?>) mapping.getJdbcMapping();
+					handler.startArray();
 					break;
 				case OracleJsonParser.Event.END_ARRAY:
-					assert (subArrayList != null && pluralType != null) : "Wrong event ordering";
-					// flush array values
-					finalResult[selectableIndex] = pluralType.getJdbcJavaType().wrap( subArrayList, options );
-					// reset until we encounter next array elem
-					subArrayList = null;
-					pluralType = null;
+					handler.endArray();
 					break;
 				case OracleJsonParser.Event.VALUE_DATE:
-					LocalDateTime localDateTime = osonParser.getLocalDateTime();
-					Class underlyingType = null;
-					if(pluralType!=null) {
-						underlyingType = pluralType.getElementType().getJavaType();
-					} else {
-						underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
-					}
-					if (java.sql.Date.class.isAssignableFrom( underlyingType )) {
-						theOne = Date.valueOf( localDateTime.toLocalDate());
-					} else if (java.time.LocalDate.class.isAssignableFrom( underlyingType )) {
-						theOne = localDateTime.toLocalDate();
-					} else {
-						throw new IllegalArgumentException("unexpected date type " + underlyingType);
-					}
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( theOne );
-					}
-					else {
-						finalResult[selectableIndex] = theOne;
-					}
-					break;
 				case OracleJsonParser.Event.VALUE_TIMESTAMP:
-					LocalDateTime local = osonParser.getLocalDateTime();
-					if ( "java.sql.Timestamp".equals(
-							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() ) ) {
-						theOne = Timestamp.valueOf( local );
-					}
-					else if ( "java.time.LocalTime".equals(
-							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() )) {
-						theOne = local.toLocalTime();
-					}
-					else if ( "java.sql.Time".equals(
-							mapping.getJdbcMapping().getJdbcJavaType().getJavaType().getTypeName() )) {
-						theOne = Time.valueOf( local.toLocalTime() );
-					}
-					else {
-						theOne = local;
-					}
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( theOne );
-					}
-					else {
-						finalResult[selectableIndex] = theOne;
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonDateValue(
+							osonParser.getLocalDateTime());
 					break;
 				case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( osonParser.getOffsetDateTime() );
-					}
-					else {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-								mapping.getJdbcMapping().getJdbcJavaType()
-										.wrap( osonParser.getOffsetDateTime(), options ) );
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getOffsetDateTime());
 					break;
 				case OracleJsonParser.Event.VALUE_INTERVALDS:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( osonParser.getDuration() );
-					}
-					else {
-						// TODO: shall I use mapping.getJdbcMapping().getJdbcJavaType().wrap(...) ?
-						finalResult[selectableIndex] = osonParser.getDuration();
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getDuration());
 					break;
 				case OracleJsonParser.Event.VALUE_INTERVALYM:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( osonParser.getPeriod() );
-					}
-					else {
-						// TODO: shall I use mapping.getJdbcMapping().getJdbcJavaType().wrap(...) ?
-						finalResult[selectableIndex] = osonParser.getPeriod();
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getPeriod());
 					break;
 				case OracleJsonParser.Event.VALUE_STRING:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add(
-								pluralType.getElementType().getJdbcJavaType().fromString( osonParser.getString() ) );
-					}
-					else {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().getJdbcJavaType().fromString( osonParser.getString());
-					}
+					handler.onStringValue( osonParser.getString() );
 					break;
 				case OracleJsonParser.Event.VALUE_TRUE:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( Boolean.TRUE );
-					}
-					else {
-						finalResult[selectableIndex] = Boolean.TRUE;
-					}
+					handler.onBooleanValue( true );
 					break;
 				case OracleJsonParser.Event.VALUE_FALSE:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( Boolean.FALSE );
-					}
-					else {
-						finalResult[selectableIndex] = Boolean.FALSE;
-					}
+					handler.onBooleanValue( false );
 					break;
 				case OracleJsonParser.Event.VALUE_NULL:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( null );
-					}
-					else {
-						finalResult[selectableIndex] = null;
-					}
+					handler.onNullValue();
 					break;
 				case OracleJsonParser.Event.VALUE_DECIMAL:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( osonParser.isIntegralNumber() ? osonParser.getInt() : osonParser.getFloat() );
-					}
-					else {
-						// not array case: wrap value directly
-
-						if ( osonParser.isIntegralNumber() ) {
-							if("java.lang.Double".equals( mapping.getJdbcMapping().getJdbcJavaType().getTypeName() ) ) {
-								theOne = mapping.getJdbcMapping().convertToDomainValue(
-										mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getDouble(), options ) );
-
-							} else {
-								theOne = mapping.getJdbcMapping().convertToDomainValue(
-										mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getInt(), options ) );
-
-							}
-						}
-						else {
-
-							theOne = mapping.getJdbcMapping().convertToDomainValue(
-									mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
-						}
-						finalResult[selectableIndex] = theOne;
+					if (osonParser.isIntegralNumber()) {
+						((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+								osonParser.getInt());
+					} else {
+						((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+								osonParser.getFloat());
 					}
 					break;
 				case OracleJsonParser.Event.VALUE_DOUBLE:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( osonParser.getDouble() );
-					}
-					else {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getDouble(), options ) );
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getDouble());
 					break;
 				case OracleJsonParser.Event.VALUE_FLOAT:
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( osonParser.getFloat() );
-					}
-					else {
-						finalResult[selectableIndex] = mapping.getJdbcMapping().convertToDomainValue(
-								mapping.getJdbcMapping().getJdbcJavaType().wrap( osonParser.getFloat(), options ) );
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getFloat());
 					break;
 				case OracleJsonParser.Event.VALUE_BINARY:
-					if(pluralType!=null) {
-						underlyingType = pluralType.getElementType().getJavaType();
-					}
-					else {
-						underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
-					}
-
-					if (java.util.UUID.class.isAssignableFrom( underlyingType ))  {
-						theOne = UUIDJavaType.INSTANCE.wrap( osonParser.getBytes(), options );
-					}
-					else {
-						theOne = osonParser.getBytes();
-					}
-
-					if ( pluralType != null ) {
-						// dealing with arrays
-						subArrayList.add( theOne );
-					}
-					else {
-						finalResult[selectableIndex] = theOne;
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonBinaryValue(
+							osonParser.getBytes());
 					break;
 				case OracleJsonParser.Event.START_OBJECT:
-					if ( currentKeyName == null ) {
-						// that's the root
-						consumeOsonTokens( osonParser, osonParser.next(), finalResult,
-								embeddableMappingType,
-								options );
-					}
-					else {
-						selectableIndex = embeddableMappingType.getSelectableIndex( currentKeyName );
-						if ( selectableIndex != -1 ) {
-							final SelectableMapping selectable = embeddableMappingType.getJdbcValueSelectable(
-									selectableIndex );
-							final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
-									.getJdbcType();
-							final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-							finalResult[selectableIndex] = new Object[subMappingType.getJdbcValueCount()];
-							consumeOsonTokens( osonParser, osonParser.next(),
-									(Object[]) finalResult[selectableIndex],
-									subMappingType,
-									options );
-						}
-					}
+					handler.startObject(  );
+					//consumeOsonTokens( osonParser, osonParser.next(), handler);
 					break;
 				case OracleJsonParser.Event.END_OBJECT:
+					handler.endObject();
 					return;
 				default:
 					throw new IOException( "Unknown OSON event " + event );
@@ -377,14 +159,19 @@ else if ( "java.sql.Time".equals(
 	 * @param source the OSON bytes as <code>byte[]</code>
 	 * @param options the wrapping options
 	 * @return the Object array
-	 * @param <T> return type i.e object array
+	 * @param <T> return type i.e., object array
 	 * @throws IOException OSON parsing has failed
 	 */
 	public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object source, WrapperOptions options) throws IOException {
-		Object []finalResult = new Object[embeddableMappingType.getJdbcValueCount()];
+
 		OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( ByteBuffer.wrap( (byte[])source ) );
-		consumeOsonTokens(osonParser, osonParser.next(), finalResult, embeddableMappingType, options);
-		return (T)finalResult;
+
+		ObjectArrayOsonDocumentHandler handler = new ObjectArrayOsonDocumentHandler( embeddableMappingType,
+				options);
+
+		consumeOsonTokens(osonParser, osonParser.next(), handler);
+
+		return (T)handler.getMappedObjectArray();
 	}
 
 	@Override
@@ -762,6 +549,25 @@ public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions
 		return  objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
 	}
 
+	@Override
+	public boolean supportsSourceType(Class<?> sourceType) {
+		return JsonParser.class.isAssignableFrom( sourceType );
+	}
+
+	@Override
+	public boolean supportsTargetType(Class<?> targetType) {
+		return JsonParser.class.isAssignableFrom( targetType );
+	}
 
+	public void setJacksonObjectMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
+		this.objectMapper = objectMapper;
+
+		// need this until annotation introspector is removed.
+		if (embeddableMappingType != null) {
+			this.objectMapper.setAnnotationIntrospector(
+					new JacksonJakartaAnnotationIntrospector( embeddableMappingType ) );
+		}
+
+	}
 
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
index 01d2d156cc49..8b70c7138d98 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
@@ -206,23 +206,23 @@ else if ( javaType.getJavaTypeClass().isArray() ) {
 
 	@Override
 	public boolean supportsSourceType(Class<?> sourceType) {
-		return false;
+		return CharSequence.class.isAssignableFrom(sourceType);
 	}
 
 	@Override
 	public boolean supportsTargetType(Class<?> targetType) {
-		return false;
+		return String.class.isAssignableFrom( targetType );
 	}
 
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
-
+		target = toString(value, javaType, options);
 	}
 
 	@Override
 	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-		return null;
+		return fromString((CharSequence) source, javaType, options);
 	}
 
 	private <T> String writeValueAsString(Object value, JavaType<T> javaType, Type type) {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
new file mode 100644
index 000000000000..ecc434a67fa5
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
@@ -0,0 +1,243 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format.jackson;
+
+import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+import org.hibernate.metamodel.mapping.SelectableMapping;
+import org.hibernate.type.BasicPluralType;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.UUIDJavaType;
+import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
+import org.hibernate.type.format.JsonDocumentHandler;
+
+import java.sql.Date;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * Implementation of <code>JsonDocumentHandler</code> for OSON document.
+ * This implementation will produce an Object Array based on
+ * embeddable mapping
+ *
+ */
+public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
+
+	private Object [] objectArrayResult;
+	SelectableMapping mapping = null;
+	String currentKeyName = null;
+	List<Object> subArrayObjectList = null;
+	BasicPluralType<?, ?> subArrayObjectTypes = null;
+
+	// mapping definitions are in a tree
+	// Each mapping definition may contain sub mappings (sub embeddable mapping)
+	// This stack is used to keep pointer on mapping to be used
+	// see startObject/endObject methods
+	Stack<EmbeddableMappingType> embeddableMappingTypes = new Stack<>();
+
+	WrapperOptions wrapperOptions;
+
+	// index within objectArrayResult
+	int currentSelectableIndexInResultArray = -1;
+
+	public ObjectArrayOsonDocumentHandler(EmbeddableMappingType embeddableMappingType, WrapperOptions wrapperOptions) {
+		this.embeddableMappingTypes.push(embeddableMappingType);
+		this.wrapperOptions = wrapperOptions;
+		this.objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()];
+	}
+
+	/**
+	 * Gets the Object array built from document handling
+	 * @return the array
+	 */
+	public Object [] getMappedObjectArray() {
+		return this.objectArrayResult;
+	}
+
+	@Override
+	public void startObject() {
+		if (currentKeyName != null) {
+			// we are dealing with a sub-object, allocate space for it.
+			// otherwise, we have nothing to do.
+			currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
+			assert currentSelectableIndexInResultArray != -1: "Cannot get index of " + currentKeyName;
+
+			final SelectableMapping selectable = embeddableMappingTypes.peek().getJdbcValueSelectable(
+					currentSelectableIndexInResultArray );
+			final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
+					.getJdbcType();
+			final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
+			objectArrayResult[currentSelectableIndexInResultArray] =
+					new Object[subMappingType.getJdbcValueCount()];
+			embeddableMappingTypes.push( subMappingType );
+		}
+	}
+
+	@Override
+	public void endObject() {
+		embeddableMappingTypes.pop();
+	}
+
+	@Override
+	public void startArray() {
+		assert (subArrayObjectList == null && subArrayObjectTypes == null) : "startArray called twice ?";
+
+		// initialize an array to gather values
+		subArrayObjectList = new ArrayList<>();
+		assert (mapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
+				: "Array event received for non plural type";
+		// initialize array's element type
+		subArrayObjectTypes = (BasicPluralType<?, ?>) mapping.getJdbcMapping();
+	}
+
+	@Override
+	public void endArray() {
+		assert (subArrayObjectList != null && subArrayObjectTypes != null) : "endArray called before startArray";
+		// flush array values
+		objectArrayResult[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, wrapperOptions );
+		// reset until we encounter next array element
+		subArrayObjectList = null;
+		subArrayObjectTypes = null;
+	}
+
+	@Override
+	public void onObjectKey(String key) {
+		this.currentKeyName = key;
+
+		currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
+		if ( currentSelectableIndexInResultArray >= 0 ) {
+			// we may not have a selectable mapping for that key
+			mapping = embeddableMappingTypes.peek().getJdbcValueSelectable( currentSelectableIndexInResultArray );
+		}
+		else {
+			throw new IllegalArgumentException(
+					String.format(
+							"Could not find selectable [%s] in embeddable type [%s] for JSON processing.",
+							currentKeyName,
+							embeddableMappingTypes.peek().getMappedJavaType().getJavaTypeClass().getName()
+					)
+			);
+		}
+	}
+
+	@Override
+	public void onNullValue() {
+		if ( subArrayObjectList != null ) {
+			// dealing with arrays
+			subArrayObjectList.add( null );
+		}
+		else {
+			objectArrayResult[currentSelectableIndexInResultArray] = null;
+		}
+	}
+
+	@Override
+	public void onBooleanValue(boolean value) {
+		if ( subArrayObjectList != null ) {
+			// dealing with arrays
+			subArrayObjectList.add( value?Boolean.TRUE:Boolean.FALSE);
+		}
+		else {
+			objectArrayResult[currentSelectableIndexInResultArray] = value?Boolean.TRUE:Boolean.FALSE;
+		}
+	}
+
+	@Override
+	public void onStringValue(String value) {
+		if ( subArrayObjectList != null ) {
+			// dealing with arrays
+			subArrayObjectList.add(
+					subArrayObjectTypes.getElementType().getJdbcJavaType().fromString( value ) );
+		}
+		else {
+			objectArrayResult[currentSelectableIndexInResultArray] =
+					mapping.getJdbcMapping().getJdbcJavaType().fromString( value);
+		}
+	}
+
+	/**
+	 * Callback for OSON values
+	 * @param value the OSON value
+	 * @param <T> the type of the value as returned by OracleJsonParser
+	 */
+	public <T> void onOsonValue(T value) {
+		if ( subArrayObjectList != null ) {
+			// dealing with arrays
+			subArrayObjectList.add( value );
+		}
+		else {
+			objectArrayResult[currentSelectableIndexInResultArray] =
+					mapping.getJdbcMapping().convertToDomainValue(
+					mapping.getJdbcMapping().getJdbcJavaType()
+							.wrap( value, wrapperOptions ) );
+		}
+	}
+
+	/**
+	 * Callback for OSON binary value
+	 * @param bytes the OSON byters
+	 */
+	public void onOsonBinaryValue(byte[] bytes) {
+		Class underlyingType = null;
+		Object theOneToBeUsed;
+		if(subArrayObjectTypes!=null) {
+			underlyingType = subArrayObjectTypes.getElementType().getJavaType();
+		}
+		else {
+			underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
+		}
+
+		if (java.util.UUID.class.isAssignableFrom( underlyingType ))  {
+			theOneToBeUsed = UUIDJavaType.INSTANCE.wrap( bytes, wrapperOptions );
+		}
+		else {
+			theOneToBeUsed = bytes;
+		}
+
+		if ( subArrayObjectList != null ) {
+			// dealing with arrays
+			subArrayObjectList.add( theOneToBeUsed );
+		}
+		else {
+			objectArrayResult[currentSelectableIndexInResultArray] = theOneToBeUsed;
+		}
+	}
+
+	/**
+	 * Callback for OracleJsonParser.Event.VALUE_DATE and OracleJsonParser.Event.VALUE_TIMESTAMP:
+	 * @param localDateTime the time
+	 */
+	public void onOsonDateValue(LocalDateTime localDateTime) {
+
+		Class underlyingType = null;
+		Object theOneToBeUsed = localDateTime;
+
+		if(subArrayObjectTypes!=null) {
+			underlyingType = subArrayObjectTypes.getElementType().getJavaType();
+		}
+		else {
+			underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
+		}
+		if (java.sql.Date.class.isAssignableFrom( underlyingType )) {
+			theOneToBeUsed = Date.valueOf( localDateTime.toLocalDate());
+		}
+		else if (java.time.LocalDate.class.isAssignableFrom( underlyingType )) {
+			theOneToBeUsed = localDateTime.toLocalDate();
+		}
+		else if (java.sql.Timestamp.class.isAssignableFrom( underlyingType )) {
+			theOneToBeUsed = Timestamp.valueOf( localDateTime );
+		}
+
+		if ( subArrayObjectList != null ) {
+			// dealing with arrays
+			subArrayObjectList.add( theOneToBeUsed );
+		}
+		else {
+			objectArrayResult[currentSelectableIndexInResultArray] = theOneToBeUsed;
+		}
+	}
+}

From a01918cb950682af0f2f3b722412b10a7a0f5846 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Mon, 6 Jan 2025 14:51:40 +0530
Subject: [PATCH 15/81] HHH-17404: More fixes

---
 .../dialect/OracleOsonJacksonJdbcType.java    |  6 ++---
 .../type/format/JsonDocumentHandler.java      |  1 +
 .../jackson/JacksonOsonFormatMapper.java      | 27 +++++++++++++------
 3 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index f9d16ec55d96..6bd2e4a7c748 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -87,9 +87,9 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 				//     modify the interface yet.
 				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, getEmbeddableMappingType());
 
-				if(getEmbeddableMappingType()!= null) {
-					return ((JacksonOsonFormatMapper)mapper).toOson(value,javaType,options);
-				}
+//				if(getEmbeddableMappingType()!= null) {
+//					return ((JacksonOsonFormatMapper)mapper).toOson(value,javaType,options,getEmbeddableMappingType());
+//				}
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
 				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
index 497985faa064..f4a292076360 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
@@ -57,4 +57,5 @@ public interface JsonDocumentHandler {
 	 */
 	void onStringValue(String value);
 
+
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 4c858b3a10cb..ca0a49ad608c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -23,10 +23,14 @@
 import oracle.sql.json.OracleJsonParser;
 import oracle.sql.json.OracleJsonTimestamp;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
 import org.hibernate.type.descriptor.java.BasicPluralJavaType;
 import org.hibernate.type.descriptor.java.JavaType;
+
+import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.format.JsonDocumentHandler;
 
 import java.io.ByteArrayOutputStream;
@@ -37,6 +41,16 @@
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
 
+import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+
 
 /**
  * @author Emmanuel Jannetti
@@ -47,6 +61,7 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 	public static final String SHORT_NAME = "jackson";
 
 	private  ObjectMapper objectMapper;
+	private EmbeddableMappingType embeddableMappingType;
 
 	// fields/Methods to retrieve serializer data
 
@@ -195,15 +210,15 @@ public <T> String toString(T value, Type type) {
 	}
 
 
-	public <X>byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options) {
+	public <X>byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options,EmbeddableMappingType embeddableMappingType) {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
-		serializetoOsonApproach2( value,generator,javaType,options);
+		serializetoOsonApproach2( value,generator,javaType,options,embeddableMappingType);
 		generator.close();
 		return out.toByteArray();
 	}
 
-	private <X> void serializetoOsonApproach2(X value, OracleJsonGenerator generator, JavaType<X> javaType, WrapperOptions options) {
+	private <X> void serializetoOsonApproach2(X value, OracleJsonGenerator generator, JavaType<X> javaType, WrapperOptions options, EmbeddableMappingType embeddableMappingType) {
 		generator.writeStartObject();
 		serializetoOsonApproach2Util( value, generator, javaType, options,embeddableMappingType );
 		generator.writeEnd();
@@ -562,11 +577,7 @@ public boolean supportsTargetType(Class<?> targetType) {
 	public void setJacksonObjectMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
 		this.objectMapper = objectMapper;
 
-		// need this until annotation introspector is removed.
-		if (embeddableMappingType != null) {
-			this.objectMapper.setAnnotationIntrospector(
-					new JacksonJakartaAnnotationIntrospector( embeddableMappingType ) );
-		}
+		this.embeddableMappingType = embeddableMappingType;
 
 	}
 

From bdcad4e8e36a87aba7b08c35c167583eebfb9586 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Tue, 7 Jan 2025 22:27:05 +0530
Subject: [PATCH 16/81] HHH-17404: Serialization for additional types

---
 .../jackson/JacksonOsonFormatMapper.java      | 33 ++++++++++---------
 .../ObjectArrayOsonDocumentHandler.java       |  8 +++++
 2 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index ca0a49ad608c..8af5c094fe6f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -225,10 +225,10 @@ private <X> void serializetoOsonApproach2(X value, OracleJsonGenerator generator
 	}
 
 	private <X> void serializetoOsonApproach2Util(X value,
-												  OracleJsonGenerator generator,
-												  JavaType<X> javaType,
-												  WrapperOptions options,
-												  EmbeddableMappingType embeddableMappingType) {
+												OracleJsonGenerator generator,
+												JavaType<X> javaType,
+												WrapperOptions options,
+												EmbeddableMappingType embeddableMappingType) {
 
 		final Object[] values = embeddableMappingType.getValues( value );
 		for ( int i = 0; i < values.length; i++ ) {
@@ -296,20 +296,20 @@ private void serializeValue(Object value,
 				generator.write( javaType.unwrap( value,Integer.class,options ) );
 				break;
 			case SqlTypes.BOOLEAN:
-				generator.write( (Boolean) value );
+				generator.write( javaType.unwrap( value,Boolean.class,options ) );
 				break;
 			case SqlTypes.BIT:
-				generator.write( (Integer) value );
+				generator.write( javaType.unwrap( value,Integer.class,options ) );
 				break;
 			case SqlTypes.BIGINT:
-				generator.write( (BigInteger) value );
+				generator.write( javaType.unwrap( value,BigInteger.class,options ) );
 				break;
 			case SqlTypes.FLOAT:
-				generator.write( (Float) value );
+				generator.write( javaType.unwrap( value,Float.class,options ) );
 				break;
 			case SqlTypes.REAL:
 			case SqlTypes.DOUBLE:
-				generator.write( (Double) value );
+				generator.write( javaType.unwrap( value,Double.class,options ) );
 				break;
 			case SqlTypes.CHAR:
 			case SqlTypes.NCHAR:
@@ -331,7 +331,7 @@ private void serializeValue(Object value,
 			case SqlTypes.ENUM:
 			case SqlTypes.NAMED_ENUM:
 				// correct?
-				generator.write( javaType.toString(value) );
+				generator.write( javaType.unwrap( value,String.class,options ) );
 				break;
 			case SqlTypes.DATE:
 				DATE dd = new DATE(javaType.unwrap( value,java.sql.Date.class,options ));
@@ -341,7 +341,7 @@ private void serializeValue(Object value,
 			case SqlTypes.TIME:
 			case SqlTypes.TIME_WITH_TIMEZONE:
 			case SqlTypes.TIME_UTC:
-				generator.write( javaType.toString(value) );
+				generator.write( javaType.unwrap( value,String.class,options ) );
 				break;
 			case SqlTypes.TIMESTAMP:
 				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( value, Timestamp.class, options ));
@@ -371,7 +371,7 @@ else if (value instanceof Instant ) {
 					generator.write(instant.atOffset( ZoneOffset.UTC )  );
 					break;
 				}
-				generator.write( javaType.toString(value) );
+				generator.write( javaType.unwrap( value,String.class,options ) );
 				break;
 			case SqlTypes.NUMERIC:
 			case SqlTypes.DECIMAL:
@@ -385,16 +385,19 @@ else if (value instanceof Instant ) {
 				break;
 			case SqlTypes.UUID:
 				UUID uuid = javaType.unwrap( value, UUID.class, options );
-				byte[] bytes = _asBytes( uuid );
-				generator.write( bytes );
+				byte[] uuidBytes = _asBytes( uuid );
+				generator.write( uuidBytes );
 				break;
 			case SqlTypes.BINARY:
 			case SqlTypes.VARBINARY:
 			case SqlTypes.LONGVARBINARY:
 			case SqlTypes.LONG32VARBINARY:
+				byte[] bytes = javaType.unwrap( value, byte[].class, options );
+				generator.write( bytes );
+				break;
 			case SqlTypes.BLOB:
 			case SqlTypes.MATERIALIZED_BLOB:
-
+				// how to handle
 				break;
 			case SqlTypes.ARRAY:
 			case SqlTypes.JSON_ARRAY:
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
index ecc434a67fa5..cd0f5f9f9df0 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
@@ -15,6 +15,7 @@
 import java.sql.Date;
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Stack;
@@ -231,6 +232,13 @@ else if (java.time.LocalDate.class.isAssignableFrom( underlyingType )) {
 		else if (java.sql.Timestamp.class.isAssignableFrom( underlyingType )) {
 			theOneToBeUsed = Timestamp.valueOf( localDateTime );
 		}
+		else if(java.time.LocalTime.class.isAssignableFrom( underlyingType )) {
+			theOneToBeUsed = localDateTime.toLocalTime();
+		}
+		else if ( java.util.Date.class.isAssignableFrom( underlyingType ) ) {
+			// better way?
+			theOneToBeUsed = java.util.Date.from( localDateTime.atZone( ZoneId.of( "UTC" ) ).toInstant());
+		}
 
 		if ( subArrayObjectList != null ) {
 			// dealing with arrays

From d8b0288702b16be4c76a7f03ddd1c3762fd5b88a Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Mon, 6 Jan 2025 11:35:53 +0100
Subject: [PATCH 17/81] HHH-17404 : add more date related types to document
 handler

---
 .../JacksonJakartaAnnotationIntrospector.java | 130 ------------------
 .../ObjectArrayOsonDocumentHandler.java       |  13 +-
 2 files changed, 10 insertions(+), 133 deletions(-)
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
deleted file mode 100644
index 3d1375a856c2..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJakartaAnnotationIntrospector.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.type.format.jackson;
-
-import com.fasterxml.jackson.databind.PropertyName;
-import com.fasterxml.jackson.databind.introspect.Annotated;
-import com.fasterxml.jackson.databind.introspect.AnnotatedField;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
-import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
-import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
-import com.fasterxml.jackson.databind.util.NameTransformer;
-import jakarta.persistence.Column;
-import jakarta.persistence.Embeddable;
-import org.hibernate.metamodel.mapping.AttributeMapping;
-import org.hibernate.metamodel.mapping.AttributeMappingsList;
-import org.hibernate.metamodel.mapping.EmbeddableMappingType;
-import org.hibernate.metamodel.mapping.SelectableMapping;
-import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class JacksonJakartaAnnotationIntrospector extends JacksonAnnotationIntrospector {
-
-	private final EmbeddableMappingType mappingType;
-	private final Map<String, EmbeddableMappingTypeWithFlattening> mappingTypeMap = new HashMap<>();
-
-	public JacksonJakartaAnnotationIntrospector(EmbeddableMappingType mappingType) {
-		this.mappingType = mappingType;
-		resolveEmbeddableTypes( this.mappingType );
-	}
-
-	@Override
-	public PropertyName findNameForSerialization(Annotated a) {
-		Column column = _findAnnotation(a, Column.class);
-		if (column != null && !column.name().isEmpty()) {
-			return PropertyName.construct(column.name());
-		}
-		return super.findNameForSerialization(a);
-	}
-
-	@Override
-	public PropertyName findNameForDeserialization(Annotated a) {
-		Column column = _findAnnotation(a, Column.class);
-		if (column != null && !column.name().isEmpty()) {
-			return PropertyName.construct(column.name());
-		}
-		return super.findNameForDeserialization(a);
-	}
-	private String getFieldNameFromGetterName(String fieldName) {
-		// we assume that method is get|set<camelCase Nme>
-		// 1. we strip out get|set
-		// 2. lowercase on first letter
-		assert fieldName != null;
-		assert fieldName.substring( 0,3 ).equalsIgnoreCase( "get" ) ||
-			fieldName.substring( 0,3 ).equalsIgnoreCase( "set" );
-		fieldName = fieldName.substring( 3 );
-		return Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1);
-	}
-	@Override
-	public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) {
-		if(member instanceof AnnotatedField) {
-			Embeddable embeddable = member.getType().getRawClass().getAnnotation(Embeddable.class);
-			if (embeddable != null) {
-				String propName = member.getName();
-				if(mappingTypeMap.get(propName) != null){
-					EmbeddableMappingTypeWithFlattening embeddableMappingTypeWithFlattening =
-							mappingTypeMap.get( propName );
-					if(embeddableMappingTypeWithFlattening.isShouldFlatten()) {
-						return NameTransformer.simpleTransformer( "","" );
-					}
-				}
-			}
-		}
-		else if (member instanceof AnnotatedMethod) {
-			Embeddable embeddable = member.getType().getRawClass().getAnnotation(Embeddable.class);
-			if (embeddable != null) {
-				String propName = getFieldNameFromGetterName(member.getName());
-				if(mappingTypeMap.get(propName) != null){
-					EmbeddableMappingTypeWithFlattening embeddableMappingTypeWithFlattening =
-							mappingTypeMap.get( propName );
-					if(embeddableMappingTypeWithFlattening.isShouldFlatten()) {
-						return NameTransformer.simpleTransformer( "","" );
-					}
-				}
-			}
-		}
-		return super.findUnwrappingNameTransformer(member);
-	}
-// theJson
-	private void resolveEmbeddableTypes(EmbeddableMappingType embeddableMappingType) {
-		AttributeMappingsList attributeMappings = embeddableMappingType.getAttributeMappings();
-
-		for (int i = 0; i < attributeMappings.size(); i++){
-			AttributeMapping attributeMapping = attributeMappings.get(i);
-			if ( attributeMapping instanceof EmbeddedAttributeMapping embeddedAttributeMapping ) {
-
-				EmbeddableMappingType attributeEmbeddableMappingType = embeddedAttributeMapping.getMappedType();
-				SelectableMapping aggregateMapping = attributeEmbeddableMappingType.getAggregateMapping();
-
-				mappingTypeMap.put( attributeMapping.getAttributeName(),
-						new EmbeddableMappingTypeWithFlattening(
-								attributeEmbeddableMappingType,
-								aggregateMapping == null ));
-
-				resolveEmbeddableTypes( attributeEmbeddableMappingType);
-			}
-		}
-	}
-
-	static class EmbeddableMappingTypeWithFlattening {
-		private final EmbeddableMappingType embeddableMappingType;
-		private final boolean shouldFlatten;
-
-		public EmbeddableMappingTypeWithFlattening(EmbeddableMappingType embeddableMappingType, boolean shouldFlatten) {
-			this.embeddableMappingType = embeddableMappingType;
-			this.shouldFlatten = shouldFlatten;
-		}
-
-		public EmbeddableMappingType getEmbeddableMappingType() {
-			return embeddableMappingType;
-		}
-
-		public boolean isShouldFlatten() {
-			return shouldFlatten;
-		}
-	}
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
index cd0f5f9f9df0..c95ebd4ee71e 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
@@ -13,6 +13,7 @@
 import org.hibernate.type.format.JsonDocumentHandler;
 
 import java.sql.Date;
+import java.sql.Time;
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -36,7 +37,7 @@ public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
 
 	// mapping definitions are in a tree
 	// Each mapping definition may contain sub mappings (sub embeddable mapping)
-	// This stack is used to keep pointer on mapping to be used
+	// This stack is used to keep a pointer on the current mapping to be used
 	// see startObject/endObject methods
 	Stack<EmbeddableMappingType> embeddableMappingTypes = new Stack<>();
 
@@ -152,11 +153,11 @@ public void onStringValue(String value) {
 		if ( subArrayObjectList != null ) {
 			// dealing with arrays
 			subArrayObjectList.add(
-					subArrayObjectTypes.getElementType().getJdbcJavaType().fromString( value ) );
+					subArrayObjectTypes.getElementType().getJdbcJavaType().fromEncodedString( value ,0,value.length()) );
 		}
 		else {
 			objectArrayResult[currentSelectableIndexInResultArray] =
-					mapping.getJdbcMapping().getJdbcJavaType().fromString( value);
+					mapping.getJdbcMapping().getJdbcJavaType().fromEncodedString( value,0,value.length());
 		}
 	}
 
@@ -229,6 +230,12 @@ public void onOsonDateValue(LocalDateTime localDateTime) {
 		else if (java.time.LocalDate.class.isAssignableFrom( underlyingType )) {
 			theOneToBeUsed = localDateTime.toLocalDate();
 		}
+		else if (java.time.LocalTime.class.isAssignableFrom( underlyingType )) {
+			theOneToBeUsed = localDateTime.toLocalTime();
+		}
+		else if (java.sql.Time.class.isAssignableFrom( underlyingType )) {
+			theOneToBeUsed = Time.valueOf( localDateTime.toLocalTime() );
+		}
 		else if (java.sql.Timestamp.class.isAssignableFrom( underlyingType )) {
 			theOneToBeUsed = Timestamp.valueOf( localDateTime );
 		}

From fd641691887770400117220fdeb27276372ce411 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Tue, 7 Jan 2025 17:52:28 +0100
Subject: [PATCH 18/81] HHH-17404 : use of BigDecimal in case of DECIMAL OSON
 event received in document handler

---
 .../hibernate/type/format/JsonDocumentHandler.java    |  5 +++++
 .../type/format/jackson/JacksonOsonFormatMapper.java  | 11 +++--------
 .../jackson/ObjectArrayOsonDocumentHandler.java       | 11 +++++++++--
 3 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
index f4a292076360..fd88db2fbd35 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
@@ -8,6 +8,7 @@
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 
+
 /**
  * JSON document handler.
  * Used to parse JSON documents. Implementors of this will define
@@ -57,5 +58,9 @@ public interface JsonDocumentHandler {
 	 */
 	void onStringValue(String value);
 
+	/**
+	 * Callback to be called when Number value is encountered.
+	 */
+	void onNumberValue(Number value);
 
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 8af5c094fe6f..a86db3aacb87 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -131,13 +131,8 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 					handler.onNullValue();
 					break;
 				case OracleJsonParser.Event.VALUE_DECIMAL:
-					if (osonParser.isIntegralNumber()) {
-						((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-								osonParser.getInt());
-					} else {
-						((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-								osonParser.getFloat());
-					}
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getBigDecimal());
 					break;
 				case OracleJsonParser.Event.VALUE_DOUBLE:
 					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
@@ -186,7 +181,7 @@ public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object s
 
 		consumeOsonTokens(osonParser, osonParser.next(), handler);
 
-		return (T)handler.getMappedObjectArray();
+		return (T)handler.getObjectArray();
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
index c95ebd4ee71e..160df852e1ae 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
@@ -54,9 +54,9 @@ public ObjectArrayOsonDocumentHandler(EmbeddableMappingType embeddableMappingTyp
 
 	/**
 	 * Gets the Object array built from document handling
-	 * @return the array
+	 * @return the array of objects
 	 */
-	public Object [] getMappedObjectArray() {
+	public Object [] getObjectArray() {
 		return this.objectArrayResult;
 	}
 
@@ -65,6 +65,7 @@ public void startObject() {
 		if (currentKeyName != null) {
 			// we are dealing with a sub-object, allocate space for it.
 			// otherwise, we have nothing to do.
+			// Push the new (sub)mapping definition.
 			currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
 			assert currentSelectableIndexInResultArray != -1: "Cannot get index of " + currentKeyName;
 
@@ -81,6 +82,7 @@ public void startObject() {
 
 	@Override
 	public void endObject() {
+		// go back in the mapping definition tree
 		embeddableMappingTypes.pop();
 	}
 
@@ -161,6 +163,11 @@ public void onStringValue(String value) {
 		}
 	}
 
+	@Override
+	public void onNumberValue(Number value) {
+		onOsonValue(value);
+	}
+
 	/**
 	 * Callback for OSON values
 	 * @param value the OSON value

From e2e9607e2248c17ab65536a846e7344f8add2c23 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Tue, 7 Jan 2025 18:30:11 +0100
Subject: [PATCH 19/81] HHH-17404 : fix toString in mapper

---
 .../jackson/JacksonOsonFormatMapper.java      | 28 +++----------------
 .../ObjectArrayOsonDocumentHandler.java       |  2 ++
 2 files changed, 6 insertions(+), 24 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index a86db3aacb87..e25d740e16fc 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -6,7 +6,6 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
@@ -36,7 +35,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.lang.reflect.Array;
-import java.lang.reflect.Type;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
@@ -73,8 +71,11 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 	 */
 	public JacksonOsonFormatMapper(ObjectMapper objectMapper) {
 		super(objectMapper);
-		this.objectMapper = objectMapper;
 	}
+
+	/**
+	 * Creates a new JacksonOsonFormatMapper
+	 */
 	public JacksonOsonFormatMapper() {
 		super();
 	}
@@ -184,27 +185,6 @@ public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object s
 		return (T)handler.getObjectArray();
 	}
 
-	@Override
-	public <T> T fromString(CharSequence charSequence, Type type) {
-		try {
-			return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( type ) );
-		}
-		catch (JsonProcessingException e) {
-			throw new IllegalArgumentException( "Could not deserialize string to java type: " + type, e );
-		}
-	}
-
-	@Override
-	public <T> String toString(T value, Type type) {
-		try {
-			return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
-		}
-		catch (JsonProcessingException e) {
-			throw new IllegalArgumentException( "Could not serialize object of java type: " + type, e );
-		}
-	}
-
-
 	public <X>byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options,EmbeddableMappingType embeddableMappingType) {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
index 160df852e1ae..3ce21dbcb743 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
@@ -25,6 +25,8 @@
  * Implementation of <code>JsonDocumentHandler</code> for OSON document.
  * This implementation will produce an Object Array based on
  * embeddable mapping
+ * Once All JSON document is handle the mapped Object array can be retrieved using the
+ * <code>getObjectArray()</code> method.
  *
  */
 public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {

From 4ce35d5652167682a24488fc9b4ea99f6b2603d4 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Wed, 8 Jan 2025 14:42:45 +0530
Subject: [PATCH 20/81] HHH-17404 InternalDS Support for duration

---
 .../org/hibernate/dialect/OracleDialect.java  |  5 +++
 .../dialect/OracleDurationJavaType.java       | 33 +++++++++++++++++++
 2 files changed, 38 insertions(+)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 738fab6ff7b3..7861da828414 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -92,6 +92,8 @@
 import org.hibernate.type.NullType;
 import org.hibernate.type.StandardBasicTypes;
 import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
+import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
+import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.descriptor.jdbc.NullJdbcType;
 import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
@@ -833,6 +835,7 @@ protected String columnType(int sqlTypeCode) {
 	protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
 		super.registerColumnTypes( typeContributions, serviceRegistry );
 		final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
+		final JavaTypeRegistry javaTypeRegistry = typeContributions.getTypeConfiguration().getJavaTypeRegistry();
 
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "SYS.XMLTYPE", this ) );
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
@@ -853,6 +856,8 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
 		// We need the DDL type during runtime to produce the proper encoding in certain functions
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( BIT, "number(1,0)", this ) );
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( DURATION, "interval day to second", this ) );
+		javaTypeRegistry.addDescriptor( OracleDurationJavaType.INSTANCE );
+
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
new file mode 100644
index 000000000000..7bff7256d666
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
@@ -0,0 +1,33 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.dialect;
+
+import oracle.sql.INTERVALDS;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.DurationJavaType;
+
+import java.time.Duration;
+
+public class OracleDurationJavaType extends DurationJavaType {
+
+	public static final OracleDurationJavaType INSTANCE = new OracleDurationJavaType();
+
+	public OracleDurationJavaType() {
+		super();
+	}
+
+	@Override
+	public <X> Duration wrap(X value, WrapperOptions options) {
+		if(value == null) {
+			return null;
+		}
+
+		if ( value instanceof INTERVALDS ) {
+			return INTERVALDS.toDuration( ((INTERVALDS) value).toBytes() );
+		}
+
+		return super.wrap( value, options );
+	}
+}

From 7c1c121acfb26b885be5b5c8dde6ce914b9cd9ab Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Thu, 9 Jan 2025 13:11:19 +0530
Subject: [PATCH 21/81] HHH-17404-Replace the embeddable serialization
 approach.

---
 .../dialect/OracleOsonJacksonJdbcType.java    |   7 +-
 .../aggregate/OracleAggregateSupport.java     |  39 +---
 .../jackson/JacksonOsonFormatMapper.java      | 170 ++++--------------
 3 files changed, 44 insertions(+), 172 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 6bd2e4a7c748..b8f5e1cf33c3 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -87,9 +87,10 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 				//     modify the interface yet.
 				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, getEmbeddableMappingType());
 
-//				if(getEmbeddableMappingType()!= null) {
-//					return ((JacksonOsonFormatMapper)mapper).toOson(value,javaType,options,getEmbeddableMappingType());
-//				}
+				if(getEmbeddableMappingType()!= null) {
+					return ((JacksonOsonFormatMapper)mapper).toOson(value,javaType,options,getEmbeddableMappingType());
+				}
+
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
 				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index bea29441e55b..d8c7372cee4b 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -22,7 +22,6 @@
 import org.hibernate.metamodel.mapping.SelectableMapping;
 import org.hibernate.metamodel.mapping.SelectablePath;
 import org.hibernate.metamodel.mapping.SqlTypedMapping;
-import org.hibernate.query.sqm.CastType;
 import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
 import org.hibernate.sql.ast.SqlAstTranslator;
 import org.hibernate.sql.ast.spi.SqlAppender;
@@ -140,21 +139,10 @@ public String aggregateComponentCustomReadExpression(
 							case BIGINT:
 							case CLOB:
 							case NCLOB:
-								CastType castType = column.getJdbcMapping().getCastType();
-								switch ( castType ) {
-									case INTEGER_BOOLEAN:
-										if (!dateTypesStoreAsString) {
-											return template.replace(
-													placeholder,
-													"decode(json_value(" + parentPartExpression + columnExpression + "'),'true',1,'false',0,null)"
-											);
-										}
-									default:
-										return template.replace(
-												placeholder,
-												"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
-										);
-								}
+								return template.replace(
+										placeholder,
+										"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
+								);
 
 							case DATE:
 								if (this.dateTypesStoreAsString) {
@@ -257,21 +245,10 @@ public String aggregateComponentCustomReadExpression(
 										"json_query(" + parentPartExpression + columnExpression + "' returning " + jsonTypeName + ")"
 								);
 							default:
-								CastType castTypeCharBoolean = column.getJdbcMapping().getCastType();
-								switch ( castTypeCharBoolean ){
-									case YN_BOOLEAN:
-										if (!dateTypesStoreAsString) {
-											return template.replace(
-													placeholder,
-													"decode(json_value(" + parentPartExpression + columnExpression + "'),'true','Y','false','N',null)"
-											);
-										}
-									default:
-										return template.replace(
-												placeholder,
-												"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getColumnDefinition() + ')'
-										);
-								}
+								return template.replace(
+										placeholder,
+										"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getColumnDefinition() + ')'
+								);
 
 						}
 					case NONE:
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index e25d740e16fc..526daa657bbd 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -6,12 +6,8 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
-import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
-import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
-import com.fasterxml.jackson.databind.ser.PropertyWriter;
 import oracle.jdbc.driver.json.tree.OracleJsonDateImpl;
 import oracle.jdbc.driver.json.tree.OracleJsonTimestampImpl;
 import oracle.sql.DATE;
@@ -22,9 +18,12 @@
 import oracle.sql.json.OracleJsonParser;
 import oracle.sql.json.OracleJsonTimestamp;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
-
+import org.hibernate.metamodel.mapping.SelectableMapping;
+import org.hibernate.metamodel.mapping.ValuedModelPart;
+import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
+import org.hibernate.type.BasicType;
+import org.hibernate.type.SqlTypes;
 import org.hibernate.type.descriptor.WrapperOptions;
-import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
 import org.hibernate.type.descriptor.java.BasicPluralJavaType;
 import org.hibernate.type.descriptor.java.JavaType;
 
@@ -38,15 +37,12 @@
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
-
+import java.sql.Time;
 import java.sql.Timestamp;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.OffsetDateTime;
 import java.time.ZoneOffset;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
 import java.util.UUID;
 
 
@@ -188,22 +184,22 @@ public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object s
 	public <X>byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options,EmbeddableMappingType embeddableMappingType) {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
-		serializetoOsonApproach2( value,generator,javaType,options,embeddableMappingType);
+		serializetoOson( value,generator,javaType,options,embeddableMappingType);
 		generator.close();
 		return out.toByteArray();
 	}
 
-	private <X> void serializetoOsonApproach2(X value, OracleJsonGenerator generator, JavaType<X> javaType, WrapperOptions options, EmbeddableMappingType embeddableMappingType) {
+	private <X> void serializetoOson(X value, OracleJsonGenerator generator, JavaType<X> javaType, WrapperOptions options, EmbeddableMappingType embeddableMappingType) {
 		generator.writeStartObject();
-		serializetoOsonApproach2Util( value, generator, javaType, options,embeddableMappingType );
+		serializetoOsonUtil( value, generator, javaType, options,embeddableMappingType );
 		generator.writeEnd();
 	}
 
-	private <X> void serializetoOsonApproach2Util(X value,
-												OracleJsonGenerator generator,
-												JavaType<X> javaType,
-												WrapperOptions options,
-												EmbeddableMappingType embeddableMappingType) {
+	private <X> void serializetoOsonUtil(X value,
+										OracleJsonGenerator generator,
+										JavaType<X> javaType,
+										WrapperOptions options,
+										EmbeddableMappingType embeddableMappingType) {
 
 		final Object[] values = embeddableMappingType.getValues( value );
 		for ( int i = 0; i < values.length; i++ ) {
@@ -213,7 +209,16 @@ private <X> void serializetoOsonApproach2Util(X value,
 				final BasicType<Object> basicType = (BasicType<Object>) attributeMapping.getMappedType();
 
 				generator.writeKey( name );
-				serializeValue( basicType.convertToRelationalValue( values[i] ), (JavaType<Object>) basicType.getJdbcJavaType(),basicType.getJdbcType(), options,generator);
+
+				if (values[i] == null) {
+					generator.writeNull();
+					continue;
+				}
+				serializeValue( basicType.convertToRelationalValue( values[i] ),
+						(JavaType<Object>) basicType.getJdbcJavaType(),
+						basicType.getJdbcType(),
+						options,
+						generator);
 
 			}
 			else if (attributeMapping instanceof EmbeddedAttributeMapping) {
@@ -225,7 +230,7 @@ else if (attributeMapping instanceof EmbeddedAttributeMapping) {
 				}
 				if (aggregateMapping == null) {
 					// flattened case
-					serializetoOsonApproach2Util( (X) values[i],
+					serializetoOsonUtil( (X) values[i],
 							generator,
 							javaType,
 							options,
@@ -236,7 +241,7 @@ else if (attributeMapping instanceof EmbeddedAttributeMapping) {
 					final String name = aggregateMapping.getSelectableName();
 					generator.writeKey( name );
 					generator.writeStartObject();
-					serializetoOsonApproach2Util( (X) values[i],
+					serializetoOsonUtil( (X) values[i],
 							generator,
 							javaType,
 							options,
@@ -316,7 +321,8 @@ private void serializeValue(Object value,
 			case SqlTypes.TIME:
 			case SqlTypes.TIME_WITH_TIMEZONE:
 			case SqlTypes.TIME_UTC:
-				generator.write( javaType.unwrap( value,String.class,options ) );
+				Time time = javaType.unwrap( value, Time.class,options );
+				generator.write( time.toString() );
 				break;
 			case SqlTypes.TIMESTAMP:
 				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( value, Timestamp.class, options ));
@@ -367,12 +373,11 @@ else if (value instanceof Instant ) {
 			case SqlTypes.VARBINARY:
 			case SqlTypes.LONGVARBINARY:
 			case SqlTypes.LONG32VARBINARY:
-				byte[] bytes = javaType.unwrap( value, byte[].class, options );
-				generator.write( bytes );
-				break;
 			case SqlTypes.BLOB:
 			case SqlTypes.MATERIALIZED_BLOB:
 				// how to handle
+				byte[] bytes = javaType.unwrap( value, byte[].class, options );
+				generator.write( bytes );
 				break;
 			case SqlTypes.ARRAY:
 			case SqlTypes.JSON_ARRAY:
@@ -420,120 +425,9 @@ private void _appendInt(int value, byte[] buffer, int offset)
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
 		com.fasterxml.jackson.databind.JavaType jacksonJavaType = objectMapper.constructType( javaType.getJavaType() );
+		ObjectWriter writer = objectMapper.writerFor( jacksonJavaType );
+		writer.writeValue( (JsonGenerator) target, value);
 
-		if(embeddableMappingType == null ) {
-			ObjectWriter writer = objectMapper.writerFor( jacksonJavaType );
-			writer.writeValue( (JsonGenerator) target, value);
-			return;
-		}
-
-		DefaultSerializerProvider provider =((DefaultSerializerProvider)objectMapper.getSerializerProvider())
-				.createInstance( objectMapper.getSerializationConfig(),objectMapper.getSerializerFactory() );
-		JsonSerializer<Object> valueSerializer = provider
-				.findTypedValueSerializer( jacksonJavaType,true, null );
-		serializetoOson(value,valueSerializer,embeddableMappingType,(JsonGenerator)target,objectMapper,provider);
-		((JsonGenerator)target).flush();
-
-	}
-
-	private <T> void serializetoOson(T value, JsonSerializer<Object> valueSerializer, EmbeddableMappingType embeddableMappingType, JsonGenerator target, ObjectMapper objectMapper, DefaultSerializerProvider provider)
-			throws IOException {
-		target.writeStartObject();
-		serializetoOsonUtil(value,valueSerializer,embeddableMappingType,target,objectMapper,provider);
-		target.writeEndObject();
-
-	}
-
-	private <T> void serializetoOsonUtil(T value,
-										JsonSerializer<Object> valueSerializer,
-										EmbeddableMappingType embeddableMappingType,
-										JsonGenerator generator,
-										ObjectMapper mapper,
-										DefaultSerializerProvider provider) throws IOException {
-		final Object[] values = embeddableMappingType.getValues( value );
-
-		Map<String, BeanPropertyWriter> beanPropertyWriterMap = buildBeanPropertyMap(valueSerializer.properties());
-		for ( int i = 0; i < values.length; i++ ) {
-			final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
-			if ( attributeMapping instanceof SelectableMapping ) {
-				// basic attribute ??
-				final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
-
-				final BasicType<Object> basicType = (BasicType<Object>) attributeMapping.getMappedType();
-				BasicValueConverter<?, ?> valueConverter = basicType.getValueConverter();
-				JavaType<?> javaType =
-						valueConverter!=null ? valueConverter.getRelationalJavaType() : attributeMapping.getJavaType();
-				generator.writeFieldName( name );
-
-				BeanPropertyWriter writer = beanPropertyWriterMap.get( ( (BasicAttributeMapping) attributeMapping ).getAttributeName() );
-				JsonSerializer<Object> serializer =
-						provider.findValueSerializer( objectMapper.constructType( javaType.getJavaType() ), writer );
-				JsonSerializer<Object> nullSerializer = provider.findNullValueSerializer( null );
-
-				try {
-					assert serializer != null;
-					if ( values[i] == null ) {
-						nullSerializer.serialize( null, generator, provider );
-					}
-					else {
-						serializer.serialize( basicType.convertToRelationalValue( values[i] ),generator,provider);
-					}
-
-				}
-				catch (Exception e) {
-					throw new RuntimeException( e );
-				}
-			}
-			else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
-				if ( values[i] == null ) {
-					// Skipping the update of the separator is on purpose
-					continue;
-				}
-				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
-				final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
-				if ( aggregateMapping == null ){
-
-					JsonSerializer<Object> serializer = provider
-							.findTypedValueSerializer( objectMapper.constructType( attributeMapping.getJavaType().getJavaType() ),true,null );
-					// flattened case
-					serializetoOsonUtil( (T) values[i],
-							serializer,
-							mappingType,
-							generator,
-							mapper,
-							provider);
-				}
-				else {
-					// non flattened case
-					final String name = aggregateMapping.getSelectableName();
-					generator.writeFieldName( name );
-					generator.writeStartObject();
-					JsonSerializer<Object> serializer = provider
-							.findTypedValueSerializer(
-									objectMapper.constructType( attributeMapping.getJavaType().getJavaType() ),
-									true,null );
-					serializetoOsonUtil( (T)values[i],
-							serializer,
-							mappingType,
-							generator,
-							mapper,
-							provider);
-					generator.writeEndObject();
-				}
-
-			}
-
-		}
-
-	}
-
-	private Map<String, BeanPropertyWriter> buildBeanPropertyMap(Iterator<PropertyWriter> properties) {
-		Map<String,BeanPropertyWriter> result = new HashMap<String,BeanPropertyWriter>();
-		while ( properties.hasNext() ) {
-			BeanPropertyWriter writer = (BeanPropertyWriter) properties.next();
-			result.put( writer.getName(), writer );
-		}
-		return result;
 	}
 
 	@Override

From e2b8ae5e8f07f7b1d84e9300b60c6bcd5af50791 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 8 Jan 2025 10:57:32 +0100
Subject: [PATCH 22/81] HHH-17404 : fix issue with sub-embeddables

---
 .../jackson/JacksonOsonFormatMapper.java      |  5 +-
 .../ObjectArrayOsonDocumentHandler.java       | 56 +++++++++++--------
 2 files changed, 35 insertions(+), 26 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 526daa657bbd..4b460c9ef037 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -144,12 +144,11 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 							osonParser.getBytes());
 					break;
 				case OracleJsonParser.Event.START_OBJECT:
-					handler.startObject(  );
-					//consumeOsonTokens( osonParser, osonParser.next(), handler);
+					handler.startObject();
 					break;
 				case OracleJsonParser.Event.END_OBJECT:
 					handler.endObject();
-					return;
+					break;
 				default:
 					throw new IOException( "Unknown OSON event " + event );
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
index 3ce21dbcb743..b8eb3a7c1542 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
@@ -31,17 +31,24 @@
  */
 public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
 
+	// final result of mapped obejct array
 	private Object [] objectArrayResult;
-	SelectableMapping mapping = null;
+	// current mapping to be used
+	SelectableMapping currentSelectableMapping = null;
 	String currentKeyName = null;
 	List<Object> subArrayObjectList = null;
 	BasicPluralType<?, ?> subArrayObjectTypes = null;
 
 	// mapping definitions are in a tree
 	// Each mapping definition may contain sub mappings (sub embeddable mapping)
-	// This stack is used to keep a pointer on the current mapping to be used
-	// see startObject/endObject methods
+	// This stack is used to keep a pointer on the current mapping to be used to assign correct types.
+	// see startObject()/endObject() methods
 	Stack<EmbeddableMappingType> embeddableMappingTypes = new Stack<>();
+	// As for mapping definitions, when "sub embeddable" is encountered, the array
+	// that needs to be filled with Objects is the one we allocate in the final result array slot.
+	// We use a stack to keep track of array ref
+	Stack<Object[]> objectArrays = new Stack<>();
+
 
 	WrapperOptions wrapperOptions;
 
@@ -52,6 +59,7 @@ public ObjectArrayOsonDocumentHandler(EmbeddableMappingType embeddableMappingTyp
 		this.embeddableMappingTypes.push(embeddableMappingType);
 		this.wrapperOptions = wrapperOptions;
 		this.objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()];
+		this.objectArrays.push( this.objectArrayResult );
 	}
 
 	/**
@@ -65,10 +73,10 @@ public ObjectArrayOsonDocumentHandler(EmbeddableMappingType embeddableMappingTyp
 	@Override
 	public void startObject() {
 		if (currentKeyName != null) {
-			// we are dealing with a sub-object, allocate space for it.
+			// We are dealing with a sub-object, allocate space for it then,
 			// otherwise, we have nothing to do.
 			// Push the new (sub)mapping definition.
-			currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
+			this.currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
 			assert currentSelectableIndexInResultArray != -1: "Cannot get index of " + currentKeyName;
 
 			final SelectableMapping selectable = embeddableMappingTypes.peek().getJdbcValueSelectable(
@@ -76,16 +84,18 @@ public void startObject() {
 			final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
 					.getJdbcType();
 			final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-			objectArrayResult[currentSelectableIndexInResultArray] =
+			this.objectArrays.peek()[currentSelectableIndexInResultArray] =
 					new Object[subMappingType.getJdbcValueCount()];
-			embeddableMappingTypes.push( subMappingType );
+			this.embeddableMappingTypes.push( subMappingType );
+			this.objectArrays.push( (Object[]) this.objectArrays.peek()[currentSelectableIndexInResultArray] );
 		}
 	}
 
 	@Override
 	public void endObject() {
 		// go back in the mapping definition tree
-		embeddableMappingTypes.pop();
+		this.embeddableMappingTypes.pop();
+		this.objectArrays.pop();
 	}
 
 	@Override
@@ -94,17 +104,17 @@ public void startArray() {
 
 		// initialize an array to gather values
 		subArrayObjectList = new ArrayList<>();
-		assert (mapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
+		assert (currentSelectableMapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
 				: "Array event received for non plural type";
 		// initialize array's element type
-		subArrayObjectTypes = (BasicPluralType<?, ?>) mapping.getJdbcMapping();
+		subArrayObjectTypes = (BasicPluralType<?, ?>) currentSelectableMapping.getJdbcMapping();
 	}
 
 	@Override
 	public void endArray() {
 		assert (subArrayObjectList != null && subArrayObjectTypes != null) : "endArray called before startArray";
 		// flush array values
-		objectArrayResult[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, wrapperOptions );
+		this.objectArrays.peek()[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, wrapperOptions );
 		// reset until we encounter next array element
 		subArrayObjectList = null;
 		subArrayObjectTypes = null;
@@ -117,7 +127,7 @@ public void onObjectKey(String key) {
 		currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
 		if ( currentSelectableIndexInResultArray >= 0 ) {
 			// we may not have a selectable mapping for that key
-			mapping = embeddableMappingTypes.peek().getJdbcValueSelectable( currentSelectableIndexInResultArray );
+			currentSelectableMapping = embeddableMappingTypes.peek().getJdbcValueSelectable( currentSelectableIndexInResultArray );
 		}
 		else {
 			throw new IllegalArgumentException(
@@ -137,7 +147,7 @@ public void onNullValue() {
 			subArrayObjectList.add( null );
 		}
 		else {
-			objectArrayResult[currentSelectableIndexInResultArray] = null;
+			this.objectArrays.peek()[currentSelectableIndexInResultArray] = null;
 		}
 	}
 
@@ -148,7 +158,7 @@ public void onBooleanValue(boolean value) {
 			subArrayObjectList.add( value?Boolean.TRUE:Boolean.FALSE);
 		}
 		else {
-			objectArrayResult[currentSelectableIndexInResultArray] = value?Boolean.TRUE:Boolean.FALSE;
+			this.objectArrays.peek()[currentSelectableIndexInResultArray] = value?Boolean.TRUE:Boolean.FALSE;
 		}
 	}
 
@@ -160,8 +170,8 @@ public void onStringValue(String value) {
 					subArrayObjectTypes.getElementType().getJdbcJavaType().fromEncodedString( value ,0,value.length()) );
 		}
 		else {
-			objectArrayResult[currentSelectableIndexInResultArray] =
-					mapping.getJdbcMapping().getJdbcJavaType().fromEncodedString( value,0,value.length());
+			this.objectArrays.peek()[currentSelectableIndexInResultArray] =
+					currentSelectableMapping.getJdbcMapping().getJdbcJavaType().fromEncodedString( value,0,value.length());
 		}
 	}
 
@@ -181,9 +191,9 @@ public <T> void onOsonValue(T value) {
 			subArrayObjectList.add( value );
 		}
 		else {
-			objectArrayResult[currentSelectableIndexInResultArray] =
-					mapping.getJdbcMapping().convertToDomainValue(
-					mapping.getJdbcMapping().getJdbcJavaType()
+			this.objectArrays.peek()[currentSelectableIndexInResultArray] =
+					currentSelectableMapping.getJdbcMapping().convertToDomainValue(
+					currentSelectableMapping.getJdbcMapping().getJdbcJavaType()
 							.wrap( value, wrapperOptions ) );
 		}
 	}
@@ -199,7 +209,7 @@ public void onOsonBinaryValue(byte[] bytes) {
 			underlyingType = subArrayObjectTypes.getElementType().getJavaType();
 		}
 		else {
-			underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
+			underlyingType = (Class) currentSelectableMapping.getJdbcMapping().getJdbcJavaType().getJavaType();
 		}
 
 		if (java.util.UUID.class.isAssignableFrom( underlyingType ))  {
@@ -214,7 +224,7 @@ public void onOsonBinaryValue(byte[] bytes) {
 			subArrayObjectList.add( theOneToBeUsed );
 		}
 		else {
-			objectArrayResult[currentSelectableIndexInResultArray] = theOneToBeUsed;
+			this.objectArrays.peek()[currentSelectableIndexInResultArray] = theOneToBeUsed;
 		}
 	}
 
@@ -231,7 +241,7 @@ public void onOsonDateValue(LocalDateTime localDateTime) {
 			underlyingType = subArrayObjectTypes.getElementType().getJavaType();
 		}
 		else {
-			underlyingType = (Class) mapping.getJdbcMapping().getJdbcJavaType().getJavaType();
+			underlyingType = (Class) currentSelectableMapping.getJdbcMapping().getJdbcJavaType().getJavaType();
 		}
 		if (java.sql.Date.class.isAssignableFrom( underlyingType )) {
 			theOneToBeUsed = Date.valueOf( localDateTime.toLocalDate());
@@ -261,7 +271,7 @@ else if ( java.util.Date.class.isAssignableFrom( underlyingType ) ) {
 			subArrayObjectList.add( theOneToBeUsed );
 		}
 		else {
-			objectArrayResult[currentSelectableIndexInResultArray] = theOneToBeUsed;
+			this.objectArrays.peek()[currentSelectableIndexInResultArray] = theOneToBeUsed;
 		}
 	}
 }

From aad86becbdc5e1ff2eddaf2758aec3ad373056b3 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 8 Jan 2025 16:34:31 +0100
Subject: [PATCH 23/81] HHH-17404 : remove creation of ObjectMapper in the Oson
 type to rely on common ones

---
 .../OracleOsonJacksonArrayJdbcType.java       | 49 ++++---------------
 .../dialect/OracleOsonJacksonJdbcType.java    | 48 +++++-------------
 .../jackson/JacksonJsonFormatMapper.java      |  2 +-
 .../jackson/JacksonOsonFormatMapper.java      | 38 +++++++-------
 .../NestedStructEmbeddableTest.java           |  1 +
 .../NestedStructWithArrayEmbeddableTest.java  |  1 +
 .../embeddable/StructEmbeddableArrayTest.java |  1 +
 .../embeddable/StructEmbeddableTest.java      |  1 +
 8 files changed, 46 insertions(+), 95 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index f75f6977f07a..7016818f61d0 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -4,8 +4,8 @@
  */
 package org.hibernate.dialect;
 
+import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import oracle.sql.json.OracleJsonDatum;
 import org.hibernate.type.descriptor.ValueBinder;
 import org.hibernate.type.descriptor.ValueExtractor;
@@ -20,8 +20,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -32,21 +30,20 @@
  */
 public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
 
-	private static Method jacksonOsonObjectMapperGetter = null;
-
+	private static final Class osonFactoryKlass;
 	static {
 		try {
-			Class jacksonOsonConverter = OracleOsonJacksonJdbcType.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.JacksonOsonConverter" );
-			jacksonOsonObjectMapperGetter = jacksonOsonConverter.getMethod( "getObjectMapper" );
+			osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
 		}
-		catch (ClassNotFoundException | LinkageError | NoSuchMethodException e) {
-			// should not happen as OracleOsonJacksonJdbcType is loaded
-			// only when Oracle OSON JDBC extension is present
+		catch (ClassNotFoundException | LinkageError e) {
+			// should not happen as OracleOsonJacksonArrayJdbcType is loaded
+			// only when an Oracle OSON JDBC extension is present
 			// see OracleDialect class.
 			throw new ExceptionInInitializerError( "OracleOsonJacksonArrayJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
 		}
 	}
 
+
 	public OracleOsonJacksonArrayJdbcType(JdbcType elementJdbcType) {
 		super(elementJdbcType);
 	}
@@ -60,28 +57,14 @@ public String toString() {
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
-		final ObjectMapper objectMapper;
-		try {
-			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
-		}
-		catch (IllegalAccessException | InvocationTargetException e) {
-			// should not happen
-			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
-		}
-
 		return new BasicBinder<>( javaType, this ) {
 
 			private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				// TODO : we should not have to do this.
-				//    for now we have to inject the objectMapper.
-				//    As this is not a validated architectural decision, we do not
-				//     modify the interface yet.
-				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, null );
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
-
-				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
+				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
+				JsonGenerator osonGen = osonFactory.createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
 				osonGen.close();
 				ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
@@ -114,24 +97,10 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions
 	@Override
 	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
-		final ObjectMapper objectMapper;
-		try {
-			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
-		}
-		catch (IllegalAccessException | InvocationTargetException e) {
-			// should not happen
-			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
-		}
-
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				// TODO : we should not have to do this.
-				//    for now we have to inject the objectMapper.
-				//    As this is not a validated architectural decision, we do not
-				//     modify the interface yet.
-				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, null );
 
 				return mapper.readFromSource(  getJavaType(), osonBytes, options);
 			}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index b8f5e1cf33c3..fd23c4a9b51e 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -4,8 +4,8 @@
  */
 package org.hibernate.dialect;
 
+import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import oracle.jdbc.OracleType;
 import oracle.sql.json.OracleJsonDatum;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@@ -21,8 +21,6 @@
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayOutputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -33,20 +31,21 @@
  */
 public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
 	public static final OracleOsonJacksonJdbcType INSTANCE = new OracleOsonJacksonJdbcType( null );
-	private static Method jacksonOsonObjectMapperGetter = null;
 
+	private static final Class osonFactoryKlass;
 	static {
 		try {
-			Class jacksonOsonConverter = OracleOsonJacksonJdbcType.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.JacksonOsonConverter" );
-			jacksonOsonObjectMapperGetter = jacksonOsonConverter.getMethod( "getObjectMapper" );
+			osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
 		}
-		catch (ClassNotFoundException | LinkageError | NoSuchMethodException e) {
+		catch (ClassNotFoundException | LinkageError e) {
 			// should not happen as OracleOsonJacksonJdbcType is loaded
 			// only when Oracle OSON JDBC extension is present
 			// see OracleDialect class.
 			throw new ExceptionInInitializerError( "OracleOsonJacksonJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
 		}
 	}
+
+
 	private OracleOsonJacksonJdbcType(EmbeddableMappingType embeddableMappingType) {
 		super( embeddableMappingType );
 	}
@@ -67,14 +66,7 @@ public AggregateJdbcType resolveAggregateJdbcType(
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
-		final ObjectMapper objectMapper;
-		try {
-			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
-		}
-		catch (IllegalAccessException | InvocationTargetException e) {
-			// should not happen
-			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
-		}
+
 
 		return new BasicBinder<>( javaType, this ) {
 
@@ -82,17 +74,11 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				// TODO : we should not have to do this.
-				//    for now we have to inject the objectMapper.
-				//    As this is not a validated architectural decision, we do not
-				//     modify the interface yet.
-				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, getEmbeddableMappingType());
-
-				if(getEmbeddableMappingType()!= null) {
-					return ((JacksonOsonFormatMapper)mapper).toOson(value,javaType,options,getEmbeddableMappingType());
-				}
+				((JacksonOsonFormatMapper)mapper).setEmbeddableMappingType( getEmbeddableMappingType());
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
-				JsonGenerator osonGen = objectMapper.getFactory().createGenerator( out );
+				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
+				JsonGenerator osonGen = osonFactory.createGenerator( out );
 				mapper.writeToTarget( value, javaType, osonGen, options );
 				osonGen.close(); // until now
 				return out.toByteArray();
@@ -125,25 +111,13 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions
 	@Override
 	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
-		final ObjectMapper objectMapper;
-		try {
-			objectMapper = (ObjectMapper) jacksonOsonObjectMapperGetter.invoke( null );
-		}
-		catch (IllegalAccessException | InvocationTargetException e) {
-			// should not happen
-			throw new RuntimeException("Can't retrieve ObjectMapper from OSON extension", e );
-		}
-
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				// TODO : we should not have to do this.
-				//    for now we have to inject the objectMapper.
-				//    As this is not a validated architectural decision, we do not
-				//     modify the interface yet.
-				((JacksonOsonFormatMapper)mapper).setJacksonObjectMapper(objectMapper, getEmbeddableMappingType() );
+				((JacksonOsonFormatMapper)mapper).setEmbeddableMappingType( getEmbeddableMappingType() );
 
 				if (getEmbeddableMappingType() != null &&
 						getJavaType().getJavaTypeClass() == Object[].class) {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index 85f82149b196..ba936e9915a8 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -22,7 +22,7 @@ public class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
 
 	public static final String SHORT_NAME = "jackson";
 
-	private final ObjectMapper objectMapper;
+	protected final ObjectMapper objectMapper;
 
 	public JacksonJsonFormatMapper() {
 		this(new ObjectMapper().findAndRegisterModules());
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 4b460c9ef037..d1bd224847b3 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -54,26 +54,35 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 
 	public static final String SHORT_NAME = "jackson";
 
-	private  ObjectMapper objectMapper;
-	private EmbeddableMappingType embeddableMappingType;
 
-	// fields/Methods to retrieve serializer data
-
-
-	/**
-	 * Creates a new JacksonOsonFormatMapper
-	 * @param objectMapper the Jackson object mapper
-	 * same as JacksonOsonFormatMapper(objectMapper, null)
-	 */
-	public JacksonOsonFormatMapper(ObjectMapper objectMapper) {
-		super(objectMapper);
+	private static final Class osonModuleKlass;
+	static {
+		try {
+			osonModuleKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonModule" );
+		}
+		catch (ClassNotFoundException | LinkageError e) {
+			// should not happen as JacksonOsonFormatMapper is loaded
+			// only when Oracle OSON JDBC extension is present
+			// see OracleDialect class.
+			throw new ExceptionInInitializerError( "JacksonOsonFormatMapper class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
+		}
 	}
 
+	// TODO : remove the use of this once the OSON writer has been refactor to Document handling
+	private EmbeddableMappingType embeddableMappingType = null;
+
 	/**
 	 * Creates a new JacksonOsonFormatMapper
 	 */
 	public JacksonOsonFormatMapper() {
 		super();
+		try {
+			objectMapper.registerModule( (Module) osonModuleKlass.getDeclaredConstructor().newInstance() );
+		}
+		catch (Exception e) {
+			throw new RuntimeException( "Cannot instanciate " + osonModuleKlass.getCanonicalName(), e );
+		}
+		objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
 	}
 
 	/**
@@ -445,11 +454,6 @@ public boolean supportsTargetType(Class<?> targetType) {
 		return JsonParser.class.isAssignableFrom( targetType );
 	}
 
-	public void setJacksonObjectMapper(ObjectMapper objectMapper, EmbeddableMappingType embeddableMappingType) {
-		this.objectMapper = objectMapper;
-
-		this.embeddableMappingType = embeddableMappingType;
 
-	}
 
 }
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java
index 32014e193fb6..3ea0197f0107 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java
@@ -79,6 +79,7 @@
 @RequiresDialect( PostgreSQLDialect.class )
 @RequiresDialect( OracleDialect.class )
 @RequiresDialect( DB2Dialect.class )
+@SkipForDialect(dialectClass = OracleDialect.class, reason = "Waiting for the fix of a bug that prevent creation of INTERVALDS from Duration")
 public class NestedStructEmbeddableTest implements AdditionalMappingContributor {
 
 	@Override
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java
index bd9b0bcdde3e..acef9d18de66 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java
@@ -56,6 +56,7 @@
 
 @RequiresDialect( PostgreSQLDialect.class )
 @RequiresDialect( OracleDialect.class )
+@SkipForDialect(dialectClass = OracleDialect.class, reason = "Waiting for the fix of a bug that prevent creation of INTERVALDS from Duration")
 @BootstrapServiceRegistry(
 		// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
 		integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java
index 28847fee8230..8eb9b5fb2abe 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java
@@ -83,6 +83,7 @@
 @SessionFactory
 @RequiresDialect( PostgreSQLDialect.class )
 @RequiresDialect( OracleDialect.class )
+@SkipForDialect(dialectClass = OracleDialect.class, reason = "Waiting for the fix of a bug that prevent creation of INTERVALDS from Duration")
 public class StructEmbeddableArrayTest implements AdditionalMappingContributor {
 
 	@Override
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableTest.java
index b2caa216381e..2ff564c018eb 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableTest.java
@@ -76,6 +76,7 @@
 @SessionFactory
 @RequiresDialect( PostgreSQLDialect.class )
 @RequiresDialect( OracleDialect.class )
+@SkipForDialect(dialectClass = OracleDialect.class, reason = "Waiting for the fix of a bug that prevent creation of INTERVALDS from Duration")
 @RequiresDialect( DB2Dialect.class )
 public class StructEmbeddableTest implements AdditionalMappingContributor {
 

From 6b14f828f00e464a78f97d67e19c3ef5758b75a9 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 8 Jan 2025 19:50:23 +0100
Subject: [PATCH 24/81]  HHH-17404 : fix OsonFactory for extaction

---
 .../org/hibernate/dialect/OracleOsonJacksonJdbcType.java   | 7 ++++++-
 .../type/format/jackson/JacksonOsonFormatMapper.java       | 6 +++---
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index fd23c4a9b51e..22937d92bea1 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -6,6 +6,7 @@
 
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
 import oracle.sql.json.OracleJsonDatum;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@@ -134,7 +135,11 @@ private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 					// We are dealing with embeddable (@Embeddable)
 					type = (JavaType<X>) getEmbeddableMappingType().getJavaType();
 				}
-				return mapper.readFromSource( type, osonBytes, options );
+
+				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
+				JsonParser osonParser = osonFactory.createParser(  osonBytes );
+
+				return mapper.readFromSource( type, osonParser, options );
 			}
 
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index d1bd224847b3..08887f72595c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -440,8 +440,8 @@ public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, Wrap
 
 	@Override
 	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-		JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
-		return  objectMapper.readValue( osonParser, objectMapper.constructType( javaType.getJavaType()) );
+		//JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
+		return  objectMapper.readValue( (JsonParser)source, objectMapper.constructType( javaType.getJavaType()) );
 	}
 
 	@Override
@@ -451,7 +451,7 @@ public boolean supportsSourceType(Class<?> sourceType) {
 
 	@Override
 	public boolean supportsTargetType(Class<?> targetType) {
-		return JsonParser.class.isAssignableFrom( targetType );
+		return JsonGenerator.class.isAssignableFrom( targetType );
 	}
 
 

From 3518b1b9e477542f127b24e6547248185fba787c Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Thu, 9 Jan 2025 08:41:22 +0100
Subject: [PATCH 25/81] HHH-17404 : fix OracleDurationJdbcType

---
 .../dialect/OracleDurationJdbcType.java       | 45 ++++++++++++++++---
 1 file changed, 38 insertions(+), 7 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
index b6a9323aaebb..f29bc723f4aa 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -4,14 +4,18 @@
  */
 package org.hibernate.dialect;
 
+import oracle.jdbc.OracleType;
+import org.hibernate.engine.jdbc.Size;
 import org.hibernate.type.SqlTypes;
+import org.hibernate.type.Type;
 import org.hibernate.type.descriptor.ValueBinder;
 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.BasicBinder;
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
-import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.descriptor.jdbc.DurationJdbcType;
+import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
 
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
@@ -19,11 +23,13 @@
 import java.sql.SQLException;
 import java.time.Duration;
 
-public class OracleDurationJdbcType implements JdbcType {
+public class OracleDurationJdbcType extends DurationJdbcType {
 
 	public static final OracleDurationJdbcType INSTANCE = new OracleDurationJdbcType();
 
-	public OracleDurationJdbcType() {}
+	public OracleDurationJdbcType() {
+		super();
+	}
 
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
@@ -80,13 +86,38 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o
 		};
 	}
 
-	@Override
+	/**
+	 * The {@linkplain SqlTypes JDBC type code} used when interacting with JDBC APIs.
+	 * <p>
+	 * For example, it's used when calling {@link java.sql.PreparedStatement#setNull(int, int)}.
+	 *
+	 * @return a JDBC type code
+	 */
 	public int getJdbcTypeCode() {
 		return SqlTypes.DURATION;
 	}
-
-	@Override
+	/**
+	 * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type to
+	 * be used for schema generation.
+	 * <p>
+	 * This value is passed to {@link DdlTypeRegistry#getTypeName(int, Size, Type)}
+	 * to obtain the SQL column type.
+	 *
+	 * @return a JDBC type code
+	 * @since 6.2
+	 */
+	public int getDdlTypeCode() {
+		return OracleType.INTERVAL_DAY_TO_SECOND.getVendorTypeNumber();
+	}
+	/**
+	 * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type.
+	 * <p>
+	 * This value might be different from {@link #getDdlTypeCode()} if the actual type
+	 * e.g. JSON is emulated through a type like CLOB.
+	 *
+	 * @return a JDBC type code
+	 */
 	public int getDefaultSqlTypeCode() {
-		return SqlTypes.DURATION;
+		return OracleType.INTERVAL_DAY_TO_SECOND.getVendorTypeNumber();
 	}
 }

From 6cac337f58673dbcfde7f22a9f579cac05a80f24 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Thu, 9 Jan 2025 22:29:27 +0530
Subject: [PATCH 26/81] HHH-17404 - Fix Nested Embeddable Tests

---
 .../java/org/hibernate/dialect/OracleDialect.java   |  3 +++
 .../hibernate/dialect/OracleDurationJdbcType.java   |  5 ++---
 .../dialect/OracleOsonJacksonJdbcType.java          | 13 +++++++++++--
 .../dialect/aggregate/OracleAggregateSupport.java   | 13 +++++++++++++
 .../format/jackson/JacksonOsonFormatMapper.java     | 11 +++++------
 5 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 7861da828414..34d205cada78 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -826,6 +826,9 @@ protected String columnType(int sqlTypeCode) {
 			case VARBINARY:
 				return "raw($l)";
 
+			case DURATION:
+				return "interval day to second";
+
 			default:
 				return super.columnType( sqlTypeCode );
 		}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
index f29bc723f4aa..6170d96dc184 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -4,7 +4,6 @@
  */
 package org.hibernate.dialect;
 
-import oracle.jdbc.OracleType;
 import org.hibernate.engine.jdbc.Size;
 import org.hibernate.type.SqlTypes;
 import org.hibernate.type.Type;
@@ -107,7 +106,7 @@ public int getJdbcTypeCode() {
 	 * @since 6.2
 	 */
 	public int getDdlTypeCode() {
-		return OracleType.INTERVAL_DAY_TO_SECOND.getVendorTypeNumber();
+		return SqlTypes.DURATION;
 	}
 	/**
 	 * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type.
@@ -118,6 +117,6 @@ public int getDdlTypeCode() {
 	 * @return a JDBC type code
 	 */
 	public int getDefaultSqlTypeCode() {
-		return OracleType.INTERVAL_DAY_TO_SECOND.getVendorTypeNumber();
+		return SqlTypes.DURATION;
 	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 22937d92bea1..c9f1de066d20 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -158,8 +158,17 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
 				// can I use rs.getBinaryStream( paramIndex); ?
-				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-				return doExtraction(ojd,options);
+				try {
+					if(javaType.getJavaTypeClass().isAssignableFrom( String.class )) {
+						return fromString( rs.getString( 1 ),javaType,options );
+					}
+					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+					return doExtraction(ojd,options);
+				}catch (SQLException e) {
+
+					throw new SQLException( e );
+				}
+
 			}
 
 			@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index d8c7372cee4b..77eded5f0959 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -178,6 +178,19 @@ public String aggregateComponentCustomReadExpression(
 											"json_value(" + parentPartExpression + columnExpression + "' returning timestamp)"
 									);
 								}
+							case DURATION:
+								if (this.dateTypesStoreAsString) {
+									return template.replace(
+											placeholder,
+											"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getColumnDefinition() + ')'
+									);
+								}
+								else {
+									return template.replace(
+											placeholder,
+											"json_value(" + parentPartExpression + columnExpression + "' returning interval day to second)"
+									);
+								}
 							case TIMESTAMP_WITH_TIMEZONE:
 							case TIMESTAMP_UTC:
 								if (this.dateTypesStoreAsString) {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 08887f72595c..5f1c4164ecc4 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -6,8 +6,9 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.SerializationFeature;
 import oracle.jdbc.driver.json.tree.OracleJsonDateImpl;
 import oracle.jdbc.driver.json.tree.OracleJsonTimestampImpl;
 import oracle.sql.DATE;
@@ -242,7 +243,7 @@ else if (attributeMapping instanceof EmbeddedAttributeMapping) {
 							generator,
 							javaType,
 							options,
-							embeddableMappingType );
+							mappingType );
 				}
 				else {
 					// non flattened case
@@ -253,7 +254,7 @@ else if (attributeMapping instanceof EmbeddedAttributeMapping) {
 							generator,
 							javaType,
 							options,
-							embeddableMappingType);
+							mappingType);
 					generator.writeEnd();
 
 				}
@@ -432,15 +433,13 @@ private void _appendInt(int value, byte[] buffer, int offset)
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
-		com.fasterxml.jackson.databind.JavaType jacksonJavaType = objectMapper.constructType( javaType.getJavaType() );
-		ObjectWriter writer = objectMapper.writerFor( jacksonJavaType );
+		ObjectWriter writer = objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) );
 		writer.writeValue( (JsonGenerator) target, value);
 
 	}
 
 	@Override
 	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-		//JsonParser osonParser = objectMapper.getFactory().createParser( (byte[]) source );
 		return  objectMapper.readValue( (JsonParser)source, objectMapper.constructType( javaType.getJavaType()) );
 	}
 

From 3a3f9c4554da662e75fca7e9ce15114ae8393d94 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Fri, 10 Jan 2025 13:21:45 +0530
Subject: [PATCH 27/81] HHH-17404- Fix Array Embeddables.

---
 .../hibernate/dialect/OracleOsonJacksonArrayJdbcType.java  | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index 7016818f61d0..ea8fc7eb528e 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -6,6 +6,7 @@
 
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
 import oracle.sql.json.OracleJsonDatum;
 import org.hibernate.type.descriptor.ValueBinder;
 import org.hibernate.type.descriptor.ValueExtractor;
@@ -100,9 +101,11 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
-				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 
-				return mapper.readFromSource(  getJavaType(), osonBytes, options);
+				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
+				JsonParser osonParser = osonFactory.createParser( osonBytes );
+				return mapper.readFromSource(  getJavaType(), osonParser, options);
 			}
 
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {

From 5ae78384130f7cc5a90ed27f209766019c67801d Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Fri, 10 Jan 2025 18:54:36 +0530
Subject: [PATCH 28/81] HHH-17404- Additional Fixes for Duration.

---
 .../java/org/hibernate/dialect/OracleDurationJdbcType.java   | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
index 6170d96dc184..fe6d7c3bc738 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -4,6 +4,7 @@
  */
 package org.hibernate.dialect;
 
+import oracle.jdbc.OracleTypes;
 import org.hibernate.engine.jdbc.Size;
 import org.hibernate.type.SqlTypes;
 import org.hibernate.type.Type;
@@ -106,7 +107,7 @@ public int getJdbcTypeCode() {
 	 * @since 6.2
 	 */
 	public int getDdlTypeCode() {
-		return SqlTypes.DURATION;
+		return OracleTypes.INTERVALDS;
 	}
 	/**
 	 * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type.
@@ -117,6 +118,6 @@ public int getDdlTypeCode() {
 	 * @return a JDBC type code
 	 */
 	public int getDefaultSqlTypeCode() {
-		return SqlTypes.DURATION;
+		return OracleTypes.INTERVALDS;
 	}
 }

From 9dcf37de2bfb38e1c7429093ea7b409f4983c465 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Sat, 11 Jan 2025 23:35:39 +0530
Subject: [PATCH 29/81] HHH-17404 - Add JSON_ARRAY serializer

---
 .../OracleOsonJacksonArrayJdbcType.java       |  36 +++++-
 .../jackson/JacksonOsonFormatMapper.java      | 122 ++++++++++++++----
 2 files changed, 129 insertions(+), 29 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index ea8fc7eb528e..692918f1e298 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -5,16 +5,21 @@
 package org.hibernate.dialect;
 
 import com.fasterxml.jackson.core.JsonFactory;
-import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.sql.json.OracleJsonDatum;
+import oracle.sql.json.OracleJsonFactory;
+import oracle.sql.json.OracleJsonGenerator;
+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.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.JsonJdbcType;
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
@@ -55,6 +60,26 @@ public String toString() {
 		return "OracleOsonJacksonArrayJdbcType";
 	}
 
+	private <X> void toOson(FormatMapper mapper,
+							X value,
+							JavaType<X> javaType,
+							OracleJsonGenerator osonGen,
+							WrapperOptions options) {
+		final JdbcType elementJdbcType = getElementJdbcType();
+		final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
+		if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) {
+			final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
+//			return JsonHelper.arrayToString( embeddableMappingType, domainObjects, options );
+			((JacksonOsonFormatMapper)mapper).arrayToOson(osonGen,embeddableMappingType,domainObjects,options);
+		}
+		else {
+			assert !( elementJdbcType instanceof AggregateJdbcType);
+			final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
+//			return JsonHelper.arrayToString( elementJavaType, elementJdbcType, domainObjects, options );
+			((JacksonOsonFormatMapper)mapper).arrayToOson(osonGen,elementJavaType,elementJdbcType,domainObjects,options);
+		}
+	}
+
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
@@ -64,9 +89,12 @@ private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions opt
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
-				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
-				JsonGenerator osonGen = osonFactory.createGenerator( out );
-				mapper.writeToTarget( value, javaType, osonGen, options );
+//				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
+//				JsonGenerator osonGen = osonFactory.createGenerator( out );
+				OracleJsonFactory oracleJsonFactory = new OracleJsonFactory();
+				OracleJsonGenerator osonGen = oracleJsonFactory.createJsonBinaryGenerator( out );
+				((OracleOsonJacksonArrayJdbcType)getJdbcType()).toOson(mapper,value,javaType,osonGen,options);
+//				mapper.writeToTarget( value, javaType, osonGen, options );
 				osonGen.close();
 				ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
 				return in;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 5f1c4164ecc4..714f0c80f9e3 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -19,6 +19,7 @@
 import oracle.sql.json.OracleJsonParser;
 import oracle.sql.json.OracleJsonTimestamp;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+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;
@@ -27,7 +28,7 @@
 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.jdbc.AggregateJdbcType;
 import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.format.JsonDocumentHandler;
@@ -190,44 +191,115 @@ public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object s
 		return (T)handler.getObjectArray();
 	}
 
-	public <X>byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options,EmbeddableMappingType embeddableMappingType) {
+	public <X>byte[] toOson(X value, WrapperOptions options,EmbeddableMappingType embeddableMappingType) {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
-		serializetoOson( value,generator,javaType,options,embeddableMappingType);
+		toOson( value,generator,options,embeddableMappingType);
 		generator.close();
 		return out.toByteArray();
 	}
 
-	private <X> void serializetoOson(X value, OracleJsonGenerator generator, JavaType<X> javaType, WrapperOptions options, EmbeddableMappingType embeddableMappingType) {
+	public void arrayToOson(OracleJsonGenerator osonGen,
+							EmbeddableMappingType elementMappingType,
+							Object[] values,
+							WrapperOptions options) {
+		if ( values.length == 0 ) {
+			osonGen.writeStartArray();
+			osonGen.writeEnd();
+			return;
+		}
+		osonGen.writeStartArray();
+		for ( Object value : values ) {
+			toOson( elementMappingType, value, options, osonGen);
+		}
+		osonGen.writeEnd();
+	}
+
+	private <X>void toOson(MappingType mappedType,
+						Object value, WrapperOptions options, OracleJsonGenerator osonGen) {
+		if (value == null) {
+			osonGen.writeNull();
+		}
+		else if ( mappedType instanceof EmbeddableMappingType ) {
+			toOson( (X) value, osonGen, options,(EmbeddableMappingType) mappedType );
+		}
+		else if ( mappedType instanceof BasicType<?> ) {
+			//noinspection unchecked
+			final BasicType<Object> basicType = (BasicType<Object>) mappedType;
+			convertedBasicValueToOson(basicType.convertToRelationalValue( value ),
+					options, osonGen, basicType);
+		}
+		else {
+			throw new UnsupportedOperationException( "Support for mapping type not yet implemented: " + mappedType.getClass().getName() );
+		}
+	}
+
+	private void convertedBasicValueToOson(Object value,
+										WrapperOptions options,
+										OracleJsonGenerator osonGen,
+										BasicType<Object> basicType) {
+		serializeValue(
+				value,
+				(JavaType<Object>) basicType.getJdbcJavaType(),
+				basicType.getJdbcType(),
+				options,
+				osonGen
+		);
+	}
+
+	public void arrayToOson(OracleJsonGenerator osonGen,
+							JavaType<?> elementJavaType,
+							JdbcType elementJdbcType,
+							Object[] values,
+							WrapperOptions options) {
+		if ( values.length == 0 ) {
+			osonGen.writeStartArray();
+			osonGen.writeEnd();
+		}
+
+		osonGen.writeStartArray();
+		for ( Object value : values ) {
+			//noinspection unchecked
+			convertedValueToOson((JavaType<Object>) elementJavaType, elementJdbcType, value, options, osonGen);
+		}
+		osonGen.writeEnd();
+	}
+
+	private void convertedValueToOson(JavaType<Object> javaType,
+									JdbcType jdbcType,
+									Object value,
+									WrapperOptions options,
+									OracleJsonGenerator osonGen) {
+		if ( value == null ) {
+			osonGen.writeNull();
+		}
+		else if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
+			toOson(value,  osonGen, options, aggregateJdbcType.getEmbeddableMappingType());
+		}
+		else {
+			serializeValue( value, javaType, jdbcType, options, osonGen );
+		}
+	}
+
+	private <X> void toOson(X value, OracleJsonGenerator generator, WrapperOptions options, EmbeddableMappingType embeddableMappingType) {
 		generator.writeStartObject();
-		serializetoOsonUtil( value, generator, javaType, options,embeddableMappingType );
+		toOsonUtil( value, generator, options,embeddableMappingType );
 		generator.writeEnd();
 	}
 
-	private <X> void serializetoOsonUtil(X value,
-										OracleJsonGenerator generator,
-										JavaType<X> javaType,
-										WrapperOptions options,
-										EmbeddableMappingType embeddableMappingType) {
+	private <X> void toOsonUtil(X value,
+								OracleJsonGenerator generator,
+								WrapperOptions options,
+								EmbeddableMappingType embeddableMappingType) {
 
 		final Object[] values = embeddableMappingType.getValues( value );
 		for ( int i = 0; i < values.length; i++ ) {
 			final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
 			if ( attributeMapping instanceof SelectableMapping ) {
 				final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
-				final BasicType<Object> basicType = (BasicType<Object>) attributeMapping.getMappedType();
 
 				generator.writeKey( name );
-
-				if (values[i] == null) {
-					generator.writeNull();
-					continue;
-				}
-				serializeValue( basicType.convertToRelationalValue( values[i] ),
-						(JavaType<Object>) basicType.getJdbcJavaType(),
-						basicType.getJdbcType(),
-						options,
-						generator);
+				toOson( attributeMapping.getMappedType(), values[i], options,generator );
 
 			}
 			else if (attributeMapping instanceof EmbeddedAttributeMapping) {
@@ -239,9 +311,8 @@ else if (attributeMapping instanceof EmbeddedAttributeMapping) {
 				}
 				if (aggregateMapping == null) {
 					// flattened case
-					serializetoOsonUtil( (X) values[i],
+					toOsonUtil( (X) values[i],
 							generator,
-							javaType,
 							options,
 							mappingType );
 				}
@@ -250,9 +321,8 @@ else if (attributeMapping instanceof EmbeddedAttributeMapping) {
 					final String name = aggregateMapping.getSelectableName();
 					generator.writeKey( name );
 					generator.writeStartObject();
-					serializetoOsonUtil( (X) values[i],
+					toOsonUtil( (X) values[i],
 							generator,
-							javaType,
 							options,
 							mappingType);
 					generator.writeEnd();
@@ -455,4 +525,6 @@ public boolean supportsTargetType(Class<?> targetType) {
 
 
 
+
+
 }

From 4a001e2518e7fbaf17d564e5665dec504e691fe1 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Wed, 15 Jan 2025 10:33:51 +0530
Subject: [PATCH 30/81] HHH-17404- Fix duration types

---
 .../org/hibernate/dialect/OracleDialect.java  |  5 ++++-
 .../dialect/OracleDurationJdbcType.java       | 22 +++++++++++++++++--
 .../dialect/OracleOsonJacksonJdbcType.java    |  6 +++--
 3 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 34d205cada78..170d2fb426d0 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -858,7 +858,7 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
 		}
 		// We need the DDL type during runtime to produce the proper encoding in certain functions
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( BIT, "number(1,0)", this ) );
-		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( DURATION, "interval day to second", this ) );
+		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( oracle.jdbc.OracleTypes.INTERVALDS, "interval day to second", this ) );
 		javaTypeRegistry.addDescriptor( OracleDurationJavaType.INSTANCE );
 
 	}
@@ -892,6 +892,8 @@ public JdbcType resolveSqlTypeDescriptor(
 		switch ( jdbcTypeCode ) {
 			case OracleTypes.JSON:
 				return jdbcTypeRegistry.getDescriptor( JSON );
+			case oracle.jdbc.OracleTypes.INTERVALDS:
+				return jdbcTypeRegistry.getDescriptor( DURATION );
 			case STRUCT:
 				if ( "MDSYS.SDO_GEOMETRY".equals( columnTypeName ) ) {
 					jdbcTypeCode = GEOMETRY;
@@ -1070,6 +1072,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 		typeContributions.contributeJdbcType( ObjectNullAsNullTypeJdbcType.INSTANCE );
 		// Oracle Stores the duration is ISO-8601 format.
 		typeContributions.contributeJdbcType( OracleDurationJdbcType.INSTANCE );
+		typeContributions.contributeJavaType( OracleDurationJavaType.INSTANCE );
 
 		// Until we remove StandardBasicTypes, we have to keep this
 		typeContributions.contributeType(
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
index fe6d7c3bc738..3530b10c801d 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -14,7 +14,8 @@
 import org.hibernate.type.descriptor.java.JavaType;
 import org.hibernate.type.descriptor.jdbc.BasicBinder;
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
-import org.hibernate.type.descriptor.jdbc.DurationJdbcType;
+import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
 
 import java.sql.CallableStatement;
@@ -23,7 +24,7 @@
 import java.sql.SQLException;
 import java.time.Duration;
 
-public class OracleDurationJdbcType extends DurationJdbcType {
+public class OracleDurationJdbcType implements JdbcType {
 
 	public static final OracleDurationJdbcType INSTANCE = new OracleDurationJdbcType();
 
@@ -120,4 +121,21 @@ public int getDdlTypeCode() {
 	public int getDefaultSqlTypeCode() {
 		return OracleTypes.INTERVALDS;
 	}
+
+	@Override
+	public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
+		return Duration.class;
+	}
+
+	@Override
+	public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
+		return (appender, value, dialect, wrapperOptions) -> dialect.appendIntervalLiteral(
+				appender,
+				javaType.unwrap(
+						value,
+						Duration.class,
+						wrapperOptions
+				)
+		);
+	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index c9f1de066d20..23788df123ab 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -159,12 +159,14 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
 				// can I use rs.getBinaryStream( paramIndex); ?
 				try {
-					if(javaType.getJavaTypeClass().isAssignableFrom( String.class )) {
-						return fromString( rs.getString( 1 ),javaType,options );
+					if(javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class) {
+						return fromString( rs.getString( paramIndex ),javaType,options );
 					}
+
 					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
 					return doExtraction(ojd,options);
 				}catch (SQLException e) {
+					// fallback to string if possible
 
 					throw new SQLException( e );
 				}

From 5824d23b3e99ae3c034fe57f3b16db5b19c4f4d1 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Tue, 14 Jan 2025 18:29:29 +0100
Subject: [PATCH 31/81] HHH-17404 : add JSON document handler for serialization

---
 .../internal/StrategySelectorBuilder.java     |  24 +-
 .../OracleOsonJacksonArrayJdbcType.java       |  50 +--
 .../dialect/OracleOsonJacksonJdbcType.java    |  29 +-
 .../descriptor/jdbc/JsonArrayJdbcType.java    |   8 +-
 .../type/descriptor/jdbc/JsonHelper.java      | 244 ++++++++----
 .../type/descriptor/jdbc/JsonJdbcType.java    |  13 +-
 .../type/format/JsonDocumentHandler.java      |  12 +-
 .../type/format/JsonDocumentWriter.java       |  83 ++++
 .../ObjectArrayOsonDocumentHandler.java       |  27 +-
 .../format/ObjectArrayOsonDocumentWriter.java | 241 ++++++++++++
 .../type/format/StringJsonDocumentWriter.java | 358 ++++++++++++++++++
 .../jackson/JacksonOsonFormatMapper.java      | 247 +++---------
 .../embeddable/NestedJsonEmbeddableTest.java  |   1 +
 13 files changed, 968 insertions(+), 369 deletions(-)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
 rename hibernate-core/src/main/java/org/hibernate/type/format/{jackson => }/ObjectArrayOsonDocumentHandler.java (94%)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
index 77150de87f70..480f746a5d61 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
@@ -45,6 +45,7 @@
 import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
 import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
 import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.jackson.JacksonIntegration;
 import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 import org.hibernate.type.format.jackson.JacksonXmlFormatMapper;
@@ -302,21 +303,24 @@ private static void addCacheKeysFactories(StrategySelectorImpl strategySelector)
 	}
 
 	private static void addJsonFormatMappers(StrategySelectorImpl strategySelector) {
-		strategySelector.registerStrategyImplementor(
-				FormatMapper.class,
-				JacksonJsonFormatMapper.SHORT_NAME,
-				JacksonJsonFormatMapper.class
-		);
 		strategySelector.registerStrategyImplementor(
 				FormatMapper.class,
 				JsonBJsonFormatMapper.SHORT_NAME,
 				JsonBJsonFormatMapper.class
 		);
-		strategySelector.registerStrategyImplementor(
-				FormatMapper.class,
-				JacksonOsonFormatMapper.SHORT_NAME,
-				JacksonOsonFormatMapper.class
-		);
+		if ( JacksonIntegration.isOracleOsonExtensionAvailable() ) {
+			strategySelector.registerStrategyImplementor(
+					FormatMapper.class,
+					JacksonOsonFormatMapper.SHORT_NAME,
+					JacksonOsonFormatMapper.class
+			);
+		} else {
+			strategySelector.registerStrategyImplementor(
+					FormatMapper.class,
+					JacksonJsonFormatMapper.SHORT_NAME,
+					JacksonJsonFormatMapper.class
+			);
+		}
 	}
 
 	private static void addXmlFormatMappers(StrategySelectorImpl strategySelector) {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index 692918f1e298..3af61871895a 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -7,24 +7,17 @@
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.sql.json.OracleJsonDatum;
-import oracle.sql.json.OracleJsonFactory;
-import oracle.sql.json.OracleJsonGenerator;
-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.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.JsonJdbcType;
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
@@ -33,10 +26,13 @@
 
 /**
  * @author Emmanuel Jannetti
+ * @author Bidyadhar Mohanty
  */
 public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
 
+
 	private static final Class osonFactoryKlass;
+
 	static {
 		try {
 			osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
@@ -45,7 +41,7 @@ public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
 			// should not happen as OracleOsonJacksonArrayJdbcType is loaded
 			// only when an Oracle OSON JDBC extension is present
 			// see OracleDialect class.
-			throw new ExceptionInInitializerError( "OracleOsonJacksonArrayJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
+			throw new ExceptionInInitializerError( "OracleOsonJacksonArrayJdbcType class loaded without OSON extension: " + e.getClass()+ " " + e.getMessage());
 		}
 	}
 
@@ -60,50 +56,21 @@ public String toString() {
 		return "OracleOsonJacksonArrayJdbcType";
 	}
 
-	private <X> void toOson(FormatMapper mapper,
-							X value,
-							JavaType<X> javaType,
-							OracleJsonGenerator osonGen,
-							WrapperOptions options) {
-		final JdbcType elementJdbcType = getElementJdbcType();
-		final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
-		if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) {
-			final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
-//			return JsonHelper.arrayToString( embeddableMappingType, domainObjects, options );
-			((JacksonOsonFormatMapper)mapper).arrayToOson(osonGen,embeddableMappingType,domainObjects,options);
-		}
-		else {
-			assert !( elementJdbcType instanceof AggregateJdbcType);
-			final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
-//			return JsonHelper.arrayToString( elementJavaType, elementJdbcType, domainObjects, options );
-			((JacksonOsonFormatMapper)mapper).arrayToOson(osonGen,elementJavaType,elementJdbcType,domainObjects,options);
-		}
-	}
 
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 		return new BasicBinder<>( javaType, this ) {
 
-			private <X> InputStream toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
+			private <X> InputStream toOsonStream(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-
-				ByteArrayOutputStream out = new ByteArrayOutputStream();
-//				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
-//				JsonGenerator osonGen = osonFactory.createGenerator( out );
-				OracleJsonFactory oracleJsonFactory = new OracleJsonFactory();
-				OracleJsonGenerator osonGen = oracleJsonFactory.createJsonBinaryGenerator( out );
-				((OracleOsonJacksonArrayJdbcType)getJdbcType()).toOson(mapper,value,javaType,osonGen,options);
-//				mapper.writeToTarget( value, javaType, osonGen, options );
-				osonGen.close();
-				ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
-				return in;
+				return  new ByteArrayInputStream(((JacksonOsonFormatMapper)mapper).arrayToOson(value, javaType,getElementJdbcType(),options));
 			}
 			@Override
 			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setBinaryStream( index, toOson( value, getJavaType(), options ) );
+					st.setBinaryStream( index, toOsonStream( value, getJavaType(), options ) );
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -114,7 +81,7 @@ protected void doBind(PreparedStatement st, X value, int index, WrapperOptions o
 			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setBinaryStream( name, toOson( value, getJavaType(), options ) );
+					st.setBinaryStream( name, toOsonStream( value, getJavaType(), options ) );
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -129,7 +96,6 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
-
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
 				JsonParser osonParser = osonFactory.createParser( osonBytes );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 23788df123ab..7acf03ab6e22 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -74,9 +74,11 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 			private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
 
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				// TODO : we should not have to do this.
-				((JacksonOsonFormatMapper)mapper).setEmbeddableMappingType( getEmbeddableMappingType());
 
+				if (getEmbeddableMappingType()!= null) {
+					return ((JacksonOsonFormatMapper)mapper).fromObjectArray(value,javaType,options,getEmbeddableMappingType());
+				}
+				
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
 				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
 				JsonGenerator osonGen = osonFactory.createGenerator( out );
@@ -112,13 +114,15 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions
 	@Override
 	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
+		if (javaType.getJavaTypeClass().isAssignableFrom( String.class )) {
+			return super.getExtractor(javaType);
+		}
+
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				// TODO : we should not have to do this.
-				((JacksonOsonFormatMapper)mapper).setEmbeddableMappingType( getEmbeddableMappingType() );
 
 				if (getEmbeddableMappingType() != null &&
 						getJavaType().getJavaTypeClass() == Object[].class) {
@@ -157,25 +161,13 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-				// can I use rs.getBinaryStream( paramIndex); ?
-				try {
-					if(javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class) {
-						return fromString( rs.getString( paramIndex ),javaType,options );
-					}
-
-					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-					return doExtraction(ojd,options);
-				}catch (SQLException e) {
-					// fallback to string if possible
-
-					throw new SQLException( e );
-				}
+				 OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+				 return doExtraction(ojd,options);
 
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-				// can I use rs.getBinaryStream( paramIndex); ?
 				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
 				return doExtraction(ojd,options);
 			}
@@ -183,7 +175,6 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 			@Override
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
-				// can I use rs.getBinaryStream( paramIndex); ?
 				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
 				return doExtraction(ojd,options);
 			}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java
index 1b64926ad1f0..6e2b3e640dab 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java
@@ -16,6 +16,7 @@
 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.StringJsonDocumentWriter;
 
 /**
  * Specialized type mapping for {@code JSON_ARRAY} and the JSON ARRAY SQL data type.
@@ -64,15 +65,18 @@ protected <X> X fromString(String string, JavaType<X> javaType, WrapperOptions o
 	protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) {
 		final JdbcType elementJdbcType = getElementJdbcType();
 		final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
 		if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) {
 			final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
-			return JsonHelper.arrayToString( embeddableMappingType, domainObjects, options );
+			JsonHelper.serializeArray( embeddableMappingType, domainObjects, options,  writer);
 		}
 		else {
 			assert !( elementJdbcType instanceof AggregateJdbcType );
 			final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
-			return JsonHelper.arrayToString( elementJavaType, elementJdbcType, domainObjects, options );
+			JsonHelper.serializeArray( elementJavaType, elementJdbcType, domainObjects, options, writer );
 		}
+		return sb.toString();
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 562249a97197..254b5fb04e91 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -5,6 +5,7 @@
 package org.hibernate.type.descriptor.jdbc;
 
 
+import java.io.IOException;
 import java.io.OutputStream;
 import java.lang.reflect.Array;
 import java.sql.SQLException;
@@ -40,7 +41,10 @@
 import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
 import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
 import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
-
+import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
+import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.format.JsonDocumentWriter;
 import static org.hibernate.type.descriptor.jdbc.StructHelper.getEmbeddedPart;
 import static org.hibernate.type.descriptor.jdbc.StructHelper.instantiate;
 
@@ -59,41 +63,144 @@ public static String toString(EmbeddableMappingType embeddableMappingType, Objec
 		return sb.toString();
 	}
 
-	public static String arrayToString(MappingType elementMappingType, Object[] values, WrapperOptions options) {
+
+	public static void serializeArray(MappingType elementMappingType, Object[] values, WrapperOptions options, JsonDocumentWriter writer) {
+		writer.startArray();
 		if ( values.length == 0 ) {
-			return "[]";
+			writer.endArray();
+			return;
 		}
-		final StringBuilder sb = new StringBuilder();
-		final JsonAppender jsonAppender = new JsonAppender( sb );
-		char separator = '[';
 		for ( Object value : values ) {
-			sb.append( separator );
-			toString( elementMappingType, value, options, jsonAppender );
-			separator = ',';
+			try {
+				if (value == null) {
+					writer.nullValue();
+				}
+				else {
+					JsonHelper.serialize( (EmbeddableMappingType) elementMappingType, value, options, writer );
+				}
+			}
+			catch (IOException e) {
+				// TODO : do better than this
+				throw new RuntimeException( e );
+			}
 		}
-		sb.append( ']' );
-		return sb.toString();
+		writer.endArray();
 	}
 
-	public static String arrayToString(
-			JavaType<?> elementJavaType,
-			JdbcType elementJdbcType,
-			Object[] values,
-			WrapperOptions options) {
+	public static void serializeArray(JavaType<?> elementJavaType, JdbcType elementJdbcType, Object[] values, WrapperOptions options, JsonDocumentWriter writer) {
+		writer.startArray();
 		if ( values.length == 0 ) {
-			return "[]";
+			writer.endArray();
+			return;
 		}
-		final StringBuilder sb = new StringBuilder();
-		final JsonAppender jsonAppender = new JsonAppender( sb );
-		char separator = '[';
 		for ( Object value : values ) {
-			sb.append( separator );
-			//noinspection unchecked
-			convertedValueToString( (JavaType<Object>) elementJavaType, elementJdbcType, value, options, jsonAppender );
-			separator = ',';
+			if (value == null) {
+				writer.nullValue();
+			}
+			else {
+				writer.serializeJsonValue( value ,(JavaType<Object>) elementJavaType,elementJdbcType,options);
+			}
+		}
+		writer.endArray();
+	}
+
+	/**
+	 * Checks that a JDBCType 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 a JSON 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 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 (values[i] == null) {
+					writer.nullValue();
+				}
+				else if (attributeMapping.getMappedType() instanceof BasicType<?>) {
+					final BasicType<Object> basicType = (BasicType<Object>) attributeMapping.getMappedType();
+					if ( isArrayType(basicType.getJdbcType())) {
+						final int length = Array.getLength( values[i] );
+						writer.startArray();
+						if ( length != 0 ) {
+							//noinspection unchecked
+							final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) basicType.getJdbcJavaType() ).getElementJavaType();
+							final JdbcType elementJdbcType = ( (ArrayJdbcType) basicType.getJdbcType() ).getElementJdbcType();
+							final Object domainArray = basicType.convertToRelationalValue(   values[i] );
+							for ( int j = 0; j < length; j++ ) {
+								writer.serializeJsonValue(Array.get(domainArray,j), elementJavaType, elementJdbcType, options);
+							}
+						}
+						writer.endArray();
+					}
+					else {
+						writer.serializeJsonValue(basicType.convertToRelationalValue( values[i]),
+								(JavaType<Object>)basicType.getJdbcJavaType(),basicType.getJdbcType(), options);
+					}
+				}
+				else if ( attributeMapping.getMappedType() instanceof EmbeddableMappingType ) {
+					writer.startObject();
+					serializeMapping(  (EmbeddableMappingType)attributeMapping.getMappedType(), values[i], options,writer);
+					writer.endObject();
+				}
+
+			}
+			else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
+				if ( values[i] == null ) {
+					//writer.nullValue();
+					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() );
+			}
+
 		}
-		sb.append( ']' );
-		return sb.toString();
 	}
 
 	private static void toString(EmbeddableMappingType embeddableMappingType, Object value, WrapperOptions options, JsonAppender appender) {
@@ -118,31 +225,29 @@ private static void toString(
 				appender.append( "\":" );
 				toString( attributeMapping.getMappedType(), values[i], options, appender );
 			}
-			else if ( attributeMapping instanceof EmbeddedAttributeMapping embeddedAttributeMapping ) {
+			else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
 				if ( values[i] == null ) {
-					// Skipping the update of the separator on purpose
+					// Skipping the update of the separator is on purpose
 					continue;
 				}
+				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
+				final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
+				if ( mappingType.shouldSelectAggregateMapping()) {
+					final String name = aggregateMapping.getSelectableName();
+					appender.append( separator );
+					appender.append( '"' );
+					appender.append( name );
+					appender.append( "\":" );
+					toString( mappingType, values[i], options, appender );
+				}
 				else {
-					final EmbeddableMappingType mappingType = embeddedAttributeMapping.getMappedType();
-					final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
-					if ( aggregateMapping == null ) {
-						toString(
-								mappingType,
-								options,
-								appender,
-								values[i],
-								separator
-						);
-					}
-					else {
-						final String name = aggregateMapping.getSelectableName();
-						appender.append( separator );
-						appender.append( '"' );
-						appender.append( name );
-						appender.append( "\":" );
-						toString( mappingType, values[i], options, appender );
-					}
+					toString(
+							mappingType,
+							options,
+							appender,
+							values[i],
+							separator
+					);
 				}
 			}
 			else {
@@ -159,7 +264,9 @@ private static void toString(MappingType mappedType, Object value, WrapperOption
 		else if ( mappedType instanceof EmbeddableMappingType embeddableMappingType ) {
 			toString( embeddableMappingType, value, options, appender );
 		}
-		else if ( mappedType instanceof BasicType<?> basicType ) {
+		else if ( mappedType instanceof BasicType<?> ) {
+			//noinspection unchecked
+			final BasicType<Object> basicType = (BasicType<Object>) mappedType;
 			convertedBasicValueToString( basicType.convertToRelationalValue( value ), options, appender, basicType );
 		}
 		else {
@@ -167,8 +274,8 @@ else if ( mappedType instanceof BasicType<?> basicType ) {
 		}
 	}
 
-	private static <T> void convertedValueToString(
-			JavaType<T> javaType,
+	private static void convertedValueToString(
+			JavaType<Object> javaType,
 			JdbcType jdbcType,
 			Object value,
 			WrapperOptions options,
@@ -180,41 +287,31 @@ else if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
 			toString( aggregateJdbcType.getEmbeddableMappingType(), value, options, appender );
 		}
 		else {
-			convertedCastBasicValueToString( value, options, appender, javaType, jdbcType );
+			convertedBasicValueToString( value, options, appender, javaType, jdbcType );
 		}
 	}
 
 
-	private static <T> void convertedBasicValueToString(
+	private static void convertedBasicValueToString(
 			Object value,
 			WrapperOptions options,
 			JsonAppender appender,
-			BasicType<T> basicType) {
-		convertedCastBasicValueToString(
+			BasicType<Object> basicType) {
+		//noinspection unchecked
+		convertedBasicValueToString(
 				value,
 				options,
 				appender,
-				basicType.getJdbcJavaType(),
+				(JavaType<Object>) basicType.getJdbcJavaType(),
 				basicType.getJdbcType()
 		);
 	}
 
-	private static <T> void convertedCastBasicValueToString(
+	private static void convertedBasicValueToString(
 			Object value,
 			WrapperOptions options,
 			JsonAppender appender,
-			JavaType<T> javaType,
-			JdbcType jdbcType) {
-		assert javaType.isInstance( value );
-		//noinspection unchecked
-		convertedBasicValueToString( (T) value, options, appender, javaType, jdbcType );
-	}
-
-	private static <T> void convertedBasicValueToString(
-			T value,
-			WrapperOptions options,
-			JsonAppender appender,
-			JavaType<T> javaType,
+			JavaType<Object> javaType,
 			JdbcType jdbcType) {
 		switch ( jdbcType.getDefaultSqlTypeCode() ) {
 			case SqlTypes.TINYINT:
@@ -326,12 +423,13 @@ private static <T> void convertedBasicValueToString(
 				final int length = Array.getLength( value );
 				appender.append( '[' );
 				if ( length != 0 ) {
-					final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
+					//noinspection unchecked
+					final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) javaType ).getElementJavaType();
 					final JdbcType elementJdbcType = ( (ArrayJdbcType) jdbcType ).getElementJdbcType();
-					final Object firstArrayElement = Array.get( value, 0 );
-					convertedValueToString( elementJavaType, elementJdbcType, firstArrayElement, options, appender );
+					Object arrayElement = Array.get( value, 0 );
+					convertedValueToString( elementJavaType, elementJdbcType, arrayElement, options, appender );
 					for ( int i = 1; i < length; i++ ) {
-						final Object arrayElement = Array.get( value, i );
+						arrayElement = Array.get( value, i );
 						appender.append( ',' );
 						convertedValueToString( elementJavaType, elementJdbcType, arrayElement, options, appender );
 					}
@@ -1376,7 +1474,7 @@ String expectedChars() {
 		}
 	}
 
-	private static class JsonAppender extends OutputStream implements SqlAppender {
+	public static class JsonAppender extends OutputStream implements SqlAppender {
 
 		private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
index f0631db28929..015280b910b3 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
@@ -4,6 +4,7 @@
  */
 package org.hibernate.type.descriptor.jdbc;
 
+import java.io.IOException;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -16,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.format.StringJsonDocumentWriter;
 
 /**
  * Specialized type mapping for {@code JSON} and the JSON SQL data type.
@@ -97,7 +99,16 @@ public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) t
 
 	protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) {
 		if ( embeddableMappingType != null ) {
-			return JsonHelper.toString( embeddableMappingType, value, options );
+			// used to be JsonHelper.toString( embeddableMappingType, value, options );
+			try {
+				StringBuilder sb = new StringBuilder();
+				StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+				JsonHelper.serialize( embeddableMappingType, value, options, writer);
+				return sb.toString();
+			}
+			catch (IOException e) {
+				throw new RuntimeException("Failed to serialize JSON mapping", e );
+			}
 		}
 		return options.getJsonFormatMapper().toString( value, javaType, options );
 	}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
index fd88db2fbd35..139e4b3683b8 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
@@ -3,10 +3,6 @@
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
 
 
 /**
@@ -21,22 +17,22 @@ public interface JsonDocumentHandler {
 	/**
 	 * Callback to be called when the start of an JSON object is encountered.
 	 */
-	void startObject();
+	void onStartObject();
 
 	/**
 	 * Callback to be called when the end of an JSON object is encountered.
 	 */
-	void endObject();
+	void onEndObject();
 
 	/**
 	 * Callback to be called when the start of an array is encountered.
 	 */
-	void startArray();
+	void onStartArray();
 
 	/**
 	 * Callback to be called when the end of an array is encountered.
 	 */
-	void endArray();
+	void onEndArray();
 
 	/**
 	 * Callback to be called when the key of JSON attribute is encountered.
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
new file mode 100644
index 000000000000..6a9923b1a9ed
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
@@ -0,0 +1,83 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+
+import java.io.IOException;
+
+/**
+ * JSON document producer.
+ * Used to parse JSON documents. Implementors of this will define
+ * proper callback implementations.
+ *
+ * @author Emmanuel Jannetti
+ */
+
+public interface JsonDocumentWriter {
+	/**
+	 * Callback to be called when the start of an JSON object is encountered.
+	 */
+	void startObject() throws IOException;
+
+	/**
+	 * Callback to be called when the end of an JSON object is encountered.
+	 */
+	void endObject() throws IOException;
+
+	/**
+	 * Callback to be called when the start of an array is encountered.
+	 */
+	void startArray();
+
+	/**
+	 * Callback to be called when the end of an array is encountered.
+	 */
+	void endArray();
+
+	/**
+	 * Callback to be called when the key of JSON attribute is encountered.
+	 * @param key the attribute name
+	 */
+	void objectKey(String key);
+
+	/**
+	 * Callback to be called when null value is encountered.
+	 */
+	void nullValue();
+
+	/**
+	 * Callback to be called when boolean value is encountered.
+	 * @param value the boolean value
+	 */
+	void booleanValue(boolean value);
+
+	/**
+	 * Callback to be called when string value is encountered.
+	 * @param value the String value
+	 */
+	void stringValue(String value);
+
+	/**
+	 * Callback to be called when Number value is encountered.
+	 * @param value the String value.
+	 */
+	void numberValue(Number value);
+
+	/**
+	 * Serialize a JSON value to the document
+	 * @param value the value to be serialized
+	 * @param javaType the Java type of the value
+	 * @param jdbcType the JDBC type for the value to be serialized
+	 * @param options the wrapping options
+	 */
+	void serializeJsonValue(Object value,
+							JavaType<Object> javaType,
+							JdbcType jdbcType,
+							WrapperOptions options);
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
similarity index 94%
rename from hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
rename to hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
index b8eb3a7c1542..0833fd8707ed 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/ObjectArrayOsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
@@ -2,7 +2,7 @@
  * SPDX-License-Identifier: LGPL-2.1-or-later
  * Copyright Red Hat Inc. and Hibernate Authors
  */
-package org.hibernate.type.format.jackson;
+package org.hibernate.type.format;
 
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.mapping.SelectableMapping;
@@ -10,7 +10,6 @@
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.UUIDJavaType;
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
-import org.hibernate.type.format.JsonDocumentHandler;
 
 import java.sql.Date;
 import java.sql.Time;
@@ -31,8 +30,8 @@
  */
 public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
 
-	// final result of mapped obejct array
-	private Object [] objectArrayResult;
+	// final result of a mapped object array
+	private final Object [] objectArrayResult;
 	// current mapping to be used
 	SelectableMapping currentSelectableMapping = null;
 	String currentKeyName = null;
@@ -42,7 +41,7 @@ public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
 	// mapping definitions are in a tree
 	// Each mapping definition may contain sub mappings (sub embeddable mapping)
 	// This stack is used to keep a pointer on the current mapping to be used to assign correct types.
-	// see startObject()/endObject() methods
+	// see onStartObject()/onEndObject() methods
 	Stack<EmbeddableMappingType> embeddableMappingTypes = new Stack<>();
 	// As for mapping definitions, when "sub embeddable" is encountered, the array
 	// that needs to be filled with Objects is the one we allocate in the final result array slot.
@@ -58,7 +57,7 @@ public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
 	public ObjectArrayOsonDocumentHandler(EmbeddableMappingType embeddableMappingType, WrapperOptions wrapperOptions) {
 		this.embeddableMappingTypes.push(embeddableMappingType);
 		this.wrapperOptions = wrapperOptions;
-		this.objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()];
+		this.objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()+ ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
 		this.objectArrays.push( this.objectArrayResult );
 	}
 
@@ -71,7 +70,7 @@ public ObjectArrayOsonDocumentHandler(EmbeddableMappingType embeddableMappingTyp
 	}
 
 	@Override
-	public void startObject() {
+	public void onStartObject() {
 		if (currentKeyName != null) {
 			// We are dealing with a sub-object, allocate space for it then,
 			// otherwise, we have nothing to do.
@@ -92,15 +91,15 @@ public void startObject() {
 	}
 
 	@Override
-	public void endObject() {
+	public void onEndObject() {
 		// go back in the mapping definition tree
 		this.embeddableMappingTypes.pop();
 		this.objectArrays.pop();
 	}
 
 	@Override
-	public void startArray() {
-		assert (subArrayObjectList == null && subArrayObjectTypes == null) : "startArray called twice ?";
+	public void onStartArray() {
+		assert (subArrayObjectList == null && subArrayObjectTypes == null) : "onStartArray called twice ?";
 
 		// initialize an array to gather values
 		subArrayObjectList = new ArrayList<>();
@@ -111,8 +110,8 @@ public void startArray() {
 	}
 
 	@Override
-	public void endArray() {
-		assert (subArrayObjectList != null && subArrayObjectTypes != null) : "endArray called before startArray";
+	public void onEndArray() {
+		assert (subArrayObjectList != null && subArrayObjectTypes != null) : "onEndArray called before onStartArray";
 		// flush array values
 		this.objectArrays.peek()[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, wrapperOptions );
 		// reset until we encounter next array element
@@ -203,7 +202,7 @@ public <T> void onOsonValue(T value) {
 	 * @param bytes the OSON byters
 	 */
 	public void onOsonBinaryValue(byte[] bytes) {
-		Class underlyingType = null;
+		Class underlyingType;
 		Object theOneToBeUsed;
 		if(subArrayObjectTypes!=null) {
 			underlyingType = subArrayObjectTypes.getElementType().getJavaType();
@@ -234,7 +233,7 @@ public void onOsonBinaryValue(byte[] bytes) {
 	 */
 	public void onOsonDateValue(LocalDateTime localDateTime) {
 
-		Class underlyingType = null;
+		Class underlyingType;
 		Object theOneToBeUsed = localDateTime;
 
 		if(subArrayObjectTypes!=null) {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
new file mode 100644
index 000000000000..3a5300e11ad3
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
@@ -0,0 +1,241 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import oracle.jdbc.driver.json.tree.OracleJsonDateImpl;
+import oracle.jdbc.driver.json.tree.OracleJsonTimestampImpl;
+import oracle.sql.DATE;
+import oracle.sql.TIMESTAMP;
+import oracle.sql.json.OracleJsonDate;
+import oracle.sql.json.OracleJsonGenerator;
+import oracle.sql.json.OracleJsonTimestamp;
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.UUIDJavaType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.UUID;
+
+/**
+ * Implementation of <code>JsonDocumentWriter</code> for OSON document.
+ * This implementation will produce an Object Array based on
+ * embeddable mapping
+ * Once All JSON document is handle the mapped Object array can be retrieved using the
+ * <code>getObjectArray()</code> method.
+ *
+ * @author Emmanuel Jannetti
+ */
+public class ObjectArrayOsonDocumentWriter implements JsonDocumentWriter {
+
+
+	private final OracleJsonGenerator generator;
+
+	public ObjectArrayOsonDocumentWriter(OracleJsonGenerator generator) {
+		this.generator = generator;
+	}
+
+
+	@Override
+	public void startObject() throws IOException {
+		this.generator.writeStartObject();
+	}
+
+
+	@Override
+	public void endObject() throws IOException {
+		this.generator.writeEnd();
+	}
+
+
+	@Override
+	public void startArray() {
+		generator.writeStartArray();
+	}
+
+
+	@Override
+	public void endArray() {
+		generator.writeEnd();
+	}
+
+
+	@Override
+	public void objectKey(String key) {
+		this.generator.writeKey( key );
+	}
+
+
+	@Override
+	public void nullValue() {
+		this.generator.writeNull();
+	}
+
+
+	@Override
+	public void booleanValue(boolean value) {
+		this.generator.write(value);
+	}
+
+
+	@Override
+	public void stringValue(String value) {
+		this.generator.write(value);
+	}
+
+
+	@Override
+	public void numberValue(Number value) {
+		this.generator.write((BigDecimal) value );
+	}
+
+	@Override
+	public void serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
+		serializeValue(value, javaType, jdbcType, options);
+	}
+
+
+	private void serializeValue(Object value,
+								JavaType<Object> javaType,
+								JdbcType jdbcType,
+								WrapperOptions options) {
+		switch ( jdbcType.getDefaultSqlTypeCode() ) {
+			case SqlTypes.TINYINT:
+			case SqlTypes.SMALLINT:
+			case SqlTypes.INTEGER:
+				if ( value instanceof Boolean ) {
+					// BooleanJavaType has this as an implicit conversion
+					int i = ((Boolean) value) ? 1 : 0;
+					generator.write( i );
+					break;
+				}
+				if ( value instanceof Enum ) {
+					generator.write( ((Enum<?>) value ).ordinal() );
+					break;
+				}
+				generator.write( javaType.unwrap( value,Integer.class,options ) );
+				break;
+			case SqlTypes.BOOLEAN:
+				generator.write( javaType.unwrap( value,Boolean.class,options ) );
+				break;
+			case SqlTypes.BIT:
+				generator.write( javaType.unwrap( value,Integer.class,options ) );
+				break;
+			case SqlTypes.BIGINT:
+				generator.write( javaType.unwrap( value, BigInteger.class,options ) );
+				break;
+			case SqlTypes.FLOAT:
+				generator.write( javaType.unwrap( value,Float.class,options ) );
+				break;
+			case SqlTypes.REAL:
+			case SqlTypes.DOUBLE:
+				generator.write( javaType.unwrap( value,Double.class,options ) );
+				break;
+			case SqlTypes.CHAR:
+			case SqlTypes.NCHAR:
+			case SqlTypes.VARCHAR:
+			case SqlTypes.NVARCHAR:
+				if ( value instanceof Boolean ) {
+					String c = ((Boolean) value) ? "Y" : "N";
+					generator.write( c );
+					break;
+				}
+			case SqlTypes.LONGVARCHAR:
+			case SqlTypes.LONGNVARCHAR:
+			case SqlTypes.LONG32VARCHAR:
+			case SqlTypes.LONG32NVARCHAR:
+			case SqlTypes.CLOB:
+			case SqlTypes.MATERIALIZED_CLOB:
+			case SqlTypes.NCLOB:
+			case SqlTypes.MATERIALIZED_NCLOB:
+			case SqlTypes.ENUM:
+			case SqlTypes.NAMED_ENUM:
+				// correct?
+				generator.write( javaType.unwrap( value,String.class,options ) );
+				break;
+			case SqlTypes.DATE:
+				DATE dd = new DATE(javaType.unwrap( value,java.sql.Date.class,options ));
+				OracleJsonDate jsonDate = new OracleJsonDateImpl(dd.shareBytes());
+				generator.write(jsonDate);
+				break;
+			case SqlTypes.TIME:
+			case SqlTypes.TIME_WITH_TIMEZONE:
+			case SqlTypes.TIME_UTC:
+				Time time = javaType.unwrap( value, Time.class,options );
+				generator.write( time.toString() );
+				break;
+			case SqlTypes.TIMESTAMP:
+				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( value, Timestamp.class, options ));
+				OracleJsonTimestamp writeTimeStamp = new OracleJsonTimestampImpl(TS.shareBytes());
+				generator.write(writeTimeStamp);
+				break;
+			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
+				try {
+					OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
+					generator.write( dateTime );
+				}
+				catch (Exception e) {
+					Timestamp tswtz = javaType.unwrap( value, Timestamp.class, options );
+					TIMESTAMP TSWTZ = new TIMESTAMP(tswtz);
+					OracleJsonTimestamp writeTimeStampWTZ = new OracleJsonTimestampImpl(TSWTZ.shareBytes());
+					generator.write(writeTimeStampWTZ);
+				}
+				break;
+			case SqlTypes.TIMESTAMP_UTC:
+				if( value instanceof OffsetDateTime ) {
+					OffsetDateTime odt = javaType.unwrap( value, OffsetDateTime.class, options );
+					generator.write( odt );
+					break;
+				}
+				else if (value instanceof Instant ) {
+					Instant instant = javaType.unwrap( value, Instant.class, options );
+					generator.write(instant.atOffset( ZoneOffset.UTC )  );
+					break;
+				}
+				generator.write( javaType.unwrap( value,String.class,options ) );
+				break;
+			case SqlTypes.NUMERIC:
+			case SqlTypes.DECIMAL:
+				BigDecimal bd = javaType.unwrap( value, BigDecimal.class, options );
+				generator.write( bd );
+				break;
+
+			case SqlTypes.DURATION:
+				Duration duration = javaType.unwrap( value, Duration.class, options );
+				generator.write( duration );
+				break;
+			case SqlTypes.UUID:
+				generator.write( UUIDJavaType.INSTANCE.unwrap( (UUID)value, byte[].class, options ) );
+				break;
+			case SqlTypes.BINARY:
+			case SqlTypes.VARBINARY:
+			case SqlTypes.LONGVARBINARY:
+			case SqlTypes.LONG32VARBINARY:
+			case SqlTypes.BLOB:
+			case SqlTypes.MATERIALIZED_BLOB:
+				// how to handle
+				byte[] bytes = javaType.unwrap( value, byte[].class, options );
+				generator.write( bytes );
+				break;
+			case SqlTypes.ARRAY:
+			case SqlTypes.JSON_ARRAY:
+				assert false:"array case should be treated at upper level";
+				break;
+			default:
+				throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
+		}
+
+	}
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
new file mode 100644
index 000000000000..6bac99e8bc1a
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -0,0 +1,358 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import org.hibernate.dialect.JsonHelper;
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.BooleanJavaType;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.JdbcDateJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+
+import java.io.IOException;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Stack;
+
+/**
+ * JsonDocument String writer implementation
+ * @author Emmanuel Jannetti
+ */
+public class StringJsonDocumentWriter implements JsonDocumentWriter{
+
+	private static final char ARRAY_END_MARKER = ']';
+	private static final char ARRAY_START_MARKER = '[';
+	private static final char OBJECT_END_MARKER = '}';
+	private static final char OBJECT_START_MARKER = '{';
+	private static final char SEPARATOR_MARKER = ',';
+	private static final char TOKEN_QUOTE = '"';
+
+	private JsonHelper.JsonAppender appender;
+
+	/**
+	 * Processing states. This can be (nested)Object or Arrays.
+	 * When processing objects, values are stored as [,]"key":"value"[,]. we add separator when adding new key
+	 * When processing arrays, values are stored as [,]"value"[,]. we add separator when adding new value
+	 */
+	private enum PROCESSING_STATE {
+		NONE,
+		STARTING_OBJECT, // object started but no value added
+		OBJECT, // object started, and we've started adding key/value pairs
+		ENDING_OBJECT, // we are ending an object
+		STARTING_ARRAY,  // array started but no value added
+		ENDING_ARRAY,  // we are ending an array
+		ARRAY // we are piling array values
+	}
+	private Stack<PROCESSING_STATE> processingStates = new Stack<>();
+
+
+	public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
+		this.processingStates.push( PROCESSING_STATE.NONE );
+		this.appender = appender;
+	}
+
+	/**
+	 * Callback to be called when the start of an JSON object is encountered.
+	 */
+	@Override
+	public void startObject() throws IOException {
+		// Note: startArray and startObject must not call moveProcessingStateMachine()
+		if (this.processingStates.peek() == PROCESSING_STATE.STARTING_ARRAY) {
+			// are we building an array of objects?
+			// i.e, [{},...]
+			// move to PROCESSING_STATE.ARRAY first
+			this.processingStates.push( PROCESSING_STATE.ARRAY);
+		}
+		else if (this.processingStates.peek() == PROCESSING_STATE.ARRAY) {
+			// That means that we ae building an array of object ([{},...])
+			// JSON object hee are treat as array item.
+			// -> add the marker first
+			this.appender.append(SEPARATOR_MARKER);
+		}
+		this.appender.append( OBJECT_START_MARKER);
+		this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
+	}
+
+	/**
+	 * Callback to be called when the end of an JSON object is encountered.
+	 */
+	@Override
+	public void endObject() throws IOException {
+		this.appender.append( OBJECT_END_MARKER );
+		this.processingStates.push( PROCESSING_STATE.ENDING_OBJECT);
+		moveProcessingStateMachine();
+	}
+
+	/**
+	 * Callback to be called when the start of an array is encountered.
+	 */
+	@Override
+	public void startArray() {
+		this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
+		// Note: startArray and startObject do not call moveProcessingStateMachine()
+		this.appender.append( ARRAY_START_MARKER );
+
+	}
+
+	/**
+	 * Callback to be called when the end of an array is encountered.
+	 */
+	@Override
+	public void endArray() {
+		this.appender.append( ARRAY_END_MARKER );
+		this.processingStates.push( PROCESSING_STATE.ENDING_ARRAY);
+		moveProcessingStateMachine();
+	}
+
+
+	@Override
+	public void objectKey(String key) {
+		if (this.processingStates.peek().equals( PROCESSING_STATE.OBJECT )) {
+			// we have started an object, and we are adding an item key: we do add a separator.
+			this.appender.append( SEPARATOR_MARKER );
+		}
+		this.appender.append( TOKEN_QUOTE );
+		this.appender.append( key );
+		this.appender.append( "\":" );
+		moveProcessingStateMachine();
+	}
+
+	/**
+	 * Adds a separator if needed.
+	 * The logic here is know if we have to prepend a separator
+	 * as such, it must be called at the beginning of all methods
+	 * Separator is to separate array items or key/value pairs in an object.
+	 */
+	private void addItemsSeparator() {
+		if (this.processingStates.peek().equals( PROCESSING_STATE.ARRAY )) {
+			// We started to serialize an array and already added item to it:add a separator anytime.
+			this.appender.append( SEPARATOR_MARKER );
+		}
+	}
+
+	/**
+	 * Changes the current processing state.
+	 * we are called after an item (array item or object item) has been added,
+	 * do whatever it takes to move away from the current state by picking up the next logical one.
+	 * <p>
+	 * We have to deal with two kinds of (possibly empty) structure
+	 * <ul>
+	 *     <li>array of objects and values [{},null,{},"foo", ...]</li>
+	 *     <li>objects than have array as attribute value {k1:v1, k2:[v21,v22,..], k3:v3, k4:null, ...}</li>
+	 * </ul>
+	 *   <pre>
+	 *   NONE -> SA -> (A,...) --> SO -> O -> EO -> A
+	 *                         --> EA -> NONE
+	 *              -> EA  -> NONE
+	 *
+	 *        -> SO -> (O,...) ------------------> SA -> A -> EA -> O
+	 *                         --> EO -> NONE         -> EA -> O
+	 *              -> EO -> NONE
+	 *
+	 *    </pre>
+	 *
+	 */
+	private void moveProcessingStateMachine() {
+		switch (this.processingStates.peek()) {
+			case STARTING_OBJECT:
+				//after starting an object, we start adding key/value pairs
+				this.processingStates.push( PROCESSING_STATE.OBJECT );
+				break;
+			case STARTING_ARRAY:
+				//after starting an object, we start adding value to it
+				this.processingStates.push( PROCESSING_STATE.ARRAY );
+				break;
+			case ENDING_ARRAY:
+				// when ending an array, we have one or two states.
+				//   ARRAY (unless this is an empty array)
+				//   STARTING_ARRAY
+				// first pop ENDING_ARRAY
+				this.processingStates.pop();
+				if (this.processingStates.peek().equals( PROCESSING_STATE.ARRAY ))
+					this.processingStates.pop();
+				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_ARRAY );
+				break;
+			case ENDING_OBJECT:
+				// when ending an object, we have one or two states.
+				//   OBJECT (unless this is an empty object)
+				//   STARTING_OBJECT
+				// first pop ENDING_OBJECT
+				this.processingStates.pop();
+				if (this.processingStates.peek().equals( PROCESSING_STATE.OBJECT ))
+					this.processingStates.pop();
+				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_OBJECT );
+				break;
+			default:
+				//nothing to do for the other ones.
+		}
+	}
+
+	@Override
+	public void nullValue() {
+		addItemsSeparator();
+		this.appender.append( "null" );
+		moveProcessingStateMachine();
+	}
+
+	@Override
+	public void booleanValue(boolean value) {
+		addItemsSeparator();
+		BooleanJavaType.INSTANCE.appendEncodedString( this.appender, value);
+		moveProcessingStateMachine();
+	}
+
+	@Override
+	public void stringValue(String value) {
+		addItemsSeparator();
+
+		appender.append( TOKEN_QUOTE);
+		appender.startEscaping();
+		appender.append( value );
+		appender.endEscaping();
+		appender.append(TOKEN_QUOTE );
+
+		moveProcessingStateMachine();
+
+	}
+
+	@Override
+	public void numberValue(Number value) {
+		addItemsSeparator();
+		this.appender.append( value.toString() );
+		moveProcessingStateMachine();
+	}
+
+
+	@Override
+	public void serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
+		addItemsSeparator();
+		convertedBasicValueToString(value, options,this.appender,javaType,jdbcType);
+		moveProcessingStateMachine();
+	}
+
+	private  void convertedBasicValueToString(
+			Object value,
+			WrapperOptions options,
+			JsonHelper.JsonAppender appender,
+			JavaType<Object> javaType,
+			JdbcType jdbcType) {
+		switch ( jdbcType.getDefaultSqlTypeCode() ) {
+			case SqlTypes.TINYINT:
+			case SqlTypes.SMALLINT:
+			case SqlTypes.INTEGER:
+				if ( value instanceof Boolean ) {
+					// BooleanJavaType has this as an implicit conversion
+					appender.append( (Boolean) value ? '1' : '0' );
+					break;
+				}
+				if ( value instanceof Enum ) {
+					appender.appendSql( ((Enum<?>) value ).ordinal() );
+					break;
+				}
+			case SqlTypes.BOOLEAN:
+			case SqlTypes.BIT:
+			case SqlTypes.BIGINT:
+			case SqlTypes.FLOAT:
+			case SqlTypes.REAL:
+			case SqlTypes.DOUBLE:
+				// These types fit into the native representation of JSON, so let's use that
+				javaType.appendEncodedString( appender, value );
+				break;
+			case SqlTypes.CHAR:
+			case SqlTypes.NCHAR:
+			case SqlTypes.VARCHAR:
+			case SqlTypes.NVARCHAR:
+				if ( value instanceof Boolean ) {
+					// BooleanJavaType has this as an implicit conversion
+					appender.append( TOKEN_QUOTE );
+					appender.append( (Boolean) value ? 'Y' : 'N' );
+					appender.append( TOKEN_QUOTE);
+					break;
+				}
+			case SqlTypes.LONGVARCHAR:
+			case SqlTypes.LONGNVARCHAR:
+			case SqlTypes.LONG32VARCHAR:
+			case SqlTypes.LONG32NVARCHAR:
+			case SqlTypes.CLOB:
+			case SqlTypes.MATERIALIZED_CLOB:
+			case SqlTypes.NCLOB:
+			case SqlTypes.MATERIALIZED_NCLOB:
+			case SqlTypes.ENUM:
+			case SqlTypes.NAMED_ENUM:
+				// These literals can contain the '"' character, so we need to escape it
+				appender.append( TOKEN_QUOTE );
+				appender.startEscaping();
+				javaType.appendEncodedString( appender, value );
+				appender.endEscaping();
+				appender.append( TOKEN_QUOTE );
+				break;
+			case SqlTypes.DATE:
+				appender.append( TOKEN_QUOTE );
+				JdbcDateJavaType.INSTANCE.appendEncodedString(
+						appender,
+						javaType.unwrap( value, java.sql.Date.class, options )
+				);
+				appender.append( TOKEN_QUOTE );
+				break;
+			case SqlTypes.TIME:
+			case SqlTypes.TIME_WITH_TIMEZONE:
+			case SqlTypes.TIME_UTC:
+				appender.append( TOKEN_QUOTE );
+				JdbcTimeJavaType.INSTANCE.appendEncodedString(
+						appender,
+						javaType.unwrap( value, java.sql.Time.class, options )
+				);
+				appender.append( TOKEN_QUOTE );
+				break;
+			case SqlTypes.TIMESTAMP:
+				appender.append( TOKEN_QUOTE );
+				JdbcTimestampJavaType.INSTANCE.appendEncodedString(
+						appender,
+						javaType.unwrap( value, java.sql.Timestamp.class, options )
+				);
+				appender.append( TOKEN_QUOTE );
+				break;
+			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
+			case SqlTypes.TIMESTAMP_UTC:
+				appender.append( TOKEN_QUOTE );
+				DateTimeFormatter.ISO_OFFSET_DATE_TIME.formatTo(
+						javaType.unwrap( value, OffsetDateTime.class, options ),
+						appender
+				);
+				appender.append( TOKEN_QUOTE );
+				break;
+			case SqlTypes.DECIMAL:
+			case SqlTypes.NUMERIC:
+			case SqlTypes.DURATION:
+			case SqlTypes.UUID:
+				// These types need to be serialized as JSON string, but don't have a need for escaping
+				appender.append( TOKEN_QUOTE );
+				javaType.appendEncodedString( appender, value );
+				appender.append( TOKEN_QUOTE );
+				break;
+			case SqlTypes.BINARY:
+			case SqlTypes.VARBINARY:
+			case SqlTypes.LONGVARBINARY:
+			case SqlTypes.LONG32VARBINARY:
+			case SqlTypes.BLOB:
+			case SqlTypes.MATERIALIZED_BLOB:
+				// These types need to be serialized as JSON string, and for efficiency uses appendString directly
+				appender.append( TOKEN_QUOTE );
+				appender.write( javaType.unwrap( value, byte[].class, options ) );
+				appender.append( TOKEN_QUOTE );
+				break;
+			case SqlTypes.ARRAY:
+			case SqlTypes.JSON_ARRAY:
+				// Caller handles this. We should never end up here actually.
+				break;
+			default:
+				throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
+		}
+	}
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 714f0c80f9e3..2ffdf09982a9 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -9,43 +9,29 @@
 import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.databind.SerializationFeature;
-import oracle.jdbc.driver.json.tree.OracleJsonDateImpl;
-import oracle.jdbc.driver.json.tree.OracleJsonTimestampImpl;
-import oracle.sql.DATE;
-import oracle.sql.TIMESTAMP;
-import oracle.sql.json.OracleJsonDate;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
 import oracle.sql.json.OracleJsonParser;
-import oracle.sql.json.OracleJsonTimestamp;
+import org.hibernate.dialect.JsonHelper;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 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.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.descriptor.jdbc.AggregateJdbcType;
-import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
 import org.hibernate.type.format.JsonDocumentHandler;
+import org.hibernate.type.format.ObjectArrayOsonDocumentHandler;
+import org.hibernate.type.format.ObjectArrayOsonDocumentWriter;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.lang.reflect.Array;
-import java.math.BigDecimal;
-import java.math.BigInteger;
 import java.nio.ByteBuffer;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
-import java.util.UUID;
 
 
 /**
@@ -70,9 +56,6 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 		}
 	}
 
-	// TODO : remove the use of this once the OSON writer has been refactor to Document handling
-	private EmbeddableMappingType embeddableMappingType = null;
-
 	/**
 	 * Creates a new JacksonOsonFormatMapper
 	 */
@@ -104,10 +87,10 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 					handler.onObjectKey( osonParser.getString() );
 					break;
 				case OracleJsonParser.Event.START_ARRAY:
-					handler.startArray();
+					handler.onStartArray();
 					break;
 				case OracleJsonParser.Event.END_ARRAY:
-					handler.endArray();
+					handler.onEndArray();
 					break;
 				case OracleJsonParser.Event.VALUE_DATE:
 				case OracleJsonParser.Event.VALUE_TIMESTAMP:
@@ -155,10 +138,10 @@ private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Eve
 							osonParser.getBytes());
 					break;
 				case OracleJsonParser.Event.START_OBJECT:
-					handler.startObject();
+					handler.onStartObject();
 					break;
 				case OracleJsonParser.Event.END_OBJECT:
-					handler.endObject();
+					handler.onEndObject();
 					break;
 				default:
 					throw new IOException( "Unknown OSON event " + event );
@@ -191,24 +174,53 @@ public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object s
 		return (T)handler.getObjectArray();
 	}
 
-	public <X>byte[] toOson(X value, WrapperOptions options,EmbeddableMappingType embeddableMappingType) {
+	public <X>byte[] fromObjectArray(X value, JavaType<X> javaType, WrapperOptions options,EmbeddableMappingType embeddableMappingType)
+			throws IOException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
+		ObjectArrayOsonDocumentWriter writer = new ObjectArrayOsonDocumentWriter(generator);
+		JsonHelper.serialize( embeddableMappingType, value,options,writer);
+		generator.close();
+		return out.toByteArray();
+	}
+
+	public <X>byte[] arrayToOson(X value,
+								JavaType<X> javaType,
+								JdbcType elementJdbcType,
+								WrapperOptions options) {
+
+		final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
+
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
-		toOson( value,generator,options,embeddableMappingType);
+		ObjectArrayOsonDocumentWriter writer = new ObjectArrayOsonDocumentWriter(generator);
+
+		if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) {
+			final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
+			JsonHelper.serializeArray( embeddableMappingType, domainObjects, options,  writer);
+		}
+		else {
+			assert !( elementJdbcType instanceof AggregateJdbcType);
+			final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
+			JsonHelper.serializeArray( elementJavaType, elementJdbcType, domainObjects, options, writer );
+		}
+
 		generator.close();
 		return out.toByteArray();
 	}
 
-	public void arrayToOson(OracleJsonGenerator osonGen,
+	public void _arrayToOson(OracleJsonGenerator osonGen,
 							EmbeddableMappingType elementMappingType,
 							Object[] values,
 							WrapperOptions options) {
+
+		osonGen.writeStartArray();
+
 		if ( values.length == 0 ) {
-			osonGen.writeStartArray();
 			osonGen.writeEnd();
 			return;
 		}
-		osonGen.writeStartArray();
+
 		for ( Object value : values ) {
 			toOson( elementMappingType, value, options, osonGen);
 		}
@@ -247,7 +259,11 @@ private void convertedBasicValueToOson(Object value,
 		);
 	}
 
-	public void arrayToOson(OracleJsonGenerator osonGen,
+	private void serializeValue(Object value, JavaType<Object> jdbcJavaType, JdbcType jdbcType, WrapperOptions options, OracleJsonGenerator osonGen) {
+		//TODO: remove me.
+	}
+
+	public void __arrayToOson(OracleJsonGenerator osonGen,
 							JavaType<?> elementJavaType,
 							JdbcType elementJdbcType,
 							Object[] values,
@@ -333,172 +349,6 @@ else if (attributeMapping instanceof EmbeddedAttributeMapping) {
 		}
 	}
 
-	private void serializeValue(Object value,
-								JavaType<Object> javaType,
-								JdbcType jdbcType,
-								WrapperOptions options,
-								OracleJsonGenerator generator) {
-		switch ( jdbcType.getDefaultSqlTypeCode() ) {
-			case SqlTypes.TINYINT:
-			case SqlTypes.SMALLINT:
-			case SqlTypes.INTEGER:
-				if ( value instanceof Boolean ) {
-					// BooleanJavaType has this as an implicit conversion
-					int i = ((Boolean) value) ? 1 : 0;
-					generator.write( i );
-					break;
-				}
-				if ( value instanceof Enum ) {
-					generator.write( ((Enum<?>) value ).ordinal() );
-					break;
-				}
-				generator.write( javaType.unwrap( value,Integer.class,options ) );
-				break;
-			case SqlTypes.BOOLEAN:
-				generator.write( javaType.unwrap( value,Boolean.class,options ) );
-				break;
-			case SqlTypes.BIT:
-				generator.write( javaType.unwrap( value,Integer.class,options ) );
-				break;
-			case SqlTypes.BIGINT:
-				generator.write( javaType.unwrap( value,BigInteger.class,options ) );
-				break;
-			case SqlTypes.FLOAT:
-				generator.write( javaType.unwrap( value,Float.class,options ) );
-				break;
-			case SqlTypes.REAL:
-			case SqlTypes.DOUBLE:
-				generator.write( javaType.unwrap( value,Double.class,options ) );
-				break;
-			case SqlTypes.CHAR:
-			case SqlTypes.NCHAR:
-			case SqlTypes.VARCHAR:
-			case SqlTypes.NVARCHAR:
-				if ( value instanceof Boolean ) {
-					String c = ((Boolean) value) ? "Y" : "N";
-					generator.write( c );
-					break;
-				}
-			case SqlTypes.LONGVARCHAR:
-			case SqlTypes.LONGNVARCHAR:
-			case SqlTypes.LONG32VARCHAR:
-			case SqlTypes.LONG32NVARCHAR:
-			case SqlTypes.CLOB:
-			case SqlTypes.MATERIALIZED_CLOB:
-			case SqlTypes.NCLOB:
-			case SqlTypes.MATERIALIZED_NCLOB:
-			case SqlTypes.ENUM:
-			case SqlTypes.NAMED_ENUM:
-				// correct?
-				generator.write( javaType.unwrap( value,String.class,options ) );
-				break;
-			case SqlTypes.DATE:
-				DATE dd = new DATE(javaType.unwrap( value,java.sql.Date.class,options ));
-				OracleJsonDate jsonDate = new OracleJsonDateImpl(dd.shareBytes());
-				generator.write(jsonDate);
-				break;
-			case SqlTypes.TIME:
-			case SqlTypes.TIME_WITH_TIMEZONE:
-			case SqlTypes.TIME_UTC:
-				Time time = javaType.unwrap( value, Time.class,options );
-				generator.write( time.toString() );
-				break;
-			case SqlTypes.TIMESTAMP:
-				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( value, Timestamp.class, options ));
-				OracleJsonTimestamp writeTimeStamp = new OracleJsonTimestampImpl(TS.shareBytes());
-				generator.write(writeTimeStamp);
-				break;
-			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
-				try {
-					OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
-					generator.write( dateTime );
-				}
-				catch (Exception e) {
-					Timestamp tswtz = javaType.unwrap( value, Timestamp.class, options );
-					TIMESTAMP TSWTZ = new TIMESTAMP(tswtz);
-					OracleJsonTimestamp writeTimeStampWTZ = new OracleJsonTimestampImpl(TSWTZ.shareBytes());
-					generator.write(writeTimeStampWTZ);
-				}
-				break;
-			case SqlTypes.TIMESTAMP_UTC:
-				if( value instanceof OffsetDateTime ) {
-					OffsetDateTime odt = javaType.unwrap( value, OffsetDateTime.class, options );
-					generator.write( odt );
-					break;
-				}
-				else if (value instanceof Instant ) {
-					Instant instant = javaType.unwrap( value, Instant.class, options );
-					generator.write(instant.atOffset( ZoneOffset.UTC )  );
-					break;
-				}
-				generator.write( javaType.unwrap( value,String.class,options ) );
-				break;
-			case SqlTypes.NUMERIC:
-			case SqlTypes.DECIMAL:
-				BigDecimal bd = javaType.unwrap( value, BigDecimal.class, options );
-				generator.write( bd );
-				break;
-
-			case SqlTypes.DURATION:
-				Duration duration = javaType.unwrap( value, Duration.class, options );
-				generator.write( duration );
-				break;
-			case SqlTypes.UUID:
-				UUID uuid = javaType.unwrap( value, UUID.class, options );
-				byte[] uuidBytes = _asBytes( uuid );
-				generator.write( uuidBytes );
-				break;
-			case SqlTypes.BINARY:
-			case SqlTypes.VARBINARY:
-			case SqlTypes.LONGVARBINARY:
-			case SqlTypes.LONG32VARBINARY:
-			case SqlTypes.BLOB:
-			case SqlTypes.MATERIALIZED_BLOB:
-				// how to handle
-				byte[] bytes = javaType.unwrap( value, byte[].class, options );
-				generator.write( bytes );
-				break;
-			case SqlTypes.ARRAY:
-			case SqlTypes.JSON_ARRAY:
-				final int length = Array.getLength( value );
-				generator.writeStartArray();
-				if ( length != 0 ) {
-					//noinspection unchecked
-					final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) javaType ).getElementJavaType();
-					final JdbcType elementJdbcType = ( (ArrayJdbcType) jdbcType ).getElementJdbcType();
-
-					for ( int i = 0; i < length; i++ ) {
-						Object arrayElement = Array.get( value, i );
-						serializeValue( arrayElement,elementJavaType, elementJdbcType, options, generator );
-					}
-				}
-				generator.writeEnd();
-				break;
-			default:
-				throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
-		}
-
-	}
-	private byte[] _asBytes(UUID uuid)
-	{
-		byte[] buffer = new byte[16];
-		long hi = uuid.getMostSignificantBits();
-		long lo = uuid.getLeastSignificantBits();
-		_appendInt((int) (hi >> 32), buffer, 0);
-		_appendInt((int) hi, buffer, 4);
-		_appendInt((int) (lo >> 32), buffer, 8);
-		_appendInt((int) lo, buffer, 12);
-		return buffer;
-	}
-
-	private void _appendInt(int value, byte[] buffer, int offset)
-	{
-		buffer[offset] = (byte) (value >> 24);
-		buffer[++offset] = (byte) (value >> 16);
-		buffer[++offset] = (byte) (value >> 8);
-		buffer[++offset] = (byte) value;
-	}
-
 
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
@@ -524,7 +374,4 @@ public boolean supportsTargetType(Class<?> targetType) {
 	}
 
 
-
-
-
 }
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
index a1f25a98d226..054c2a860eda 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
@@ -88,6 +88,7 @@ public void testUpdate() {
 
 	@Test
 	public void testFetch() {
+
 		sessionFactoryScope().inSession(
 				entityManager -> {
 					List<JsonHolder> jsonHolders = entityManager.createQuery( "from JsonHolder b where b.id = 1", JsonHolder.class ).getResultList();

From bb606ec9d44865deb6b10e832677246e26866193 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Wed, 15 Jan 2025 11:04:43 +0530
Subject: [PATCH 32/81] HHH-17404 - Fix extractor

---
 .../java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 7acf03ab6e22..870efd4f0aef 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -114,8 +114,8 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions
 	@Override
 	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
-		if (javaType.getJavaTypeClass().isAssignableFrom( String.class )) {
-			return super.getExtractor(javaType);
+		if(javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class) {
+			return super.getExtractor( javaType );
 		}
 
 		return new BasicExtractor<>( javaType, this ) {

From ee559bc084bc34b5f75edb31c39b3e630cd62522 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Wed, 15 Jan 2025 16:16:14 +0530
Subject: [PATCH 33/81] HHH-17404- UUID fix

---
 .../main/java/org/hibernate/dialect/OracleDialect.java    | 3 ---
 .../org/hibernate/dialect/OracleDurationJdbcType.java     | 4 ++--
 .../dialect/aggregate/OracleAggregateSupport.java         | 8 +++++++-
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 170d2fb426d0..3a2b37689e74 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -92,7 +92,6 @@
 import org.hibernate.type.NullType;
 import org.hibernate.type.StandardBasicTypes;
 import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
-import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
 import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.descriptor.jdbc.NullJdbcType;
@@ -838,7 +837,6 @@ protected String columnType(int sqlTypeCode) {
 	protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
 		super.registerColumnTypes( typeContributions, serviceRegistry );
 		final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
-		final JavaTypeRegistry javaTypeRegistry = typeContributions.getTypeConfiguration().getJavaTypeRegistry();
 
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "SYS.XMLTYPE", this ) );
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
@@ -859,7 +857,6 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
 		// We need the DDL type during runtime to produce the proper encoding in certain functions
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( BIT, "number(1,0)", this ) );
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( oracle.jdbc.OracleTypes.INTERVALDS, "interval day to second", this ) );
-		javaTypeRegistry.addDescriptor( OracleDurationJavaType.INSTANCE );
 
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
index 3530b10c801d..dd85678f4261 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -14,8 +14,8 @@
 import org.hibernate.type.descriptor.java.JavaType;
 import org.hibernate.type.descriptor.jdbc.BasicBinder;
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
+import org.hibernate.type.descriptor.jdbc.DurationJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
-import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
 
 import java.sql.CallableStatement;
@@ -24,7 +24,7 @@
 import java.sql.SQLException;
 import java.time.Duration;
 
-public class OracleDurationJdbcType implements JdbcType {
+public class OracleDurationJdbcType extends DurationJdbcType {
 
 	public static final OracleDurationJdbcType INSTANCE = new OracleDurationJdbcType();
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index 77eded5f0959..4e7acca28883 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -206,10 +206,16 @@ public String aggregateComponentCustomReadExpression(
 											"json_value(" + parentPartExpression + columnExpression + "')"
 									);
 								}
+							case UUID:
+								if (this.dateTypesStoreAsString) {
+									return template.replace(
+											placeholder,
+											"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
+									);
+								}
 							case BINARY:
 							case VARBINARY:
 							case LONG32VARBINARY:
-							case UUID:
 								// We encode binary data as hex, so we have to decode here
 								if ( determineLength( column ) * 2 < 4000L ) {
 									return template.replace(

From 3dfbbcce392db3335f9739c75aff62c517886925 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 15 Jan 2025 09:50:10 +0100
Subject: [PATCH 34/81] HHH-17404 : remove unwanted committed settings

---
 hibernate-testing/hibernate-testing.gradle | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/hibernate-testing/hibernate-testing.gradle b/hibernate-testing/hibernate-testing.gradle
index 0c13cdefd35c..7a4322acf911 100644
--- a/hibernate-testing/hibernate-testing.gradle
+++ b/hibernate-testing/hibernate-testing.gradle
@@ -48,11 +48,6 @@ dependencies {
     annotationProcessor project( ':hibernate-processor' )
 
 
-
-    runtimeOnly ('com.oracle.database.jdbc:ojdbc-provider-jackson-oson:1.0.2') {
-        exclude group: 'com.oracle.database.jdbc', module: 'ojdbc8'
-    }
-
 }
 
 

From 7ee81baee2d24ed5eaeadb0fef4fa40e1e2c73e1 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 15 Jan 2025 10:41:29 +0100
Subject: [PATCH 35/81] HHH-17404 : remove unused methods

---
 .../jackson/JacksonOsonFormatMapper.java      | 146 ------------------
 1 file changed, 146 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 2ffdf09982a9..fdd2eb7b9216 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -14,11 +14,6 @@
 import oracle.sql.json.OracleJsonParser;
 import org.hibernate.dialect.JsonHelper;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
-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.BasicType;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.BasicPluralJavaType;
 import org.hibernate.type.descriptor.java.JavaType;
@@ -209,147 +204,6 @@ public <X>byte[] arrayToOson(X value,
 		return out.toByteArray();
 	}
 
-	public void _arrayToOson(OracleJsonGenerator osonGen,
-							EmbeddableMappingType elementMappingType,
-							Object[] values,
-							WrapperOptions options) {
-
-		osonGen.writeStartArray();
-
-		if ( values.length == 0 ) {
-			osonGen.writeEnd();
-			return;
-		}
-
-		for ( Object value : values ) {
-			toOson( elementMappingType, value, options, osonGen);
-		}
-		osonGen.writeEnd();
-	}
-
-	private <X>void toOson(MappingType mappedType,
-						Object value, WrapperOptions options, OracleJsonGenerator osonGen) {
-		if (value == null) {
-			osonGen.writeNull();
-		}
-		else if ( mappedType instanceof EmbeddableMappingType ) {
-			toOson( (X) value, osonGen, options,(EmbeddableMappingType) mappedType );
-		}
-		else if ( mappedType instanceof BasicType<?> ) {
-			//noinspection unchecked
-			final BasicType<Object> basicType = (BasicType<Object>) mappedType;
-			convertedBasicValueToOson(basicType.convertToRelationalValue( value ),
-					options, osonGen, basicType);
-		}
-		else {
-			throw new UnsupportedOperationException( "Support for mapping type not yet implemented: " + mappedType.getClass().getName() );
-		}
-	}
-
-	private void convertedBasicValueToOson(Object value,
-										WrapperOptions options,
-										OracleJsonGenerator osonGen,
-										BasicType<Object> basicType) {
-		serializeValue(
-				value,
-				(JavaType<Object>) basicType.getJdbcJavaType(),
-				basicType.getJdbcType(),
-				options,
-				osonGen
-		);
-	}
-
-	private void serializeValue(Object value, JavaType<Object> jdbcJavaType, JdbcType jdbcType, WrapperOptions options, OracleJsonGenerator osonGen) {
-		//TODO: remove me.
-	}
-
-	public void __arrayToOson(OracleJsonGenerator osonGen,
-							JavaType<?> elementJavaType,
-							JdbcType elementJdbcType,
-							Object[] values,
-							WrapperOptions options) {
-		if ( values.length == 0 ) {
-			osonGen.writeStartArray();
-			osonGen.writeEnd();
-		}
-
-		osonGen.writeStartArray();
-		for ( Object value : values ) {
-			//noinspection unchecked
-			convertedValueToOson((JavaType<Object>) elementJavaType, elementJdbcType, value, options, osonGen);
-		}
-		osonGen.writeEnd();
-	}
-
-	private void convertedValueToOson(JavaType<Object> javaType,
-									JdbcType jdbcType,
-									Object value,
-									WrapperOptions options,
-									OracleJsonGenerator osonGen) {
-		if ( value == null ) {
-			osonGen.writeNull();
-		}
-		else if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
-			toOson(value,  osonGen, options, aggregateJdbcType.getEmbeddableMappingType());
-		}
-		else {
-			serializeValue( value, javaType, jdbcType, options, osonGen );
-		}
-	}
-
-	private <X> void toOson(X value, OracleJsonGenerator generator, WrapperOptions options, EmbeddableMappingType embeddableMappingType) {
-		generator.writeStartObject();
-		toOsonUtil( value, generator, options,embeddableMappingType );
-		generator.writeEnd();
-	}
-
-	private <X> void toOsonUtil(X value,
-								OracleJsonGenerator generator,
-								WrapperOptions options,
-								EmbeddableMappingType embeddableMappingType) {
-
-		final Object[] values = embeddableMappingType.getValues( value );
-		for ( int i = 0; i < values.length; i++ ) {
-			final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
-			if ( attributeMapping instanceof SelectableMapping ) {
-				final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
-
-				generator.writeKey( name );
-				toOson( attributeMapping.getMappedType(), values[i], options,generator );
-
-			}
-			else if (attributeMapping instanceof EmbeddedAttributeMapping) {
-				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
-				final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
-				if ( values[i] == null ) {
-					// Skipping the update of the separator is on purpose
-					continue;
-				}
-				if (aggregateMapping == null) {
-					// flattened case
-					toOsonUtil( (X) values[i],
-							generator,
-							options,
-							mappingType );
-				}
-				else {
-					// non flattened case
-					final String name = aggregateMapping.getSelectableName();
-					generator.writeKey( name );
-					generator.writeStartObject();
-					toOsonUtil( (X) values[i],
-							generator,
-							options,
-							mappingType);
-					generator.writeEnd();
-
-				}
-
-			}
-		}
-	}
-
-
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {

From 3dc18c0b8bd1d55fad27d14baf623f2db8eca501 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Wed, 15 Jan 2025 17:35:22 +0530
Subject: [PATCH 36/81] HHH-17404 : Rollback Test Entity changes and
 JsonMapping tests fix.

---
 .../dialect/OracleOsonJacksonJdbcType.java    |  4 +-
 .../embeddable/EmbeddableAggregate.java       |  8 +--
 .../embeddable/NestedJsonEmbeddableTest.java  | 65 -------------------
 3 files changed, 7 insertions(+), 70 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 870efd4f0aef..64d35831526b 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -67,7 +67,9 @@ public AggregateJdbcType resolveAggregateJdbcType(
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
-
+		if(javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class) {
+			return super.getBinder( javaType );
+		}
 
 		return new BasicBinder<>( javaType, this ) {
 
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
index 01d63dcb166a..069ed1ac1559 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
@@ -60,7 +60,7 @@ public class EmbeddableAggregate {
 	private byte[] theBinary;
 	private Date theDate;
 	private Date theTime;
-	private Timestamp theTimestamp;
+	private Date theTimestamp;
 	private Instant theInstant;
 	private UUID theUuid;
 	private EntityOfBasics.Gender gender;
@@ -183,12 +183,12 @@ public void setTheTime(Date theTime) {
 		this.theTime = theTime;
 	}
 
-	//@Temporal( TemporalType.TIMESTAMP )
-	public Timestamp getTheTimestamp() {
+	@Temporal( TemporalType.TIMESTAMP )
+	public Date getTheTimestamp() {
 		return theTimestamp;
 	}
 
-	public void setTheTimestamp(Timestamp theTimestamp) {
+	public void setTheTimestamp(Date theTimestamp) {
 		this.theTimestamp = theTimestamp;
 	}
 
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
index 054c2a860eda..2f5d880bcad5 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedJsonEmbeddableTest.java
@@ -88,7 +88,6 @@ public void testUpdate() {
 
 	@Test
 	public void testFetch() {
-
 		sessionFactoryScope().inSession(
 				entityManager -> {
 					List<JsonHolder> jsonHolders = entityManager.createQuery( "from JsonHolder b where b.id = 1", JsonHolder.class ).getResultList();
@@ -454,30 +453,6 @@ public static class TheJson {
 		public TheJson() {
 		}
 
-		public String getStringField() {
-			return stringField;
-		}
-
-		public void setStringField(String stringField) {
-			this.stringField = stringField;
-		}
-
-		public SimpleEmbeddable getSimpleEmbeddable() {
-			return simpleEmbeddable;
-		}
-
-		public void setSimpleEmbeddable(SimpleEmbeddable simpleEmbeddable) {
-			this.simpleEmbeddable = simpleEmbeddable;
-		}
-
-		public EmbeddableAggregate getNested() {
-			return nested;
-		}
-
-		public void setNested(EmbeddableAggregate nested) {
-			this.nested = nested;
-		}
-
 		public TheJson(String stringField, Integer integerField, String leaf, EmbeddableAggregate nested) {
 			this.stringField = stringField;
 			this.simpleEmbeddable = new SimpleEmbeddable( integerField, leaf );
@@ -497,22 +472,6 @@ public static class SimpleEmbeddable {
 		@JdbcTypeCode(SqlTypes.JSON)
 		private DoubleNested doubleNested;
 
-		public Integer getIntegerField() {
-			return integerField;
-		}
-
-		public void setIntegerField(Integer integerField) {
-			this.integerField = integerField;
-		}
-
-		public DoubleNested getDoubleNested() {
-			return doubleNested;
-		}
-
-		public void setDoubleNested(DoubleNested doubleNested) {
-			this.doubleNested = doubleNested;
-		}
-
 		public SimpleEmbeddable() {
 		}
 
@@ -558,14 +517,6 @@ public DoubleNested(String leaf) {
 			this.theNested = new Nested( leaf );
 		}
 
-		public Nested getTheNested() {
-			return theNested;
-		}
-
-		public void setTheNested(Nested theNested) {
-			this.theNested = theNested;
-		}
-
 		@Override
 		public boolean equals(Object o) {
 			if ( this == o ) {
@@ -598,14 +549,6 @@ public Nested(String stringField) {
 			this.theLeaf = new Leaf( stringField );
 		}
 
-		public Leaf getTheLeaf() {
-			return theLeaf;
-		}
-
-		public void setTheLeaf(Leaf theLeaf) {
-			this.theLeaf = theLeaf;
-		}
-
 		@Override
 		public boolean equals(Object o) {
 			if ( this == o ) {
@@ -637,14 +580,6 @@ public Leaf(String stringField) {
 			this.stringField = stringField;
 		}
 
-		public String getStringField() {
-			return stringField;
-		}
-
-		public void setStringField(String stringField) {
-			this.stringField = stringField;
-		}
-
 		@Override
 		public boolean equals(Object o) {
 			if ( this == o ) {

From 51cbd5d013c98b99304a2b839a53c47a5936a007 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Thu, 16 Jan 2025 18:24:14 +0100
Subject: [PATCH 37/81] HHH-17404 : general cleanup before PR

---
 .../SessionFactoryOptionsBuilder.java         |  5 ---
 .../java/org/hibernate/dialect/Dialect.java   | 13 ------
 .../org/hibernate/dialect/OracleDialect.java  | 11 +----
 .../dialect/OracleDurationJavaType.java       | 10 +++++
 .../dialect/OracleDurationJdbcType.java       | 41 ++++++-------------
 .../OracleOsonJacksonArrayJdbcType.java       |  5 +++
 .../dialect/OracleOsonJacksonJdbcType.java    | 17 +++++---
 .../aggregate/OracleAggregateSupport.java     |  2 +
 .../type/descriptor/jdbc/JsonHelper.java      | 13 +++++-
 .../type/format/JsonDocumentWriter.java       | 41 ++++++++++---------
 .../format/ObjectArrayOsonDocumentWriter.java | 19 +++++++--
 .../type/format/StringJsonDocumentWriter.java | 24 ++++++++---
 .../jackson/JacksonOsonFormatMapper.java      |  6 ++-
 hibernate-testing/hibernate-testing.gradle    |  3 --
 14 files changed, 114 insertions(+), 96 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
index 50b4aefc85b1..75d07ae139f4 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
@@ -795,11 +795,6 @@ private static FormatMapper determineJsonFormatMapper(Object setting, StrategySe
 				setting,
 				(Callable<FormatMapper>) () -> {
 					FormatMapper jsonJacksonFormatMapper = null;
-					configurationService.getSetting(
-							SESSION_FACTORY_NAME_IS_JNDI,
-							BOOLEAN,
-							true
-					);
 					if (JacksonIntegration.isOracleOsonExtensionAvailable()) {
 						jsonJacksonFormatMapper = getOsonJacksonFormatMapperOrNull();
 					}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
index 3988cfa22505..f566d03caf27 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java
@@ -5473,19 +5473,6 @@ public Boolean supportsRefCursors() {
 		return null;
 	}
 
-
-	/**
-	 * Whether this Dialect supports jakarta Temporal annotation while
-	 * serialising or deserialising JSON
-	 *
-	 * @return {@code true} indicates it does; {@code false} indicates it does not
-	 *
-	 * @see org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData#supportsRefCursors
-	 */
-	public Boolean getSupportsJakartaTemporalAnnotationInEmbeddable() {
-		return Boolean.TRUE;
-	}
-
 	/**
 	 * Pluggable strategy for determining the {@link Size} to use for
 	 * columns of a given SQL type.
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 3a2b37689e74..5dffa323d45f 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -1095,19 +1095,10 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 			jdbcTypeRegistry.addDescriptor( OracleOrdinalEnumJdbcType.INSTANCE );
 		}
 	}
-	@Override
-	public Boolean getSupportsJakartaTemporalAnnotationInEmbeddable() {
-		return OracleOsonExtensionUsed == false;
-	}
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		if (OracleOsonExtensionUsed) {
-			return OracleAggregateSupport.valueOf( this, false );
-		}
-		else {
-			return OracleAggregateSupport.valueOf( this, true );
-		}
+		return OracleAggregateSupport.valueOf( this ,false);
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
index 7bff7256d666..ea64ae7353c8 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
@@ -10,6 +10,16 @@
 
 import java.time.Duration;
 
+/**
+ * Oracle sub-implementation of {@link DurationJavaType}
+ * which is a descriptor for {@link Duration}
+ * This implementation brings the support of <code>oracle.sql.INTERVALDS</code>
+ * as source type.
+ * @see #wrap(Object, WrapperOptions)
+ *
+ * @author ejannett
+ * @author Bidyadhar Mohanty
+ */
 public class OracleDurationJavaType extends DurationJavaType {
 
 	public static final OracleDurationJavaType INSTANCE = new OracleDurationJavaType();
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
index dd85678f4261..69eb2686ffb8 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
@@ -5,9 +5,7 @@
 package org.hibernate.dialect;
 
 import oracle.jdbc.OracleTypes;
-import org.hibernate.engine.jdbc.Size;
 import org.hibernate.type.SqlTypes;
-import org.hibernate.type.Type;
 import org.hibernate.type.descriptor.ValueBinder;
 import org.hibernate.type.descriptor.ValueExtractor;
 import org.hibernate.type.descriptor.WrapperOptions;
@@ -16,7 +14,6 @@
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
 import org.hibernate.type.descriptor.jdbc.DurationJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
-import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
 
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
@@ -24,6 +21,14 @@
 import java.sql.SQLException;
 import java.time.Duration;
 
+/**
+ * Oracle sub-implementation of {@link DurationJdbcType}
+ * which is a descriptor for {@link java.time.Duration}.
+ * In Oracle databse Duration is stored as {@link OracleTypes.INTERVALDS}
+ *
+ * @author ejannett
+ * @author Bidyadhar Mohanty
+ */
 public class OracleDurationJdbcType extends DurationJdbcType {
 
 	public static final OracleDurationJdbcType INSTANCE = new OracleDurationJdbcType();
@@ -87,37 +92,17 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o
 		};
 	}
 
-	/**
-	 * The {@linkplain SqlTypes JDBC type code} used when interacting with JDBC APIs.
-	 * <p>
-	 * For example, it's used when calling {@link java.sql.PreparedStatement#setNull(int, int)}.
-	 *
-	 * @return a JDBC type code
-	 */
+	@Override
 	public int getJdbcTypeCode() {
 		return SqlTypes.DURATION;
 	}
-	/**
-	 * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type to
-	 * be used for schema generation.
-	 * <p>
-	 * This value is passed to {@link DdlTypeRegistry#getTypeName(int, Size, Type)}
-	 * to obtain the SQL column type.
-	 *
-	 * @return a JDBC type code
-	 * @since 6.2
-	 */
+
+	@Override
 	public int getDdlTypeCode() {
 		return OracleTypes.INTERVALDS;
 	}
-	/**
-	 * A {@linkplain SqlTypes JDBC type code} that identifies the SQL column type.
-	 * <p>
-	 * This value might be different from {@link #getDdlTypeCode()} if the actual type
-	 * e.g. JSON is emulated through a type like CLOB.
-	 *
-	 * @return a JDBC type code
-	 */
+
+	@Override
 	public int getDefaultSqlTypeCode() {
 		return OracleTypes.INTERVALDS;
 	}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index 3af61871895a..9174046e0c40 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -25,6 +25,11 @@
 import java.sql.SQLException;
 
 /**
+ *
+ * Type mapping of (JSON) array of JSON SQL data type for Oracle database.
+ * This implementation is used when Jackson mapper is used and that the JDBC OSON extension
+ * is available.
+ *
  * @author Emmanuel Jannetti
  * @author Bidyadhar Mohanty
  */
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 64d35831526b..325b310eb9ea 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -28,6 +28,11 @@
 import java.sql.SQLException;
 
 /**
+ *
+ * Type mapping JSON SQL data type for Oracle database.
+ * This implementation is used when Jackson mapper is used and that the JDBC OSON extension
+ * is available.
+ *
  * @author Emmanuel Jannetti
  */
 public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
@@ -83,9 +88,9 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 				
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
 				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
-				JsonGenerator osonGen = osonFactory.createGenerator( out );
-				mapper.writeToTarget( value, javaType, osonGen, options );
-				osonGen.close(); // until now
+				try (JsonGenerator osonGen = osonFactory.createGenerator( out )) {
+					mapper.writeToTarget( value, javaType, osonGen, options );
+				}
 				return out.toByteArray();
 			}
 
@@ -143,9 +148,9 @@ private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 				}
 
 				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
-				JsonParser osonParser = osonFactory.createParser(  osonBytes );
-
-				return mapper.readFromSource( type, osonParser, options );
+				try (JsonParser osonParser = osonFactory.createParser(  osonBytes )) {
+					return mapper.readFromSource( type, osonParser, options );
+				}
 			}
 
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index 4e7acca28883..82e89624a3a9 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -75,6 +75,8 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
 	OracleAggregateSupport(boolean checkConstraintSupport, JsonSupport jsonSupport, boolean dateTypesStoreAsString) {
 		this.checkConstraintSupport = checkConstraintSupport;
 		this.jsonSupport = jsonSupport;
+		// this flag tell us if data is serialized/de-serialized as String. As opposed to using OSON
+		// In other words, this flag tells us if the Oracle OSON JDBC extension is used or not.
 		this.dateTypesStoreAsString = dateTypesStoreAsString;
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 254b5fb04e91..2335cd105dff 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -105,7 +105,7 @@ public static void serializeArray(JavaType<?> elementJavaType, JdbcType elementJ
 	}
 
 	/**
-	 * Checks that a JDBCType is assignable to an array
+	 * 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.
 	 */
@@ -115,7 +115,7 @@ private static boolean isArrayType(JdbcType type) {
 	}
 
 	/**
-	 * Serialized an Object value to a JSON document writer.
+	 * 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.
@@ -130,6 +130,15 @@ public static void serialize(EmbeddableMappingType embeddableMappingType,
 		writer.endObject();
 	}
 
+	/**
+	 * 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
+	 */
 	private static void serializeMapping(EmbeddableMappingType embeddableMappingType,
 								Object domainValue, WrapperOptions options, JsonDocumentWriter writer) throws IOException {
 		final Object[] values = embeddableMappingType.getValues( domainValue );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
index 6a9923b1a9ed..2a0f8df1a687 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
@@ -13,64 +13,67 @@
 
 /**
  * JSON document producer.
- * Used to parse JSON documents. Implementors of this will define
- * proper callback implementations.
- *
+ * Implementation of this inteface will used to build a JSON document.
+ * Implementation example is {@link StringJsonDocumentWriter }
  * @author Emmanuel Jannetti
  */
 
 public interface JsonDocumentWriter {
 	/**
-	 * Callback to be called when the start of an JSON object is encountered.
+	 * Starts a new JSON Objects.
 	 */
-	void startObject() throws IOException;
+	void startObject();
 
 	/**
-	 * Callback to be called when the end of an JSON object is encountered.
+	 * Ends a new JSON Objects
 	 */
-	void endObject() throws IOException;
+	void endObject();
 
 	/**
-	 * Callback to be called when the start of an array is encountered.
+	 * Starts a new JSON array.
+	 * @throws IOException an I/O error roccured while starting the object.
 	 */
 	void startArray();
 
 	/**
-	 * Callback to be called when the end of an array is encountered.
+	 * Ends a new JSON array.
+	 * @throws IOException an I/O error roccured while starting the object.
 	 */
 	void endArray();
 
 	/**
-	 * Callback to be called when the key of JSON attribute is encountered.
-	 * @param key the attribute name
+	 * Adds a new JSON element name.
+	 * @param key the element name.
+	 * @throws IOException an I/O error roccured while starting the object.
 	 */
 	void objectKey(String key);
 
 	/**
-	 * Callback to be called when null value is encountered.
+	 * Adds a new JSON element null value.
+	 * @throws IOException an I/O error roccured while starting the object.
 	 */
 	void nullValue();
 
 	/**
-	 * Callback to be called when boolean value is encountered.
-	 * @param value the boolean value
+	 * Adds a new JSON element boolean value.
+	 * @param value the element boolean name.
 	 */
 	void booleanValue(boolean value);
 
 	/**
-	 * Callback to be called when string value is encountered.
-	 * @param value the String value
+	 * Adds a new JSON element string value.
+	 * @param value the element string name.
 	 */
 	void stringValue(String value);
 
 	/**
-	 * Callback to be called when Number value is encountered.
-	 * @param value the String value.
+	 * Adds a new JSON element Number value.
+	 * @param value the element Number name.
 	 */
 	void numberValue(Number value);
 
 	/**
-	 * Serialize a JSON value to the document
+	 * Adds a JSON value to the document
 	 * @param value the value to be serialized
 	 * @param javaType the Java type of the value
 	 * @param jdbcType the JDBC type for the value to be serialized
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
index 3a5300e11ad3..dfcf537bb191 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
@@ -17,7 +17,6 @@
 import org.hibernate.type.descriptor.java.UUIDJavaType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 
-import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.sql.Time;
@@ -42,19 +41,23 @@ public class ObjectArrayOsonDocumentWriter implements JsonDocumentWriter {
 
 	private final OracleJsonGenerator generator;
 
+	/**
+	 * Creates a new OSON document writer
+	 * @param generator the JSON generator.
+	 */
 	public ObjectArrayOsonDocumentWriter(OracleJsonGenerator generator) {
 		this.generator = generator;
 	}
 
 
 	@Override
-	public void startObject() throws IOException {
+	public void startObject() {
 		this.generator.writeStartObject();
 	}
 
 
 	@Override
-	public void endObject() throws IOException {
+	public void endObject() {
 		this.generator.writeEnd();
 	}
 
@@ -105,7 +108,15 @@ public void serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType
 		serializeValue(value, javaType, jdbcType, options);
 	}
 
-
+	/**
+	 * Serializes a value according to its mapping type.
+	 * This method serializes the value and writes it into the underlying generator
+	 *
+	 * @param value the value
+	 * @param javaType the Java type of the value
+	 * @param jdbcType the JDBC SQL type of the value
+	 * @param options the wapping options.
+	 */
 	private void serializeValue(Object value,
 								JavaType<Object> javaType,
 								JdbcType jdbcType,
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
index 6bac99e8bc1a..a0d867bf4763 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -14,13 +14,13 @@
 import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 
-import java.io.IOException;
 import java.time.OffsetDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Stack;
 
 /**
- * JsonDocument String writer implementation
+ * Implementation of <code>JsonDocumentWriter</code> for String based OSON document.
+ * This implementation will receive a {@link JsonHelper.JsonAppender } to a serialze JSON object to it
  * @author Emmanuel Jannetti
  */
 public class StringJsonDocumentWriter implements JsonDocumentWriter{
@@ -50,7 +50,10 @@ private enum PROCESSING_STATE {
 	}
 	private Stack<PROCESSING_STATE> processingStates = new Stack<>();
 
-
+	/**
+	 * Creates a new StringJsonDocumentWriter.
+	 * @param appender the appender to receive the serialze JSON object
+	 */
 	public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
 		this.processingStates.push( PROCESSING_STATE.NONE );
 		this.appender = appender;
@@ -60,7 +63,7 @@ public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
 	 * Callback to be called when the start of an JSON object is encountered.
 	 */
 	@Override
-	public void startObject() throws IOException {
+	public void startObject() {
 		// Note: startArray and startObject must not call moveProcessingStateMachine()
 		if (this.processingStates.peek() == PROCESSING_STATE.STARTING_ARRAY) {
 			// are we building an array of objects?
@@ -82,7 +85,7 @@ else if (this.processingStates.peek() == PROCESSING_STATE.ARRAY) {
 	 * Callback to be called when the end of an JSON object is encountered.
 	 */
 	@Override
-	public void endObject() throws IOException {
+	public void endObject() {
 		this.appender.append( OBJECT_END_MARKER );
 		this.processingStates.push( PROCESSING_STATE.ENDING_OBJECT);
 		moveProcessingStateMachine();
@@ -173,6 +176,7 @@ private void moveProcessingStateMachine() {
 				//   STARTING_ARRAY
 				// first pop ENDING_ARRAY
 				this.processingStates.pop();
+				// if we have ARRAY, so that's not an empty array. pop that state
 				if (this.processingStates.peek().equals( PROCESSING_STATE.ARRAY ))
 					this.processingStates.pop();
 				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_ARRAY );
@@ -183,6 +187,7 @@ private void moveProcessingStateMachine() {
 				//   STARTING_OBJECT
 				// first pop ENDING_OBJECT
 				this.processingStates.pop();
+				// if we have OBJECT, so that's not an empty object. pop that state
 				if (this.processingStates.peek().equals( PROCESSING_STATE.OBJECT ))
 					this.processingStates.pop();
 				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_OBJECT );
@@ -235,6 +240,15 @@ public void serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType
 		moveProcessingStateMachine();
 	}
 
+	/**
+	 * Converts a value to String according to its mapping type.
+	 * This method serializes the value and writes it into the underlying appender
+	 *
+	 * @param value the value
+	 * @param javaType the Java type of the value
+	 * @param jdbcType the JDBC SQL type of the value
+	 * @param options the wapping options.
+	 */
 	private  void convertedBasicValueToString(
 			Object value,
 			WrapperOptions options,
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index fdd2eb7b9216..f7c4b1900e74 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -30,6 +30,8 @@
 
 
 /**
+ * Implementation of FormatMapper for Orale OSON support
+ *
  * @author Emmanuel Jannetti
  * @author Bidyadhar Mohanty
  */
@@ -66,7 +68,9 @@ public JacksonOsonFormatMapper() {
 	}
 
 	/**
-	 * Process OSON parser tokens
+	 * Process OSON parser tokens.
+	 * This method consume one by one event coming from an OSON parser and use the given JsonDocumentHandler
+	 * to populate values into Object array
 	 * @param osonParser the OSON parser
 	 * @param currentEvent the current of the parser
 	 * @throws IOException error while reading from underlying parser
diff --git a/hibernate-testing/hibernate-testing.gradle b/hibernate-testing/hibernate-testing.gradle
index 7a4322acf911..f64f1e44418c 100644
--- a/hibernate-testing/hibernate-testing.gradle
+++ b/hibernate-testing/hibernate-testing.gradle
@@ -46,8 +46,5 @@ dependencies {
     implementation testLibs.junit5Launcher
 
     annotationProcessor project( ':hibernate-processor' )
-
-
 }
 
-

From 9a3b85c615541853f2f6141f82d2fe09fd0bb862 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Fri, 17 Jan 2025 13:16:13 +0530
Subject: [PATCH 38/81] HHH-17404- Rollback OracleUserDefinedTypeExporter
 changes

---
 .../hibernate/dialect/type/OracleUserDefinedTypeExporter.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java
index 4c37624d4344..4808017f80ce 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleUserDefinedTypeExporter.java
@@ -337,7 +337,7 @@ private String determineValueExpression(String expression, int elementSqlTypeCod
 			case DATE:
 				return "to_date(" + expression + ",'YYYY-MM-DD')";
 			case TIME:
-				return "to_timestamp(CONCAT('1970-01-01 ', \" + expression + \"),'YYYY-MM-DD hh24:mi:ss')";
+				return "to_timestamp(" + expression + ",'hh24:mi:ss')";
 			case TIMESTAMP:
 				return "to_timestamp(" + expression + ",'YYYY-MM-DD\"T\"hh24:mi:ss.FF9')";
 			case TIMESTAMP_WITH_TIMEZONE:

From 0548022ed6c09bd7720d6021952e0c8d6ffbff0c Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Fri, 17 Jan 2025 14:06:58 +0530
Subject: [PATCH 39/81] HHH-17404- Aggregate instance creation fix.

---
 .../src/main/java/org/hibernate/dialect/OracleDialect.java     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 5dffa323d45f..58e9df7ebcf3 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -1098,7 +1098,8 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this ,false);
+		return OracleAggregateSupport.valueOf( this ,
+				!JacksonIntegration.isOracleOsonExtensionAvailable());
 	}
 
 	@Override

From 5181de9dd7e813b18c7f3865034db3a09041967c Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Fri, 17 Jan 2025 09:38:31 +0100
Subject: [PATCH 40/81] HHH-17404 : fix aggregatesupport for "no-extension"
 case

---
 .../src/main/java/org/hibernate/dialect/OracleDialect.java     | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 58e9df7ebcf3..9e5d71c618ac 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -1098,8 +1098,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this ,
-				!JacksonIntegration.isOracleOsonExtensionAvailable());
+		return OracleAggregateSupport.valueOf( this ,OracleOsonExtensionUsed);
 	}
 
 	@Override

From eeec7c30609b8c072aa5eba23f99a7e76772415e Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Fri, 17 Jan 2025 10:38:27 +0100
Subject: [PATCH 41/81] HHH-17404 : fix condition on aggregate support load

---
 .../src/main/java/org/hibernate/dialect/OracleDialect.java      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 9e5d71c618ac..b20b7ed47533 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -1098,7 +1098,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this ,OracleOsonExtensionUsed);
+		return OracleAggregateSupport.valueOf( this ,!OracleOsonExtensionUsed);
 	}
 
 	@Override

From e3205caed62cacf2059f14fc2b78f933512e970a Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Sat, 18 Jan 2025 17:43:47 +0100
Subject: [PATCH 42/81] HHH-17404 : refactor OSON formatMapper to align on JSON
 one.

---
 .../dialect/OracleOsonJacksonJdbcType.java    |  23 +++-
 .../org/hibernate/dialect/OsonHelper.java     | 104 +++++++++++++++
 .../jackson/JacksonOsonFormatMapper.java      | 123 +-----------------
 3 files changed, 125 insertions(+), 125 deletions(-)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 325b310eb9ea..dd7364105a97 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -9,6 +9,9 @@
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
 import oracle.sql.json.OracleJsonDatum;
+import oracle.sql.json.OracleJsonFactory;
+import oracle.sql.json.OracleJsonGenerator;
+import oracle.sql.json.OracleJsonParser;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
 import org.hibernate.type.descriptor.ValueBinder;
@@ -19,9 +22,12 @@
 import org.hibernate.type.descriptor.jdbc.BasicBinder;
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
 import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.ObjectArrayOsonDocumentHandler;
+import org.hibernate.type.format.ObjectArrayOsonDocumentWriter;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -83,7 +89,12 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 
 				if (getEmbeddableMappingType()!= null) {
-					return ((JacksonOsonFormatMapper)mapper).fromObjectArray(value,javaType,options,getEmbeddableMappingType());
+					ByteArrayOutputStream out = new ByteArrayOutputStream();
+					OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
+					ObjectArrayOsonDocumentWriter writer = new ObjectArrayOsonDocumentWriter(generator);
+					JsonHelper.serialize( getEmbeddableMappingType(), value,options,writer);
+					generator.close();
+					return out.toByteArray();
 				}
 				
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -137,8 +148,14 @@ private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 					// an array of objects. We use JsonParser to fetch values
 					// and build the array.(as opposed to let Jackson do it as we do not
 					// have a proper object definition at that stage).
-					return ((JacksonOsonFormatMapper)mapper).toObjectArray(
-							getEmbeddableMappingType(), osonBytes, options );
+					OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( ByteBuffer.wrap( osonBytes ) );
+
+					ObjectArrayOsonDocumentHandler handler = new ObjectArrayOsonDocumentHandler( getEmbeddableMappingType(),
+							options);
+
+					OsonHelper.consumeOsonTokens(osonParser, osonParser.next(), handler);
+
+					return (X) handler.getObjectArray();
 				}
 
 				JavaType <X> type = getJavaType();
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java
new file mode 100644
index 000000000000..09c57aa9c2c0
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java
@@ -0,0 +1,104 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.dialect;
+
+import oracle.sql.json.OracleJsonParser;
+import org.hibernate.Internal;
+import org.hibernate.type.format.JsonDocumentHandler;
+import org.hibernate.type.format.ObjectArrayOsonDocumentHandler;
+
+import java.io.IOException;
+
+/**
+ * A Helper for handling OSON events
+ */
+@Internal
+public class OsonHelper {
+
+	/**
+	 * Process OSON parser tokens.
+	 * This method consumes one by one event coming from an OSON parser and uses the given JsonDocumentHandler
+	 * to populate values into Object array
+	 * @param osonParser the OSON parser
+	 * @param currentEvent the current of the parser
+	 * @throws IOException error while reading from underlying parser
+	 */
+	public static void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Event currentEvent, JsonDocumentHandler handler)
+			throws IOException {
+
+		OracleJsonParser.Event event = currentEvent;
+
+		while ( event != null ) {
+			switch ( event ) {
+				case OracleJsonParser.Event.KEY_NAME:
+					handler.onObjectKey( osonParser.getString() );
+					break;
+				case OracleJsonParser.Event.START_ARRAY:
+					handler.onStartArray();
+					break;
+				case OracleJsonParser.Event.END_ARRAY:
+					handler.onEndArray();
+					break;
+				case OracleJsonParser.Event.VALUE_DATE:
+				case OracleJsonParser.Event.VALUE_TIMESTAMP:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonDateValue(
+							osonParser.getLocalDateTime());
+					break;
+				case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getOffsetDateTime());
+					break;
+				case OracleJsonParser.Event.VALUE_INTERVALDS:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getDuration());
+					break;
+				case OracleJsonParser.Event.VALUE_INTERVALYM:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getPeriod());
+					break;
+				case OracleJsonParser.Event.VALUE_STRING:
+					handler.onStringValue( osonParser.getString() );
+					break;
+				case OracleJsonParser.Event.VALUE_TRUE:
+					handler.onBooleanValue( true );
+					break;
+				case OracleJsonParser.Event.VALUE_FALSE:
+					handler.onBooleanValue( false );
+					break;
+				case OracleJsonParser.Event.VALUE_NULL:
+					handler.onNullValue();
+					break;
+				case OracleJsonParser.Event.VALUE_DECIMAL:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getBigDecimal());
+					break;
+				case OracleJsonParser.Event.VALUE_DOUBLE:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getDouble());
+					break;
+				case OracleJsonParser.Event.VALUE_FLOAT:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
+							osonParser.getFloat());
+					break;
+				case OracleJsonParser.Event.VALUE_BINARY:
+					((ObjectArrayOsonDocumentHandler)handler).onOsonBinaryValue(
+							osonParser.getBytes());
+					break;
+				case OracleJsonParser.Event.START_OBJECT:
+					handler.onStartObject();
+					break;
+				case OracleJsonParser.Event.END_OBJECT:
+					handler.onEndObject();
+					break;
+				default:
+					throw new IOException( "Unknown OSON event " + event );
+
+			}
+			event = osonParser.hasNext() ? osonParser.next() : null;
+		}
+
+	}
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index f7c4b1900e74..34368a05b58d 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -7,11 +7,9 @@
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.Module;
-import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
-import oracle.sql.json.OracleJsonParser;
 import org.hibernate.dialect.JsonHelper;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.type.descriptor.WrapperOptions;
@@ -20,13 +18,10 @@
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
-import org.hibernate.type.format.JsonDocumentHandler;
-import org.hibernate.type.format.ObjectArrayOsonDocumentHandler;
 import org.hibernate.type.format.ObjectArrayOsonDocumentWriter;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.nio.ByteBuffer;
 
 
 /**
@@ -67,121 +62,6 @@ public JacksonOsonFormatMapper() {
 		objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
 	}
 
-	/**
-	 * Process OSON parser tokens.
-	 * This method consume one by one event coming from an OSON parser and use the given JsonDocumentHandler
-	 * to populate values into Object array
-	 * @param osonParser the OSON parser
-	 * @param currentEvent the current of the parser
-	 * @throws IOException error while reading from underlying parser
-	 */
-	private void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Event currentEvent, JsonDocumentHandler handler)
-			throws IOException {
-
-		OracleJsonParser.Event event = currentEvent;
-
-		while ( event != null ) {
-			switch ( event ) {
-				case OracleJsonParser.Event.KEY_NAME:
-					handler.onObjectKey( osonParser.getString() );
-					break;
-				case OracleJsonParser.Event.START_ARRAY:
-					handler.onStartArray();
-					break;
-				case OracleJsonParser.Event.END_ARRAY:
-					handler.onEndArray();
-					break;
-				case OracleJsonParser.Event.VALUE_DATE:
-				case OracleJsonParser.Event.VALUE_TIMESTAMP:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonDateValue(
-							osonParser.getLocalDateTime());
-					break;
-				case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getOffsetDateTime());
-					break;
-				case OracleJsonParser.Event.VALUE_INTERVALDS:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getDuration());
-					break;
-				case OracleJsonParser.Event.VALUE_INTERVALYM:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getPeriod());
-					break;
-				case OracleJsonParser.Event.VALUE_STRING:
-					handler.onStringValue( osonParser.getString() );
-					break;
-				case OracleJsonParser.Event.VALUE_TRUE:
-					handler.onBooleanValue( true );
-					break;
-				case OracleJsonParser.Event.VALUE_FALSE:
-					handler.onBooleanValue( false );
-					break;
-				case OracleJsonParser.Event.VALUE_NULL:
-					handler.onNullValue();
-					break;
-				case OracleJsonParser.Event.VALUE_DECIMAL:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getBigDecimal());
-					break;
-				case OracleJsonParser.Event.VALUE_DOUBLE:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getDouble());
-					break;
-				case OracleJsonParser.Event.VALUE_FLOAT:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getFloat());
-					break;
-				case OracleJsonParser.Event.VALUE_BINARY:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonBinaryValue(
-							osonParser.getBytes());
-					break;
-				case OracleJsonParser.Event.START_OBJECT:
-					handler.onStartObject();
-					break;
-				case OracleJsonParser.Event.END_OBJECT:
-					handler.onEndObject();
-					break;
-				default:
-					throw new IOException( "Unknown OSON event " + event );
-
-			}
-			event = osonParser.hasNext() ? osonParser.next() : null;
-		}
-
-	}
-
-
-	/**
-	 * Consumes OSON bytes and populate an Object array as described in the embeddable mapping definitions.
-	 * @param embeddableMappingType the embeddable mapping definitions
-	 * @param source the OSON bytes as <code>byte[]</code>
-	 * @param options the wrapping options
-	 * @return the Object array
-	 * @param <T> return type i.e., object array
-	 * @throws IOException OSON parsing has failed
-	 */
-	public <T> T toObjectArray(EmbeddableMappingType embeddableMappingType, Object source, WrapperOptions options) throws IOException {
-
-		OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( ByteBuffer.wrap( (byte[])source ) );
-
-		ObjectArrayOsonDocumentHandler handler = new ObjectArrayOsonDocumentHandler( embeddableMappingType,
-				options);
-
-		consumeOsonTokens(osonParser, osonParser.next(), handler);
-
-		return (T)handler.getObjectArray();
-	}
-
-	public <X>byte[] fromObjectArray(X value, JavaType<X> javaType, WrapperOptions options,EmbeddableMappingType embeddableMappingType)
-			throws IOException {
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
-		ObjectArrayOsonDocumentWriter writer = new ObjectArrayOsonDocumentWriter(generator);
-		JsonHelper.serialize( embeddableMappingType, value,options,writer);
-		generator.close();
-		return out.toByteArray();
-	}
 
 	public <X>byte[] arrayToOson(X value,
 								JavaType<X> javaType,
@@ -211,8 +91,7 @@ public <X>byte[] arrayToOson(X value,
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
-		ObjectWriter writer = objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) );
-		writer.writeValue( (JsonGenerator) target, value);
+		objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) ).writeValue( (JsonGenerator) target, value);
 
 	}
 

From 29ad797ffbaf5bb4f4818ce1566c5edf8c61d1b2 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Tue, 21 Jan 2025 11:44:13 +0100
Subject: [PATCH 43/81] HHH-17404 : applies some review comments

---
 .../OracleOsonJacksonArrayJdbcType.java       | 45 ++++++++++++++++---
 .../dialect/OracleOsonJacksonJdbcType.java    | 13 +++---
 .../type/descriptor/jdbc/JsonHelper.java      | 18 ++++----
 .../ObjectArrayOsonDocumentHandler.java       | 37 ++++++++-------
 ...entWriter.java => OsonDocumentWriter.java} |  4 +-
 .../type/format/StringJsonDocumentWriter.java | 19 ++++----
 .../jackson/JacksonOsonFormatMapper.java      | 36 ---------------
 7 files changed, 86 insertions(+), 86 deletions(-)
 rename hibernate-core/src/main/java/org/hibernate/type/format/{ObjectArrayOsonDocumentWriter.java => OsonDocumentWriter.java} (97%)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index 9174046e0c40..5026ef539e57 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -6,19 +6,27 @@
 
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonParser;
+import oracle.jdbc.OracleType;
 import oracle.sql.json.OracleJsonDatum;
+import oracle.sql.json.OracleJsonFactory;
+import oracle.sql.json.OracleJsonGenerator;
+
+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.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.JsonJdbcType;
 import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.OsonDocumentWriter;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
+import java.io.ByteArrayOutputStream;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -67,15 +75,38 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 		return new BasicBinder<>( javaType, this ) {
 
-			private <X> InputStream toOsonStream(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
-				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
-				return  new ByteArrayInputStream(((JacksonOsonFormatMapper)mapper).arrayToOson(value, javaType,getElementJdbcType(),options));
+			private <X> byte[] toOsonStream(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
+				final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
+
+				ByteArrayOutputStream out = new ByteArrayOutputStream();
+				try (OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out )) {
+					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 );
+						final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
+						JsonHelper.serializeArray(
+								elementJavaType,
+								getElementJdbcType(),
+								domainObjects,
+								options,
+								writer
+						);
+					}
+					generator.close();
+					return out.toByteArray();
+				}
+
 			}
 			@Override
 			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setBinaryStream( index, toOsonStream( value, getJavaType(), options ) );
+					st.setObject( index, toOsonStream( value, getJavaType(), options ), OracleType.JSON );
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -86,7 +117,7 @@ protected void doBind(PreparedStatement st, X value, int index, WrapperOptions o
 			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setBinaryStream( name, toOsonStream( value, getJavaType(), options ) );
+					st.setObject( name, toOsonStream( value, getJavaType(), options ) , OracleType.JSON);
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index dd7364105a97..6ab8a284a4dc 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -23,7 +23,7 @@
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.ObjectArrayOsonDocumentHandler;
-import org.hibernate.type.format.ObjectArrayOsonDocumentWriter;
+import org.hibernate.type.format.OsonDocumentWriter;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayOutputStream;
@@ -88,12 +88,13 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 
-				if (getEmbeddableMappingType()!= null) {
+				if (getEmbeddableMappingType() != null) {
 					ByteArrayOutputStream out = new ByteArrayOutputStream();
-					OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
-					ObjectArrayOsonDocumentWriter writer = new ObjectArrayOsonDocumentWriter(generator);
-					JsonHelper.serialize( getEmbeddableMappingType(), value,options,writer);
-					generator.close();
+					// OracleJsonFactory is used and not OracleOsonFactory as Jackson is not involved here
+					try (OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out )) {
+						OsonDocumentWriter writer = new OsonDocumentWriter( generator);
+						JsonHelper.serialize( getEmbeddableMappingType(), value,options,writer);
+					}
 					return out.toByteArray();
 				}
 				
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 2335cd105dff..3c57a42129ef 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -241,15 +241,7 @@ else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
 				}
 				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
 				final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
-				if ( mappingType.shouldSelectAggregateMapping()) {
-					final String name = aggregateMapping.getSelectableName();
-					appender.append( separator );
-					appender.append( '"' );
-					appender.append( name );
-					appender.append( "\":" );
-					toString( mappingType, values[i], options, appender );
-				}
-				else {
+				if ( aggregateMapping == null) {
 					toString(
 							mappingType,
 							options,
@@ -258,6 +250,14 @@ else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
 							separator
 					);
 				}
+				else {
+					final String name = aggregateMapping.getSelectableName();
+					appender.append( separator );
+					appender.append( '"' );
+					appender.append( name );
+					appender.append( "\":" );
+					toString( mappingType, values[i], options, appender );
+				}
 			}
 			else {
 				throw new UnsupportedOperationException( "Support for attribute mapping type not yet implemented: " + attributeMapping.getClass().getName() );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
index 0833fd8707ed..608467bcc43f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
@@ -4,6 +4,7 @@
  */
 package org.hibernate.type.format;
 
+import org.hibernate.internal.util.collections.StandardStack;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.mapping.SelectableMapping;
 import org.hibernate.type.BasicPluralType;
@@ -18,7 +19,7 @@
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Stack;
+
 
 /**
  * Implementation of <code>JsonDocumentHandler</code> for OSON document.
@@ -42,11 +43,11 @@ public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
 	// Each mapping definition may contain sub mappings (sub embeddable mapping)
 	// This stack is used to keep a pointer on the current mapping to be used to assign correct types.
 	// see onStartObject()/onEndObject() methods
-	Stack<EmbeddableMappingType> embeddableMappingTypes = new Stack<>();
+	StandardStack<EmbeddableMappingType> embeddableMappingTypes = new StandardStack<>();
 	// As for mapping definitions, when "sub embeddable" is encountered, the array
 	// that needs to be filled with Objects is the one we allocate in the final result array slot.
 	// We use a stack to keep track of array ref
-	Stack<Object[]> objectArrays = new Stack<>();
+	StandardStack<Object[]> objectArrays = new StandardStack<>();
 
 
 	WrapperOptions wrapperOptions;
@@ -75,18 +76,20 @@ public void onStartObject() {
 			// We are dealing with a sub-object, allocate space for it then,
 			// otherwise, we have nothing to do.
 			// Push the new (sub)mapping definition.
-			this.currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
+			assert embeddableMappingTypes.getCurrent() != null;
+			this.currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
 			assert currentSelectableIndexInResultArray != -1: "Cannot get index of " + currentKeyName;
 
-			final SelectableMapping selectable = embeddableMappingTypes.peek().getJdbcValueSelectable(
+			final SelectableMapping selectable = embeddableMappingTypes.getCurrent().getJdbcValueSelectable(
 					currentSelectableIndexInResultArray );
 			final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
 					.getJdbcType();
 			final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-			this.objectArrays.peek()[currentSelectableIndexInResultArray] =
+			assert this.objectArrays.getCurrent() != null;
+			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
 					new Object[subMappingType.getJdbcValueCount()];
 			this.embeddableMappingTypes.push( subMappingType );
-			this.objectArrays.push( (Object[]) this.objectArrays.peek()[currentSelectableIndexInResultArray] );
+			this.objectArrays.push( (Object[]) this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] );
 		}
 	}
 
@@ -113,7 +116,7 @@ public void onStartArray() {
 	public void onEndArray() {
 		assert (subArrayObjectList != null && subArrayObjectTypes != null) : "onEndArray called before onStartArray";
 		// flush array values
-		this.objectArrays.peek()[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, wrapperOptions );
+		this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, wrapperOptions );
 		// reset until we encounter next array element
 		subArrayObjectList = null;
 		subArrayObjectTypes = null;
@@ -123,17 +126,17 @@ public void onEndArray() {
 	public void onObjectKey(String key) {
 		this.currentKeyName = key;
 
-		currentSelectableIndexInResultArray = embeddableMappingTypes.peek().getSelectableIndex( currentKeyName );
+		currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
 		if ( currentSelectableIndexInResultArray >= 0 ) {
 			// we may not have a selectable mapping for that key
-			currentSelectableMapping = embeddableMappingTypes.peek().getJdbcValueSelectable( currentSelectableIndexInResultArray );
+			currentSelectableMapping = embeddableMappingTypes.getCurrent().getJdbcValueSelectable( currentSelectableIndexInResultArray );
 		}
 		else {
 			throw new IllegalArgumentException(
 					String.format(
 							"Could not find selectable [%s] in embeddable type [%s] for JSON processing.",
 							currentKeyName,
-							embeddableMappingTypes.peek().getMappedJavaType().getJavaTypeClass().getName()
+							embeddableMappingTypes.getCurrent().getMappedJavaType().getJavaTypeClass().getName()
 					)
 			);
 		}
@@ -146,7 +149,7 @@ public void onNullValue() {
 			subArrayObjectList.add( null );
 		}
 		else {
-			this.objectArrays.peek()[currentSelectableIndexInResultArray] = null;
+			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = null;
 		}
 	}
 
@@ -157,7 +160,7 @@ public void onBooleanValue(boolean value) {
 			subArrayObjectList.add( value?Boolean.TRUE:Boolean.FALSE);
 		}
 		else {
-			this.objectArrays.peek()[currentSelectableIndexInResultArray] = value?Boolean.TRUE:Boolean.FALSE;
+			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = value?Boolean.TRUE:Boolean.FALSE;
 		}
 	}
 
@@ -169,7 +172,7 @@ public void onStringValue(String value) {
 					subArrayObjectTypes.getElementType().getJdbcJavaType().fromEncodedString( value ,0,value.length()) );
 		}
 		else {
-			this.objectArrays.peek()[currentSelectableIndexInResultArray] =
+			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
 					currentSelectableMapping.getJdbcMapping().getJdbcJavaType().fromEncodedString( value,0,value.length());
 		}
 	}
@@ -190,7 +193,7 @@ public <T> void onOsonValue(T value) {
 			subArrayObjectList.add( value );
 		}
 		else {
-			this.objectArrays.peek()[currentSelectableIndexInResultArray] =
+			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
 					currentSelectableMapping.getJdbcMapping().convertToDomainValue(
 					currentSelectableMapping.getJdbcMapping().getJdbcJavaType()
 							.wrap( value, wrapperOptions ) );
@@ -223,7 +226,7 @@ public void onOsonBinaryValue(byte[] bytes) {
 			subArrayObjectList.add( theOneToBeUsed );
 		}
 		else {
-			this.objectArrays.peek()[currentSelectableIndexInResultArray] = theOneToBeUsed;
+			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = theOneToBeUsed;
 		}
 	}
 
@@ -270,7 +273,7 @@ else if ( java.util.Date.class.isAssignableFrom( underlyingType ) ) {
 			subArrayObjectList.add( theOneToBeUsed );
 		}
 		else {
-			this.objectArrays.peek()[currentSelectableIndexInResultArray] = theOneToBeUsed;
+			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = theOneToBeUsed;
 		}
 	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
similarity index 97%
rename from hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
rename to hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index dfcf537bb191..7a4588750664 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -36,7 +36,7 @@
  *
  * @author Emmanuel Jannetti
  */
-public class ObjectArrayOsonDocumentWriter implements JsonDocumentWriter {
+public class OsonDocumentWriter implements JsonDocumentWriter {
 
 
 	private final OracleJsonGenerator generator;
@@ -45,7 +45,7 @@ public class ObjectArrayOsonDocumentWriter implements JsonDocumentWriter {
 	 * Creates a new OSON document writer
 	 * @param generator the JSON generator.
 	 */
-	public ObjectArrayOsonDocumentWriter(OracleJsonGenerator generator) {
+	public OsonDocumentWriter(OracleJsonGenerator generator) {
 		this.generator = generator;
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
index a0d867bf4763..07c15071a438 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -5,6 +5,7 @@
 package org.hibernate.type.format;
 
 import org.hibernate.dialect.JsonHelper;
+import org.hibernate.internal.util.collections.StandardStack;
 import org.hibernate.type.SqlTypes;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.BooleanJavaType;
@@ -16,7 +17,7 @@
 
 import java.time.OffsetDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.Stack;
+
 
 /**
  * Implementation of <code>JsonDocumentWriter</code> for String based OSON document.
@@ -48,7 +49,7 @@ private enum PROCESSING_STATE {
 		ENDING_ARRAY,  // we are ending an array
 		ARRAY // we are piling array values
 	}
-	private Stack<PROCESSING_STATE> processingStates = new Stack<>();
+	private StandardStack<PROCESSING_STATE> processingStates = new StandardStack<>();
 
 	/**
 	 * Creates a new StringJsonDocumentWriter.
@@ -65,13 +66,13 @@ public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
 	@Override
 	public void startObject() {
 		// Note: startArray and startObject must not call moveProcessingStateMachine()
-		if (this.processingStates.peek() == PROCESSING_STATE.STARTING_ARRAY) {
+		if (this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_ARRAY) {
 			// are we building an array of objects?
 			// i.e, [{},...]
 			// move to PROCESSING_STATE.ARRAY first
 			this.processingStates.push( PROCESSING_STATE.ARRAY);
 		}
-		else if (this.processingStates.peek() == PROCESSING_STATE.ARRAY) {
+		else if (this.processingStates.getCurrent() == PROCESSING_STATE.ARRAY) {
 			// That means that we ae building an array of object ([{},...])
 			// JSON object hee are treat as array item.
 			// -> add the marker first
@@ -115,7 +116,7 @@ public void endArray() {
 
 	@Override
 	public void objectKey(String key) {
-		if (this.processingStates.peek().equals( PROCESSING_STATE.OBJECT )) {
+		if (this.processingStates.getCurrent().equals( PROCESSING_STATE.OBJECT )) {
 			// we have started an object, and we are adding an item key: we do add a separator.
 			this.appender.append( SEPARATOR_MARKER );
 		}
@@ -132,7 +133,7 @@ public void objectKey(String key) {
 	 * Separator is to separate array items or key/value pairs in an object.
 	 */
 	private void addItemsSeparator() {
-		if (this.processingStates.peek().equals( PROCESSING_STATE.ARRAY )) {
+		if (this.processingStates.getCurrent().equals( PROCESSING_STATE.ARRAY )) {
 			// We started to serialize an array and already added item to it:add a separator anytime.
 			this.appender.append( SEPARATOR_MARKER );
 		}
@@ -161,7 +162,7 @@ private void addItemsSeparator() {
 	 *
 	 */
 	private void moveProcessingStateMachine() {
-		switch (this.processingStates.peek()) {
+		switch (this.processingStates.getCurrent()) {
 			case STARTING_OBJECT:
 				//after starting an object, we start adding key/value pairs
 				this.processingStates.push( PROCESSING_STATE.OBJECT );
@@ -177,7 +178,7 @@ private void moveProcessingStateMachine() {
 				// first pop ENDING_ARRAY
 				this.processingStates.pop();
 				// if we have ARRAY, so that's not an empty array. pop that state
-				if (this.processingStates.peek().equals( PROCESSING_STATE.ARRAY ))
+				if (this.processingStates.getCurrent().equals( PROCESSING_STATE.ARRAY ))
 					this.processingStates.pop();
 				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_ARRAY );
 				break;
@@ -188,7 +189,7 @@ private void moveProcessingStateMachine() {
 				// first pop ENDING_OBJECT
 				this.processingStates.pop();
 				// if we have OBJECT, so that's not an empty object. pop that state
-				if (this.processingStates.peek().equals( PROCESSING_STATE.OBJECT ))
+				if (this.processingStates.getCurrent().equals( PROCESSING_STATE.OBJECT ))
 					this.processingStates.pop();
 				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_OBJECT );
 				break;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 34368a05b58d..883179173648 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -8,19 +8,9 @@
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.SerializationFeature;
-import oracle.sql.json.OracleJsonFactory;
-import oracle.sql.json.OracleJsonGenerator;
-import org.hibernate.dialect.JsonHelper;
-import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 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.jdbc.AggregateJdbcType;
-import org.hibernate.type.descriptor.jdbc.JdbcType;
-import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
-import org.hibernate.type.format.ObjectArrayOsonDocumentWriter;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 
@@ -62,32 +52,6 @@ public JacksonOsonFormatMapper() {
 		objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
 	}
 
-
-	public <X>byte[] arrayToOson(X value,
-								JavaType<X> javaType,
-								JdbcType elementJdbcType,
-								WrapperOptions options) {
-
-		final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
-
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out );
-		ObjectArrayOsonDocumentWriter writer = new ObjectArrayOsonDocumentWriter(generator);
-
-		if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) {
-			final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
-			JsonHelper.serializeArray( embeddableMappingType, domainObjects, options,  writer);
-		}
-		else {
-			assert !( elementJdbcType instanceof AggregateJdbcType);
-			final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
-			JsonHelper.serializeArray( elementJavaType, elementJdbcType, domainObjects, options, writer );
-		}
-
-		generator.close();
-		return out.toByteArray();
-	}
-
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {

From c1f149ad79f09d7f91fabc020f091fbe9138a745 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 29 Jan 2025 17:55:22 +0100
Subject: [PATCH 44/81] HHH-17404 : implement JSON document readers and writers
 for OSON extension and for ususla String-based JSON support

---
 .../internal/StrategySelectorBuilder.java     |    3 +-
 .../org/hibernate/dialect/OracleDialect.java  |    1 +
 .../OracleOsonJacksonArrayJdbcType.java       |    5 +-
 .../dialect/OracleOsonJacksonJdbcType.java    |   32 +-
 .../internal/util/CharSequenceHelper.java     |    4 +
 .../java/BooleanPrimitiveArrayJavaType.java   |    2 +-
 .../java/DoublePrimitiveArrayJavaType.java    |    2 +-
 .../java/FloatPrimitiveArrayJavaType.java     |    2 +-
 .../java/IntegerPrimitiveArrayJavaType.java   |    2 +-
 .../type/descriptor/java/JavaType.java        |    4 +
 .../java/LongPrimitiveArrayJavaType.java      |    2 +-
 .../java/ShortPrimitiveArrayJavaType.java     |    2 +-
 .../type/descriptor/jdbc/JsonHelper.java      | 1448 +++--------------
 .../type/descriptor/jdbc/JsonJdbcType.java    |   18 +-
 .../type/format/JsonDocumentItem.java         |   87 +
 .../type/format/JsonDocumentReader.java       |  148 ++
 .../format/JsonDocumentReaderFactory.java     |   33 +
 .../type/format/JsonDocumentWriter.java       |   33 +-
 .../type/format/JsonValueJDBCTypeAdapter.java |   47 +
 .../JsonValueJDBCTypeAdapterFactory.java      |   31 +
 .../type/format/OsonDocumentReader.java       |  215 +++
 .../type/format/OsonDocumentWriter.java       |   30 +-
 .../type/format/OsonValueJDBCTypeAdapter.java |   83 +
 .../type/format/StringJsonDocument.java       |   34 +
 .../type/format/StringJsonDocumentMarker.java |   51 +
 .../type/format/StringJsonDocumentReader.java |  505 ++++++
 .../type/format/StringJsonDocumentWriter.java |  126 +-
 .../StringJsonValueJDBCTypeAdapter.java       |  149 ++
 .../jackson/JacksonJsonFormatMapper.java      |    1 -
 .../util/StringJsonDocumentReaderTest.java    |  384 +++++
 .../util/StringJsonDocumentWriterTest.java    |  183 +++
 31 files changed, 2321 insertions(+), 1346 deletions(-)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
 create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
 create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
index 480f746a5d61..bf64c2676f7b 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
@@ -314,7 +314,8 @@ private static void addJsonFormatMappers(StrategySelectorImpl strategySelector)
 					JacksonOsonFormatMapper.SHORT_NAME,
 					JacksonOsonFormatMapper.class
 			);
-		} else {
+		}
+		else {
 			strategySelector.registerStrategyImplementor(
 					FormatMapper.class,
 					JacksonJsonFormatMapper.SHORT_NAME,
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index b20b7ed47533..a91990e0a856 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -1026,6 +1026,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 				StandardConverters.STRING,JACKSON_MAPPER_NAME);
 
 		if ( getVersion().isSameOrAfter( 21 ) ) {
+
 			if ( JacksonIntegration.isOracleOsonExtensionAvailable() && JACKSON_MAPPER_NAME.equalsIgnoreCase( mapperName )) {
 				// We must check that that extension is available and actually used.
 				typeContributions.contributeJdbcType( OracleOsonJacksonJdbcType.INSTANCE );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index 5026ef539e57..282798f062aa 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -27,6 +27,7 @@
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -131,7 +132,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 		return new BasicExtractor<>( javaType, this ) {
 
-			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
+			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
 				JsonParser osonParser = osonFactory.createParser( osonBytes );
@@ -142,7 +143,7 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 				if ( datum == null ) {
 					return null;
 				}
-				byte[] osonBytes = datum.shareBytes();
+				InputStream osonBytes = datum.getStream();
 				try {
 					return fromOson( osonBytes ,options);
 				}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 6ab8a284a4dc..4413058bfb06 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -22,12 +22,11 @@
 import org.hibernate.type.descriptor.jdbc.BasicBinder;
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
 import org.hibernate.type.format.FormatMapper;
-import org.hibernate.type.format.ObjectArrayOsonDocumentHandler;
 import org.hibernate.type.format.OsonDocumentWriter;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayOutputStream;
-import java.nio.ByteBuffer;
+import java.io.InputStream;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -139,7 +138,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 		return new BasicExtractor<>( javaType, this ) {
 
-			private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
+			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 
 				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
 
@@ -149,14 +148,23 @@ private X fromOson(byte[] osonBytes, WrapperOptions options) throws Exception {
 					// an array of objects. We use JsonParser to fetch values
 					// and build the array.(as opposed to let Jackson do it as we do not
 					// have a proper object definition at that stage).
-					OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( ByteBuffer.wrap( osonBytes ) );
-
-					ObjectArrayOsonDocumentHandler handler = new ObjectArrayOsonDocumentHandler( getEmbeddableMappingType(),
-							options);
-
-					OsonHelper.consumeOsonTokens(osonParser, osonParser.next(), handler);
-
-					return (X) handler.getObjectArray();
+					OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( osonBytes );
+
+
+					//ObjectArrayOsonDocumentHandler handler = new ObjectArrayOsonDocumentHandler( getEmbeddableMappingType(),
+						//	options);
+					//OsonHelper.consumeOsonTokens(osonParser, osonParser.next(), handler);
+
+					//osonBytes.reset();
+					//OracleJsonParser osonParser2 = new OracleJsonFactory().createJsonBinaryParser( osonBytes);
+					Object[] objects =  JsonHelper.deserialize(
+							getEmbeddableMappingType(),
+							osonParser,
+							javaType.getJavaTypeClass() != Object[].class,
+							options
+					);
+					//Object[] objects2 = (Object[]) handler.getObjectArray();
+					return (X) objects;
 				}
 
 				JavaType <X> type = getJavaType();
@@ -175,7 +183,7 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 				if ( datum == null ) {
 					return null;
 				}
-				byte[] osonBytes = datum.shareBytes();
+				InputStream osonBytes = datum.getStream();
 				try {
 					return fromOson( osonBytes ,options);
 				}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/CharSequenceHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/CharSequenceHelper.java
index 041013f5ad68..6dafe79e662a 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/util/CharSequenceHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/util/CharSequenceHelper.java
@@ -24,6 +24,10 @@ else if ( sequence instanceof SubSequence ) {
 		}
 	}
 
+	public static CharSequence subSequence(CharSequence sequence) {
+		return subSequence(sequence, 0, sequence.length());
+	}
+
 	public static boolean isEmpty(CharSequence string) {
 		return string == null || string.length() == 0;
 	}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java
index c955f68a49cc..182ca0db7c6d 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/BooleanPrimitiveArrayJavaType.java
@@ -81,7 +81,7 @@ public boolean[] fromString(CharSequence charSequence) {
 		final char lastChar = charSequence.charAt( charSequence.length() - 1 );
 		final char firstChar = charSequence.charAt( 0 );
 		if ( firstChar != '{' || lastChar != '}' ) {
-			throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
+			throw new IllegalArgumentException( "Cannot parse given string into array of Booleans. First and last character must be { and }" );
 		}
 		final int len = charSequence.length();
 		int elementStart = 1;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java
index 8317b4caf391..0ed6cc8f997f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DoublePrimitiveArrayJavaType.java
@@ -81,7 +81,7 @@ public double[] fromString(CharSequence charSequence) {
 		final char lastChar = charSequence.charAt( charSequence.length() - 1 );
 		final char firstChar = charSequence.charAt( 0 );
 		if ( firstChar != '{' || lastChar != '}' ) {
-			throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
+			throw new IllegalArgumentException( "Cannot parse given string into array of Doubles. First and last character must be { and }" );
 		}
 		final int len = charSequence.length();
 		int elementStart = 1;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java
index ad795cea243f..25f431b6a114 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/FloatPrimitiveArrayJavaType.java
@@ -81,7 +81,7 @@ public float[] fromString(CharSequence charSequence) {
 		final char lastChar = charSequence.charAt( charSequence.length() - 1 );
 		final char firstChar = charSequence.charAt( 0 );
 		if ( firstChar != '{' || lastChar != '}' ) {
-			throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
+			throw new IllegalArgumentException( "Cannot parse given string into array of Floats. First and last character must be { and }" );
 		}
 		final int len = charSequence.length();
 		int elementStart = 1;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java
index 693a60a5e0f4..703e12cf1fbd 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/IntegerPrimitiveArrayJavaType.java
@@ -81,7 +81,7 @@ public int[] fromString(CharSequence charSequence) {
 		final char lastChar = charSequence.charAt( charSequence.length() - 1 );
 		final char firstChar = charSequence.charAt( 0 );
 		if ( firstChar != '{' || lastChar != '}' ) {
-			throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
+			throw new IllegalArgumentException( "Cannot parse given string into array of integers. First and last character must be { and }" );
 		}
 		final int len = charSequence.length();
 		int elementStart = 1;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java
index 4a1df2c08b79..be7453a46d61 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/JavaType.java
@@ -268,6 +268,10 @@ default T fromEncodedString(CharSequence charSequence, int start, int end) {
 		return fromString( CharSequenceHelper.subSequence( charSequence, start, end ) );
 	}
 
+	default T fromEncodedString(CharSequence charSequence) {
+		return fromEncodedString( charSequence, 0, charSequence.length()  );
+	}
+
 	/**
 	 * Unwrap an instance of our handled Java type into the requested type.
 	 * <p>
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java
index 5331dc3f59b1..9ba2fb465034 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/LongPrimitiveArrayJavaType.java
@@ -81,7 +81,7 @@ public long[] fromString(CharSequence charSequence) {
 		final char lastChar = charSequence.charAt( charSequence.length() - 1 );
 		final char firstChar = charSequence.charAt( 0 );
 		if ( firstChar != '{' || lastChar != '}' ) {
-			throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
+			throw new IllegalArgumentException( "Cannot parse given string into array of Long. First and last character must be { and }" );
 		}
 		final int len = charSequence.length();
 		int elementStart = 1;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java
index 05175ddba8de..37b7cf758aac 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/ShortPrimitiveArrayJavaType.java
@@ -81,7 +81,7 @@ public short[] fromString(CharSequence charSequence) {
 		final char lastChar = charSequence.charAt( charSequence.length() - 1 );
 		final char firstChar = charSequence.charAt( 0 );
 		if ( firstChar != '{' || lastChar != '}' ) {
-			throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
+			throw new IllegalArgumentException( "Cannot parse given string into array of Shorts. First and last character must be { and }" );
 		}
 		final int len = charSequence.length();
 		int elementStart = 1;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 3c57a42129ef..14e06bdfe668 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -9,21 +9,19 @@
 import java.io.OutputStream;
 import java.lang.reflect.Array;
 import java.sql.SQLException;
-import java.time.OffsetDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.AbstractCollection;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 
 import org.hibernate.Internal;
-import org.hibernate.internal.build.AllowReflection;
-import org.hibernate.internal.util.CharSequenceHelper;
 import org.hibernate.internal.util.collections.ArrayHelper;
+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;
@@ -34,36 +32,35 @@
 import org.hibernate.type.SqlTypes;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.BasicPluralJavaType;
-import org.hibernate.type.descriptor.java.EnumJavaType;
 import org.hibernate.type.descriptor.java.JavaType;
-import org.hibernate.type.descriptor.java.JdbcDateJavaType;
-import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
-import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
-import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
-import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.format.JsonDocumentItem;
+import org.hibernate.type.format.JsonDocumentReader;
+import org.hibernate.type.format.JsonDocumentReaderFactory;
 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;
 
 /**
  * A Helper for serializing and deserializing JSON, based on an {@link org.hibernate.metamodel.mapping.EmbeddableMappingType}.
+ *
+ * @author Christian Beikov
+ * @author Emmanuel Jannetti
  */
 @Internal
 public class JsonHelper {
 
-	public static String toString(EmbeddableMappingType embeddableMappingType, Object value, WrapperOptions options) {
-		if ( value == null ) {
-			return null;
-		}
-		final StringBuilder sb = new StringBuilder();
-		toString( embeddableMappingType, value, options, new JsonAppender( sb ) );
-		return sb.toString();
-	}
-
-
+	/**
+	 * 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 ) {
@@ -87,6 +84,14 @@ public static void serializeArray(MappingType elementMappingType, Object[] value
 		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 ) {
@@ -137,7 +142,7 @@ public static void serialize(EmbeddableMappingType embeddableMappingType,
 	 * @param domainValue the value to be serialized.
 	 * @param options wrapping options
 	 * @param writer the document writer
-	 * @throws IOException
+	 * @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 {
@@ -212,257 +217,180 @@ else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
 		}
 	}
 
-	private static void toString(EmbeddableMappingType embeddableMappingType, Object value, WrapperOptions options, JsonAppender appender) {
-		toString( embeddableMappingType, options, appender, value, '{' );
-		appender.append( '}' );
-	}
-
-	private static void toString(
-			EmbeddableMappingType embeddableMappingType,
-			WrapperOptions options,
-			JsonAppender appender,
-			Object domainValue,
-			char separator) {
-		final Object[] values = embeddableMappingType.getValues( domainValue );
-		for ( int i = 0; i < values.length; i++ ) {
-			final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
-			if ( attributeMapping instanceof SelectableMapping selectableMapping ) {
-				final String name = selectableMapping.getSelectableName();
-				appender.append( separator );
-				appender.append( '"' );
-				appender.append( name );
-				appender.append( "\":" );
-				toString( attributeMapping.getMappedType(), values[i], options, appender );
-			}
-			else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
-				if ( values[i] == null ) {
-					// Skipping the update of the separator is on purpose
-					continue;
-				}
-				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
-				final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
-				if ( aggregateMapping == null) {
-					toString(
-							mappingType,
-							options,
-							appender,
-							values[i],
-							separator
-					);
-				}
-				else {
-					final String name = aggregateMapping.getSelectableName();
-					appender.append( separator );
-					appender.append( '"' );
-					appender.append( name );
-					appender.append( "\":" );
-					toString( mappingType, values[i], options, appender );
-				}
-			}
-			else {
-				throw new UnsupportedOperationException( "Support for attribute mapping type not yet implemented: " + attributeMapping.getClass().getName() );
-			}
-			separator = ',';
-		}
-	}
-
-	private static void toString(MappingType mappedType, Object value, WrapperOptions options, JsonAppender appender) {
-		if ( value == null ) {
-			appender.append( "null" );
-		}
-		else if ( mappedType instanceof EmbeddableMappingType embeddableMappingType ) {
-			toString( embeddableMappingType, value, options, appender );
-		}
-		else if ( mappedType instanceof BasicType<?> ) {
-			//noinspection unchecked
-			final BasicType<Object> basicType = (BasicType<Object>) mappedType;
-			convertedBasicValueToString( basicType.convertToRelationalValue( value ), options, appender, basicType );
-		}
-		else {
-			throw new UnsupportedOperationException( "Support for mapping type not yet implemented: " + mappedType.getClass().getName() );
-		}
-	}
-
-	private static void convertedValueToString(
-			JavaType<Object> javaType,
-			JdbcType jdbcType,
-			Object value,
-			WrapperOptions options,
-			JsonAppender appender) {
-		if ( value == null ) {
-			appender.append( "null" );
-		}
-		else if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
-			toString( aggregateJdbcType.getEmbeddableMappingType(), value, options, appender );
-		}
-		else {
-			convertedBasicValueToString( value, options, appender, javaType, jdbcType );
-		}
-	}
-
-
-	private static void convertedBasicValueToString(
-			Object value,
-			WrapperOptions options,
-			JsonAppender appender,
-			BasicType<Object> basicType) {
-		//noinspection unchecked
-		convertedBasicValueToString(
-				value,
-				options,
-				appender,
-				(JavaType<Object>) basicType.getJdbcJavaType(),
-				basicType.getJdbcType()
-		);
-	}
-
-	private static void convertedBasicValueToString(
-			Object value,
-			WrapperOptions options,
-			JsonAppender appender,
-			JavaType<Object> javaType,
-			JdbcType jdbcType) {
-		switch ( jdbcType.getDefaultSqlTypeCode() ) {
-			case SqlTypes.TINYINT:
-			case SqlTypes.SMALLINT:
-			case SqlTypes.INTEGER:
-				if ( value instanceof Boolean booleanValue ) {
-					// BooleanJavaType has this as an implicit conversion
-					appender.append( booleanValue ? '1' : '0' );
+	/**
+	 * 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>
+	 * @throws SQLException if error occured during mapping of types
+	 */
+	private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, EmbeddableMappingType embeddableMappingType, boolean returnEmbeddable, WrapperOptions options)
+			throws SQLException {
+		// final result of a mapped object array
+		Object [] objectArrayResult;
+		// current mapping to be used
+		SelectableMapping currentSelectableMapping = null;
+		String currentKeyName = null;
+		List<Object> subArrayObjectList = null;
+		BasicPluralType<?, ?> subArrayObjectTypes = null;
+
+		// mapping definitions are in a tree
+		// Each mapping definition may contain sub mappings (sub embeddable mapping)
+		// This stack is used to keep a pointer on the current mapping to be used to assign correct types.
+		// see onStartObject()/onEndObject() methods
+		StandardStack<EmbeddableMappingType> embeddableMappingTypes = new StandardStack<>();
+		// As for mapping definitions, when "sub embeddable" is encountered, the array
+		// that needs to be filled with Objects is the one we allocate in the final result array slot.
+		// We use a stack to keep track of array ref
+		StandardStack<Object[]> objectArrays = new StandardStack<>();
+
+		// index within objectArrayResult
+		int currentSelectableIndexInResultArray = -1;
+
+		JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,returnEmbeddable);
+
+		embeddableMappingTypes.push(embeddableMappingType);
+		objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()+ ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
+		objectArrays.push( objectArrayResult );
+
+		while(reader.hasNext()) {
+			JsonDocumentItem.JsonDocumentItemType type = reader.next();
+			switch (type) {
+				case VALUE_KEY:
+					currentKeyName = reader.getObjectKeyName();
+
+					currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
+					if ( currentSelectableIndexInResultArray >= 0 ) {
+						// we may not have a selectable mapping for that key
+						currentSelectableMapping = embeddableMappingTypes.getCurrent().getJdbcValueSelectable( currentSelectableIndexInResultArray );
+					}
+					else {
+						throw new IllegalArgumentException(
+								String.format(
+										"Could not find selectable [%s] in embeddable type [%s] for JSON processing.",
+										currentKeyName,
+										embeddableMappingTypes.getCurrent().getMappedJavaType().getJavaTypeClass().getName()
+								)
+						);
+					}
 					break;
-				}
-				if ( value instanceof Enum<?> enumValue ) {
-					appender.appendSql( enumValue.ordinal() );
+				case ARRAY_START:
+					assert (subArrayObjectList == null && subArrayObjectTypes == null) : "ARRAY_START item received twice in a row";
+
+					// initialize an array to gather values
+					subArrayObjectList = new ArrayList<>();
+					assert (currentSelectableMapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
+							: "Array event received for non plural type";
+					// initialize array's element type
+					subArrayObjectTypes = (BasicPluralType<?, ?>) currentSelectableMapping.getJdbcMapping();
 					break;
-				}
-			case SqlTypes.BOOLEAN:
-			case SqlTypes.BIT:
-			case SqlTypes.BIGINT:
-			case SqlTypes.FLOAT:
-			case SqlTypes.REAL:
-			case SqlTypes.DOUBLE:
-				// These types fit into the native representation of JSON, so let's use that
-				javaType.appendEncodedString( appender, value );
-				break;
-			case SqlTypes.CHAR:
-			case SqlTypes.NCHAR:
-			case SqlTypes.VARCHAR:
-			case SqlTypes.NVARCHAR:
-				if ( value instanceof Boolean booleanValue ) {
-					// BooleanJavaType has this as an implicit conversion
-					appender.append( '"' );
-					appender.append( booleanValue ? 'Y' : 'N' );
-					appender.append( '"' );
+				case ARRAY_END:
+					assert (subArrayObjectList != null && subArrayObjectTypes != null) : "ARRAY_END item received twice in a row";
+					// flush array values
+					objectArrays.getCurrent()[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, options );
+					// reset until we encounter next array element
+					subArrayObjectList = null;
+					subArrayObjectTypes = null;
 					break;
-				}
-			case SqlTypes.LONGVARCHAR:
-			case SqlTypes.LONGNVARCHAR:
-			case SqlTypes.LONG32VARCHAR:
-			case SqlTypes.LONG32NVARCHAR:
-			case SqlTypes.CLOB:
-			case SqlTypes.MATERIALIZED_CLOB:
-			case SqlTypes.NCLOB:
-			case SqlTypes.MATERIALIZED_NCLOB:
-			case SqlTypes.ENUM:
-			case SqlTypes.NAMED_ENUM:
-				// These literals can contain the '"' character, so we need to escape it
-				appender.append( '"' );
-				appender.startEscaping();
-				javaType.appendEncodedString( appender, value );
-				appender.endEscaping();
-				appender.append( '"' );
-				break;
-			case SqlTypes.DATE:
-				appender.append( '"' );
-				JdbcDateJavaType.INSTANCE.appendEncodedString(
-						appender,
-						javaType.unwrap( value, java.sql.Date.class, options )
-				);
-				appender.append( '"' );
-				break;
-			case SqlTypes.TIME:
-			case SqlTypes.TIME_WITH_TIMEZONE:
-			case SqlTypes.TIME_UTC:
-				appender.append( '"' );
-				JdbcTimeJavaType.INSTANCE.appendEncodedString(
-						appender,
-						javaType.unwrap( value, java.sql.Time.class, options )
-				);
-				appender.append( '"' );
-				break;
-			case SqlTypes.TIMESTAMP:
-				appender.append( '"' );
-				JdbcTimestampJavaType.INSTANCE.appendEncodedString(
-						appender,
-						javaType.unwrap( value, java.sql.Timestamp.class, options )
-				);
-				appender.append( '"' );
-				break;
-			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
-			case SqlTypes.TIMESTAMP_UTC:
-				appender.append( '"' );
-				DateTimeFormatter.ISO_OFFSET_DATE_TIME.formatTo(
-						javaType.unwrap( value, OffsetDateTime.class, options ),
-						appender
-				);
-				appender.append( '"' );
-				break;
-			case SqlTypes.DECIMAL:
-			case SqlTypes.NUMERIC:
-			case SqlTypes.DURATION:
-			case SqlTypes.UUID:
-				// These types need to be serialized as JSON string, but don't have a need for escaping
-				appender.append( '"' );
-				javaType.appendEncodedString( appender, value );
-				appender.append( '"' );
-				break;
-			case SqlTypes.BINARY:
-			case SqlTypes.VARBINARY:
-			case SqlTypes.LONGVARBINARY:
-			case SqlTypes.LONG32VARBINARY:
-			case SqlTypes.BLOB:
-			case SqlTypes.MATERIALIZED_BLOB:
-				// These types need to be serialized as JSON string, and for efficiency uses appendString directly
-				appender.append( '"' );
-				appender.write( javaType.unwrap( value, byte[].class, options ) );
-				appender.append( '"' );
-				break;
-			case SqlTypes.ARRAY:
-			case SqlTypes.JSON_ARRAY:
-				final int length = Array.getLength( value );
-				appender.append( '[' );
-				if ( length != 0 ) {
-					//noinspection unchecked
-					final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) javaType ).getElementJavaType();
-					final JdbcType elementJdbcType = ( (ArrayJdbcType) jdbcType ).getElementJdbcType();
-					Object arrayElement = Array.get( value, 0 );
-					convertedValueToString( elementJavaType, elementJdbcType, arrayElement, options, appender );
-					for ( int i = 1; i < length; i++ ) {
-						arrayElement = Array.get( value, i );
-						appender.append( ',' );
-						convertedValueToString( elementJavaType, elementJdbcType, arrayElement, options, appender );
+				case OBJECT_START:
+					if (currentKeyName != null) {
+						// We are dealing with a sub-object, allocate space for it then,
+						// otherwise, we have nothing to do.
+						// Push the new (sub)mapping definition.
+						assert embeddableMappingTypes.getCurrent() != null;
+						currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
+						assert currentSelectableIndexInResultArray != -1: "Cannot get index of " + currentKeyName;
+
+						final SelectableMapping selectable = embeddableMappingTypes.getCurrent().getJdbcValueSelectable(
+								currentSelectableIndexInResultArray );
+						final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
+								.getJdbcType();
+						final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
+						assert objectArrays.getCurrent() != null;
+						objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
+								new Object[subMappingType.getJdbcValueCount()];
+						embeddableMappingTypes.push( subMappingType );
+						objectArrays.push( (Object[]) objectArrays.getCurrent()[currentSelectableIndexInResultArray] );
 					}
-				}
-				appender.append( ']' );
-				break;
-			default:
-				throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
+					break;
+				case OBJECT_END:
+					// go back in the mapping definition tree
+					embeddableMappingTypes.pop();
+					objectArrays.pop();
+					break;
+				case NULL_VALUE:
+					if ( subArrayObjectList != null ) {
+						// dealing with arrays
+						subArrayObjectList.add( null );
+					}
+					else {
+						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = null;
+					}
+					break;
+				case NUMERIC_VALUE:
+					if ( subArrayObjectList != null ) {
+						// dealing with arrays
+						subArrayObjectList.add( adapter.fromNumericValue( subArrayObjectTypes.getElementType().getJdbcJavaType(),
+								subArrayObjectTypes.getElementType().getJdbcType(),reader,options));
+					}
+					else {
+						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = adapter.fromNumericValue( currentSelectableMapping.getJdbcMapping().getJdbcJavaType(),
+								currentSelectableMapping.getJdbcMapping().getJdbcType(),reader,options);
+					}
+					break;
+				case BOOLEAN_VALUE:
+					if ( subArrayObjectList != null ) {
+						// dealing with arrays
+						subArrayObjectList.add( reader.getBooleanValue()?Boolean.TRUE:Boolean.FALSE);
+					}
+					else {
+						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = reader.getBooleanValue()?Boolean.TRUE:Boolean.FALSE;
+					}
+					break;
+				case VALUE:
+					if ( subArrayObjectList != null ) {
+						// dealing with arrays
+						subArrayObjectList.add(adapter.fromValue( subArrayObjectTypes.getElementType().getJdbcJavaType(),
+								subArrayObjectTypes.getElementType().getJdbcType(),reader,options));
+					}
+					else {
+						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = adapter.fromValue( currentSelectableMapping.getJdbcMapping().getJdbcJavaType(),
+								currentSelectableMapping.getJdbcMapping().getJdbcType(),reader,options);
+					}
+
+					break;
+				default:
+					assert false: "Unexpected type " + type;
+			}
 		}
+		return (X) objectArrayResult;
 	}
 
-	public static <X> X fromString(
+	/**
+	 * Deserialize a JSON value to Java Object
+	 * @param embeddableMappingType the mapping type
+	 * @param source the JSON value
+	 * @param returnEmbeddable do we return an Embeddable object or array of Objects
+	 * @param options wrappping options
+	 * @return the deserialized value
+	 * @param <X>
+	 * @throws SQLException
+	 */
+	public static <X> X deserialize(
 			EmbeddableMappingType embeddableMappingType,
-			String string,
+			Object source,
 			boolean returnEmbeddable,
 			WrapperOptions options) throws SQLException {
-		if ( string == null ) {
+
+		if ( source == null ) {
 			return null;
 		}
+		JsonDocumentReader reader = JsonDocumentReaderFactory.getJsonDocumentReader(source);
 
-		final int jdbcValueCount = embeddableMappingType.getJdbcValueCount();
-		final Object[] values = new Object[jdbcValueCount + ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
-		final int end = fromString( embeddableMappingType, string, 0, string.length(), values, returnEmbeddable, options );
-		assert string.substring( end ).isBlank();
+		final Object[] values = consumeJsonDocumentItems(reader, embeddableMappingType, returnEmbeddable, options);
 		if ( returnEmbeddable ) {
 			final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
 					embeddableMappingType,
@@ -482,9 +410,12 @@ public static <X> X arrayFromString(
 			JdbcType elementJdbcType,
 			String string,
 			WrapperOptions options) throws SQLException {
+
 		if ( string == null ) {
 			return null;
 		}
+
+		final CustomArrayList arrayList = new CustomArrayList();
 		final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) javaType).getElementJavaType();
 		final Class<?> preferredJavaTypeClass = elementJdbcType.getPreferredJavaTypeClass( options );
 		final JavaType<?> jdbcJavaType;
@@ -494,994 +425,41 @@ public static <X> X arrayFromString(
 		else {
 			jdbcJavaType = options.getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor( preferredJavaTypeClass );
 		}
-		final CustomArrayList arrayList = new CustomArrayList();
-		final int i = fromArrayString(
-				string,
-				false,
-				options,
-				0,
-				arrayList,
-				elementJavaType,
-				jdbcJavaType,
-				elementJdbcType
-		);
-		assert string.charAt( i - 1 ) == ']';
-		return javaType.wrap( arrayList, options );
-	}
-
-	private static int fromString(
-			EmbeddableMappingType embeddableMappingType,
-			String string,
-			int begin,
-			int end,
-			Object[] values,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		boolean hasEscape = false;
-		assert string.charAt( begin ) == '{';
-		int start = begin + 1;
-		State s = State.KEY_START;
-		int selectableIndex = -1;
-		// The following parsing logic assumes JSON is well-formed,
-		// but for the sake of the Java compiler's flow analysis
-		// and hopefully also for a better understanding, contains throws for some syntax errors
-		for ( int i = start; i < string.length(); i++ ) {
-			final char c = string.charAt( i );
-			switch ( c ) {
-				case '\\':
-					assert s == State.KEY_QUOTE || s == State.VALUE_QUOTE;
-					hasEscape = true;
-					i++;
-					break;
-				case '"':
-					switch ( s ) {
-						case KEY_START:
-							s = State.KEY_QUOTE;
-							selectableIndex = -1;
-							start = i + 1;
-							hasEscape = false;
-							break;
-						case KEY_QUOTE:
-							s = State.KEY_END;
-							selectableIndex = getSelectableMapping(
-									embeddableMappingType,
-									string,
-									start,
-									i,
-									hasEscape
-							);
-							start = -1;
-							hasEscape = false;
-							break;
-						case VALUE_START:
-							s = State.VALUE_QUOTE;
-							start = i + 1;
-							hasEscape = false;
-							break;
-						case VALUE_QUOTE:
-							s = State.VALUE_END;
-							values[selectableIndex] = fromString(
-									embeddableMappingType.getJdbcValueSelectable( selectableIndex ).getJdbcMapping(),
-									string,
-									start,
-									i,
-									hasEscape,
-									returnEmbeddable,
-									options
-							);
-							selectableIndex = -1;
-							start = -1;
-							hasEscape = false;
-							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				case ':':
-					switch ( s ) {
-						case KEY_QUOTE:
-							// I guess it's ok to have a ':' in the key..
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-						case KEY_END:
-							s = State.VALUE_START;
-							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				case ',':
-					switch ( s ) {
-						case KEY_QUOTE:
-							// I guess it's ok to have a ',' in the key..
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-						case VALUE_END:
-							s = State.KEY_START;
-							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				case '{':
-					switch ( s ) {
-						case KEY_QUOTE:
-							// I guess it's ok to have a '{' in the key..
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-						case VALUE_START:
-							final SelectableMapping selectable = embeddableMappingType.getJdbcValueSelectable(
-									selectableIndex
-							);
-							if ( !( selectable.getJdbcMapping().getJdbcType()
-									instanceof AggregateJdbcType aggregateJdbcType) ) {
-								throw new IllegalArgumentException(
-										String.format(
-												"JSON starts sub-object for a non-aggregate type at index %d. Selectable [%s] is of type [%s]",
-												i,
-												selectable.getSelectableName(),
-												selectable.getJdbcMapping().getJdbcType().getClass().getName()
-										)
-								);
-							}
-							final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-							// This encoding is only possible if the JDBC type is JSON again
-							assert aggregateJdbcType.getJdbcTypeCode() == SqlTypes.JSON
-									|| aggregateJdbcType.getDefaultSqlTypeCode() == SqlTypes.JSON;
-							final Object[] subValues = new Object[subMappingType.getJdbcValueCount()];
-							i = fromString( subMappingType, string, i, end, subValues, returnEmbeddable, options ) - 1;
-							assert string.charAt( i ) == '}';
-							if ( returnEmbeddable ) {
-								final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
-										subMappingType,
-										subValues,
-										options
-								);
-								values[selectableIndex] = instantiate( embeddableMappingType, attributeValues );
-							}
-							else {
-								values[selectableIndex] = subValues;
-							}
-							s = State.VALUE_END;
-							selectableIndex = -1;
-							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				case '[':
-					switch ( s ) {
-						case KEY_QUOTE:
-							// I guess it's ok to have a '[' in the key..
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-						case VALUE_START:
-							final SelectableMapping selectable = embeddableMappingType.getJdbcValueSelectable(
-									selectableIndex
-							);
-							final JdbcMapping jdbcMapping = selectable.getJdbcMapping();
-							if ( !(jdbcMapping instanceof BasicPluralType<?, ?> pluralType) ) {
-								throw new IllegalArgumentException(
-										String.format(
-												"JSON starts array for a non-plural type at index %d. Selectable [%s] is of type [%s]",
-												i,
-												selectable.getSelectableName(),
-												jdbcMapping.getJdbcType().getClass().getName()
-										)
-								);
-							}
-							final BasicType<?> elementType = pluralType.getElementType();
-							final CustomArrayList arrayList = new CustomArrayList();
-							i = fromArrayString( string, returnEmbeddable, options, i, arrayList, elementType ) - 1;
-							assert string.charAt( i ) == ']';
-							values[selectableIndex] = pluralType.getJdbcJavaType().wrap( arrayList, options );
-							s = State.VALUE_END;
-							selectableIndex = -1;
-							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				case '}':
-					switch ( s ) {
-						case KEY_QUOTE:
-							// I guess it's ok to have a '}' in the key..
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-						case VALUE_END:
-							// At this point, we are done
-							return i + 1;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				default:
-					switch ( s ) {
-						case KEY_QUOTE:
-						case VALUE_QUOTE:
-							// In keys and values, all chars are fine
-							break;
-						case VALUE_START:
-							// Skip whitespace
-							if ( Character.isWhitespace( c ) ) {
-								break;
-							}
-							// Here we also allow certain literals
-							final int endIdx = consumeLiteral(
-									string,
-									i,
-									values,
-									embeddableMappingType.getJdbcValueSelectable( selectableIndex ).getJdbcMapping(),
-									selectableIndex,
-									returnEmbeddable,
-									options
-							);
-							if ( endIdx != -1 ) {
-								i = endIdx;
-								s = State.VALUE_END;
-								selectableIndex = -1;
-								start = -1;
-								break;
-							}
-							throw syntaxError( string, s, i );
-						case KEY_START:
-						case KEY_END:
-						case VALUE_END:
-							// Only whitespace is allowed here
-							if ( Character.isWhitespace( c ) ) {
-								break;
-							}
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-			}
-		}
-
-		throw new IllegalArgumentException( "JSON not properly formed: " + string.subSequence( start, end ) );
-	}
-
-	private static int fromArrayString(
-			String string,
-			boolean returnEmbeddable,
-			WrapperOptions options,
-			int begin,
-			CustomArrayList arrayList,
-			BasicType<?> elementType) throws SQLException {
-		return fromArrayString(
-				string,
-				returnEmbeddable,
-				options,
-				begin,
-				arrayList,
-				elementType.getMappedJavaType(),
-				elementType.getJdbcJavaType(),
-				elementType.getJdbcType()
-		);
-	}
-
-	private static int fromArrayString(
-			String string,
-			boolean returnEmbeddable,
-			WrapperOptions options,
-			int begin,
-			CustomArrayList arrayList,
-			JavaType<?> javaType,
-			JavaType<?> jdbcJavaType,
-			JdbcType jdbcType) throws SQLException {
-		if ( string.length() == begin + 2 ) {
-			return begin + 2;
-		}
-		boolean hasEscape = false;
-		assert string.charAt( begin ) == '[';
-		int start = begin + 1;
-		State s = State.VALUE_START;
-		// The following parsing logic assumes JSON is well-formed,
-		// but for the sake of the Java compiler's flow analysis
-		// and hopefully also for a better understanding, contains throws for some syntax errors
-		for ( int i = start; i < string.length(); i++ ) {
-			final char c = string.charAt( i );
-			switch ( c ) {
-				case '\\':
-					assert s == State.VALUE_QUOTE;
-					hasEscape = true;
-					i++;
+		JsonDocumentReader reader = JsonDocumentReaderFactory.getJsonDocumentReader(string);
+		JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,false);
+
+		assert reader.hasNext():"Invalid array string";
+		assert reader.next() == JsonDocumentItem.JsonDocumentItemType.ARRAY_START:"Invalid start of array";
+		boolean endArrayFound = false;
+		while(reader.hasNext()) {
+			JsonDocumentItem.JsonDocumentItemType type = reader.next();
+			switch ( type ) {
+				case JsonDocumentItem.JsonDocumentItemType.ARRAY_END:
+					endArrayFound=true;
 					break;
-				case '"':
-					switch ( s ) {
-						case VALUE_START:
-							s = State.VALUE_QUOTE;
-							start = i + 1;
-							hasEscape = false;
-							break;
-						case VALUE_QUOTE:
-							s = State.VALUE_END;
-							arrayList.add(
-									fromString(
-											javaType,
-											jdbcJavaType,
-											jdbcType,
-											string,
-											start,
-											i,
-											hasEscape,
-											returnEmbeddable,
-											options
-									)
-							);
-							start = -1;
-							hasEscape = false;
-							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
+				case NULL_VALUE:
+					arrayList.add( null );
 					break;
-				case ',':
-					switch ( s ) {
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-						case VALUE_END:
-							s = State.VALUE_START;
-							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
+				case NUMERIC_VALUE:
+					arrayList.add( adapter.fromNumericValue(jdbcJavaType, elementJdbcType ,reader, options)  );
 					break;
-				case '{':
-					switch ( s ) {
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-//						case VALUE_START:
-//							final SelectableMapping selectable = embeddableMappingType.getJdbcValueSelectable(
-//									selectableIndex
-//							);
-//							if ( !( selectable.getJdbcMapping().getJdbcType() instanceof AggregateJdbcType ) ) {
-//								throw new IllegalArgumentException(
-//										String.format(
-//												"JSON starts sub-object for a non-aggregate type at index %d. Selectable [%s] is of type [%s]",
-//												i,
-//												selectable.getSelectableName(),
-//												selectable.getJdbcMapping().getJdbcType().getClass().getName()
-//										)
-//								);
-//							}
-//							final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping().getJdbcType();
-//							final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-//							// This encoding is only possible if the JDBC type is JSON again
-//							assert aggregateJdbcType.getJdbcTypeCode() == SqlTypes.JSON
-//									|| aggregateJdbcType.getDefaultSqlTypeCode() == SqlTypes.JSON;
-//							final Object[] subValues = new Object[subMappingType.getJdbcValueCount()];
-//							i = fromString( subMappingType, string, i, end, subValues, returnEmbeddable, options ) - 1;
-//							assert string.charAt( i ) == '}';
-//							if ( returnEmbeddable ) {
-//								final Object[] attributeValues = StructHelper.getAttributeValues(
-//										subMappingType,
-//										subValues,
-//										options
-//								);
-//								values[selectableIndex] = embeddableMappingType.getRepresentationStrategy()
-//										.getInstantiator()
-//										.instantiate(
-//												() -> attributeValues,
-//												options.getSessionFactory()
-//										);
-//							}
-//							else {
-//								values[selectableIndex] = subValues;
-//							}
-//							s = State.VALUE_END;
-//							selectableIndex = -1;
-//							break;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				case ']':
-					switch ( s ) {
-						case VALUE_QUOTE:
-							// In the value it's fine
-							break;
-						case VALUE_END:
-							// At this point, we are done
-							return i + 1;
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-				default:
-					switch ( s ) {
-						case VALUE_QUOTE:
-							// In keys and values, all chars are fine
-							break;
-						case VALUE_START:
-							// Skip whitespace
-							if ( Character.isWhitespace( c ) ) {
-								break;
-							}
-							final int elementIndex = arrayList.size();
-							arrayList.add( null );
-							// Here we also allow certain literals
-							final int endIdx = consumeLiteral(
-									string,
-									i,
-									arrayList.getUnderlyingArray(),
-									javaType,
-									jdbcJavaType,
-									jdbcType,
-									elementIndex,
-									returnEmbeddable,
-									options
-							);
-							if ( endIdx != -1 ) {
-								i = endIdx;
-								s = State.VALUE_END;
-								start = -1;
-								break;
-							}
-							throw syntaxError( string, s, i );
-						case VALUE_END:
-							// Only whitespace is allowed here
-							if ( Character.isWhitespace( c ) ) {
-								break;
-							}
-						default:
-							throw syntaxError( string, s, i );
-					}
-					break;
-			}
-		}
-
-		throw new IllegalArgumentException( "JSON not properly formed: " + string.subSequence( start, string.length() ) );
-	}
-
-	private static int consumeLiteral(
-			String string,
-			int start,
-			Object[] values,
-			JdbcMapping jdbcMapping,
-			int selectableIndex,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		return consumeLiteral(
-				string,
-				start,
-				values,
-				jdbcMapping.getMappedJavaType(),
-				jdbcMapping.getJdbcJavaType(),
-				jdbcMapping.getJdbcType(),
-				selectableIndex,
-				returnEmbeddable,
-				options
-		);
-	}
-
-	private static int consumeLiteral(
-			String string,
-			int start,
-			Object[] values,
-			JavaType<?> javaType,
-			JavaType<?> jdbcJavaType,
-			JdbcType jdbcType,
-			int selectableIndex,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		final char c = string.charAt( start );
-		switch ( c ) {
-			case 'n':
-				// only null is possible
-				values[selectableIndex] = null;
-				return consume(string, start, "null");
-			case 'f':
-				// only false is possible
-				values[selectableIndex] = false;
-				return consume(string, start, "false");
-			case 't':
-				// only false is possible
-				values[selectableIndex] = true;
-				return consume(string, start, "true");
-			case '0':
-				switch ( string.charAt( start + 1 ) ) {
-					case '.':
-						return consumeFractional(
-								string,
-								start,
-								start + 1,
-								values,
-								javaType,
-								jdbcJavaType,
-								jdbcType,
-								selectableIndex,
-								returnEmbeddable,
-								options
-						);
-					case 'E':
-					case 'e':
-						return consumeExponential(
-								string,
-								start,
-								start + 1,
-								values,
-								javaType,
-								jdbcJavaType,
-								jdbcType,
-								selectableIndex,
-								returnEmbeddable,
-								options
-						);
-				}
-				values[selectableIndex] = fromString(
-						javaType,
-						jdbcJavaType,
-						jdbcType,
-						string,
-						start,
-						start + 1,
-						returnEmbeddable,
-						options
-				);
-				return start;
-			case '-':
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-			case '8':
-			case '9':
-				// number = [ minus ] int [ frac ] [ exp ]
-				// decimal-point = %x2E       ; .
-				// digit1-9 = %x31-39         ; 1-9
-				// e = %x65 / %x45            ; e E
-				// exp = e [ minus / plus ] 1*DIGIT
-				// frac = decimal-point 1*DIGIT
-				// int = zero / ( digit1-9 *DIGIT )
-				// minus = %x2D               ; -
-				// plus = %x2B                ; +
-				// zero = %x30                ; 0
-				for (int i = start + 1; i < string.length(); i++) {
-					final char digit = string.charAt( i );
-					switch ( digit ) {
-						case '.':
-							return consumeFractional(
-									string,
-									start,
-									i,
-									values,
-									javaType,
-									jdbcJavaType,
-									jdbcType,
-									selectableIndex,
-									returnEmbeddable,
-									options
-							);
-						case 'E':
-						case 'e':
-							return consumeExponential(
-									string,
-									start,
-									i,
-									values,
-									javaType,
-									jdbcJavaType,
-									jdbcType,
-									selectableIndex,
-									returnEmbeddable,
-									options
-							);
-						case '0':
-						case '1':
-						case '2':
-						case '3':
-						case '4':
-						case '5':
-						case '6':
-						case '7':
-						case '8':
-						case '9':
-							break;
-						default:
-							values[selectableIndex] = fromString(
-									javaType,
-									jdbcJavaType,
-									jdbcType,
-									string,
-									start,
-									i,
-									returnEmbeddable,
-									options
-							);
-							return i - 1;
-					}
-				}
-		}
-
-		return -1;
-	}
-
-	private static int consumeFractional(
-			String string,
-			int start,
-			int dotIndex,
-			Object[] values,
-			JavaType<?> javaType,
-			JavaType<?> jdbcJavaType,
-			JdbcType jdbcType,
-			int selectableIndex,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		for (int i = dotIndex + 1; i < string.length(); i++) {
-			final char digit = string.charAt( i );
-			switch ( digit ) {
-				case 'E':
-				case 'e':
-					return consumeExponential(
-							string,
-							start,
-							i,
-							values,
-							javaType,
-							jdbcJavaType,
-							jdbcType,
-							selectableIndex,
-							returnEmbeddable,
-							options
-					);
-				case '0':
-				case '1':
-				case '2':
-				case '3':
-				case '4':
-				case '5':
-				case '6':
-				case '7':
-				case '8':
-				case '9':
+				case BOOLEAN_VALUE:
+					arrayList.add( reader.getBooleanValue() ? Boolean.TRUE : Boolean.FALSE );
 					break;
-				default:
-					values[selectableIndex] = fromString(
-							javaType,
-							jdbcJavaType,
-							jdbcType,
-							string,
-							start,
-							i,
-							returnEmbeddable,
-							options
-					);
-					return i - 1;
-			}
-		}
-		return start;
-	}
-
-	private static int consumeExponential(
-			String string,
-			int start,
-			int eIndex,
-			Object[] values,
-			JavaType<?> javaType,
-			JavaType<?> jdbcJavaType,
-			JdbcType jdbcType,
-			int selectableIndex,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		int i = eIndex + 1;
-		switch ( string.charAt( i ) ) {
-			case '-':
-			case '+':
-				i++;
-				break;
-		}
-		for (; i < string.length(); i++) {
-			final char digit = string.charAt( i );
-			switch ( digit ) {
-				case '0':
-				case '1':
-				case '2':
-				case '3':
-				case '4':
-				case '5':
-				case '6':
-				case '7':
-				case '8':
-				case '9':
+				case VALUE:
+					arrayList.add( adapter.fromValue(jdbcJavaType, elementJdbcType ,reader, options) );
 					break;
 				default:
-					values[selectableIndex] = fromString(
-							javaType,
-							jdbcJavaType,
-							jdbcType,
-							string,
-							start,
-							i,
-							returnEmbeddable,
-							options
-					);
-					return i - 1;
+					assert false : "Unexpected type " + type;
 			}
 		}
-		return start;
-	}
-
-	private static int consume(String string, int start, String text) {
-		if ( !string.regionMatches( start + 1, text, 1, text.length() - 1 ) ) {
-			throw new IllegalArgumentException(
-					String.format(
-							"Syntax error at position %d. Unexpected char [%s]. Expecting [%s]",
-							start + 1,
-							string.charAt( start + 1 ),
-							text
-					)
-			);
-		}
-		return start + text.length() - 1;
-	}
 
-	private static IllegalArgumentException syntaxError(String string, State s, int charIndex) {
-		return new IllegalArgumentException(
-				String.format(
-						"Syntax error at position %d. Unexpected char [%s]. Expecting one of [%s]",
-						charIndex,
-						string.charAt( charIndex ),
-						s.expectedChars()
-				)
-		);
-	}
 
-	private static int getSelectableMapping(
-			EmbeddableMappingType embeddableMappingType,
-			String string,
-			int start,
-			int end,
-			boolean hasEscape) {
-		final String name = hasEscape
-				? unescape( string, start, end )
-				: string.substring( start, end );
-		final int selectableIndex = embeddableMappingType.getSelectableIndex( name );
-		if ( selectableIndex == -1 ) {
-			throw new IllegalArgumentException(
-					String.format(
-							"Could not find selectable [%s] in embeddable type [%s] for JSON processing.",
-							name,
-							embeddableMappingType.getMappedJavaType().getJavaTypeClass().getName()
-					)
-			);
-		}
-		return selectableIndex;
-	}
-
-	private static Object fromString(
-			JdbcMapping jdbcMapping,
-			String string,
-			int start,
-			int end,
-			boolean hasEscape,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		return fromString(
-				jdbcMapping.getMappedJavaType(),
-				jdbcMapping.getJdbcJavaType(),
-				jdbcMapping.getJdbcType(),
-				string,
-				start,
-				end,
-				hasEscape,
-				returnEmbeddable,
-				options
-		);
-	}
-
-	private static Object fromString(
-			JavaType<?> javaType,
-			JavaType<?> jdbcJavaType,
-			JdbcType jdbcType,
-			String string,
-			int start,
-			int end,
-			boolean hasEscape,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		if ( hasEscape ) {
-			final String unescaped = unescape( string, start, end );
-			return fromString(
-					javaType,
-					jdbcJavaType,
-					jdbcType,
-					unescaped,
-					0,
-					unescaped.length(),
-					returnEmbeddable,
-					options
-			);
-		}
-		return fromString(
-				javaType,
-				jdbcJavaType,
-				jdbcType,
-				string,
-				start,
-				end,
-				returnEmbeddable,
-				options
-		);
-	}
-
-	private static Object fromString(
-			JavaType<?> javaType,
-			JavaType<?> jdbcJavaType,
-			JdbcType jdbcType,
-			String string,
-			int start,
-			int end,
-			boolean returnEmbeddable,
-			WrapperOptions options) throws SQLException {
-		switch ( jdbcType.getDefaultSqlTypeCode() ) {
-			case SqlTypes.BINARY:
-			case SqlTypes.VARBINARY:
-			case SqlTypes.LONGVARBINARY:
-			case SqlTypes.LONG32VARBINARY:
-				return jdbcJavaType.wrap(
-						PrimitiveByteArrayJavaType.INSTANCE.fromEncodedString(
-							string,
-							start,
-							end
-						),
-						options
-				);
-			case SqlTypes.UUID:
-				return jdbcJavaType.wrap(
-						PrimitiveByteArrayJavaType.INSTANCE.fromString(
-								string.substring( start, end ).replace( "-", "" )
-						),
-						options
-				);
-			case SqlTypes.DATE:
-				return jdbcJavaType.wrap(
-						JdbcDateJavaType.INSTANCE.fromEncodedString(
-								string,
-								start,
-								end
-						),
-						options
-				);
-			case SqlTypes.TIME:
-			case SqlTypes.TIME_WITH_TIMEZONE:
-			case SqlTypes.TIME_UTC:
-				return jdbcJavaType.wrap(
-						JdbcTimeJavaType.INSTANCE.fromEncodedString(
-								string,
-								start,
-								end
-						),
-						options
-				);
-			case SqlTypes.TIMESTAMP:
-				return jdbcJavaType.wrap(
-						JdbcTimestampJavaType.INSTANCE.fromEncodedString(
-								string,
-								start,
-								end
-						),
-						options
-				);
-			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
-			case SqlTypes.TIMESTAMP_UTC:
-				return jdbcJavaType.wrap(
-						OffsetDateTimeJavaType.INSTANCE.fromEncodedString(
-								string,
-								start,
-								end
-						),
-						options
-				);
-			case SqlTypes.TINYINT:
-			case SqlTypes.SMALLINT:
-			case SqlTypes.INTEGER:
-				if ( jdbcJavaType.getJavaTypeClass() == Boolean.class ) {
-					return jdbcJavaType.wrap( Integer.parseInt( string, start, end, 10 ), options );
-				}
-				else if ( jdbcJavaType instanceof EnumJavaType<?> ) {
-					return jdbcJavaType.wrap( Integer.parseInt( string, start, end, 10 ), options );
-				}
-			case SqlTypes.CHAR:
-			case SqlTypes.NCHAR:
-			case SqlTypes.VARCHAR:
-			case SqlTypes.NVARCHAR:
-				if ( jdbcJavaType.getJavaTypeClass() == Boolean.class && end == start + 1 ) {
-					return jdbcJavaType.wrap( string.charAt( start ), options );
-				}
-			default:
-				if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
-					final Object[] subValues = aggregateJdbcType.extractJdbcValues(
-							CharSequenceHelper.subSequence(
-									string,
-									start,
-									end
-							),
-							options
-					);
-					if ( returnEmbeddable ) {
-						final StructAttributeValues subAttributeValues = StructHelper.getAttributeValues(
-								aggregateJdbcType.getEmbeddableMappingType(),
-								subValues,
-								options
-						);
-						return instantiate( aggregateJdbcType.getEmbeddableMappingType(), subAttributeValues ) ;
-					}
-					return subValues;
-				}
-
-				return jdbcJavaType.fromEncodedString( string, start, end );
-		}
-	}
-
-	private static String unescape(String string, int start, int end) {
-		final StringBuilder sb = new StringBuilder( end - start );
-		for ( int i = start; i < end; i++ ) {
-			final char c = string.charAt( i );
-			if ( c == '\\' ) {
-				i++;
-				final char cNext = string.charAt( i );
-				switch ( cNext ) {
-					case '\\':
-					case '"':
-					case '/':
-						sb.append( cNext );
-						break;
-					case 'b':
-						sb.append( '\b' );
-						break;
-					case 'f':
-						sb.append( '\f' );
-						break;
-					case 'n':
-						sb.append( '\n' );
-						break;
-					case 'r':
-						sb.append( '\r' );
-						break;
-					case 't':
-						sb.append( '\t' );
-						break;
-					case 'u':
-						sb.append( (char) Integer.parseInt( string, i + 1, i + 5, 16 ) );
-						i += 4;
-						break;
-				}
-				continue;
-			}
-			sb.append( c );
-		}
-		return sb.toString();
+		assert endArrayFound:"Invalid end of array";
+		return javaType.wrap( arrayList, options );
 	}
 
-	enum State {
-		KEY_START( "\"\\s" ),
-		KEY_QUOTE( "" ),
-		KEY_END( ":\\s" ),
-		VALUE_START( "\"\\s" ),
-		VALUE_QUOTE( "" ),
-		VALUE_END( ",}\\s" );
 
-		final String expectedChars;
-
-		State(String expectedChars) {
-			this.expectedChars = expectedChars;
-		}
-
-		String expectedChars() {
-			return expectedChars;
-		}
-	}
 
 	public static class JsonAppender extends OutputStream implements SqlAppender {
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
index 015280b910b3..ebc6c3b1b3e0 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
@@ -23,6 +23,7 @@
  * Specialized type mapping for {@code JSON} and the JSON SQL data type.
  *
  * @author Christian Beikov
+ * @author Emmanuel Jannetti
  */
 public class JsonJdbcType implements AggregateJdbcType {
 	/**
@@ -75,7 +76,7 @@ protected <X> X fromString(String string, JavaType<X> javaType, WrapperOptions o
 			return null;
 		}
 		if ( embeddableMappingType != null ) {
-			return JsonHelper.fromString(
+			return (X) JsonHelper.deserialize(
 					embeddableMappingType,
 					string,
 					javaType.getJavaTypeClass() != Object[].class,
@@ -88,23 +89,30 @@ protected <X> X fromString(String string, JavaType<X> javaType, WrapperOptions o
 	@Override
 	public Object createJdbcValue(Object domainValue, WrapperOptions options) throws SQLException {
 		assert embeddableMappingType != null;
-		return JsonHelper.toString( embeddableMappingType, domainValue, options );
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		try {
+			JsonHelper.serialize( embeddableMappingType ,domainValue,options, writer );
+			return writer.toString();
+		}
+		catch (IOException e) {
+			throw new SQLException( e );
+		}
 	}
 
 	@Override
 	public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) throws SQLException {
 		assert embeddableMappingType != null;
-		return JsonHelper.fromString( embeddableMappingType, (String) rawJdbcValue, false, options );
+		return JsonHelper.deserialize( embeddableMappingType, (String) rawJdbcValue, false, options );
 	}
 
 	protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) {
 		if ( embeddableMappingType != null ) {
-			// used to be JsonHelper.toString( embeddableMappingType, value, options );
 			try {
 				StringBuilder sb = new StringBuilder();
 				StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
 				JsonHelper.serialize( embeddableMappingType, value, options, writer);
-				return sb.toString();
+				return writer.toString();
 			}
 			catch (IOException e) {
 				throw new RuntimeException("Failed to serialize JSON mapping", e );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java
new file mode 100644
index 000000000000..693e36d64967
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java
@@ -0,0 +1,87 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+public class JsonDocumentItem {
+	public static final JsonDocumentItem OBJECT_START_ITEM = new JsonDocumentItem(JsonDocumentItemType.OBJECT_START,
+			Boolean.TRUE );
+	public static final JsonDocumentItem OBJECT_END_ITEM = new JsonDocumentItem(JsonDocumentItemType.OBJECT_END,
+			Boolean.TRUE );
+	public static final JsonDocumentItem ARRAY_START_ITEM = new JsonDocumentItem(JsonDocumentItemType.ARRAY_START,
+			Boolean.TRUE );
+	public static final JsonDocumentItem ARRAY_END_ITEM = new JsonDocumentItem(JsonDocumentItemType.ARRAY_END,
+			Boolean.TRUE );
+	public static final JsonDocumentItem KEY_NAME_ITEM = new JsonDocumentItem(JsonDocumentItemType.VALUE_KEY,
+			Boolean.TRUE );
+
+	public static final JsonDocumentItem TRUE_VALUE_ITEM = new JsonDocumentItem(JsonDocumentItemType.BOOLEAN_VALUE,Boolean.TRUE);
+	public static final JsonDocumentItem FALSE_VALUE_ITEM = new JsonDocumentItem(JsonDocumentItemType.BOOLEAN_VALUE,Boolean.FALSE);
+	public static final JsonDocumentItem NULL_VALUE_ITEM = new JsonDocumentItem(JsonDocumentItemType.NULL_VALUE,null);
+
+
+
+	private JsonDocumentItemType type;
+	private Object value;
+
+
+	public Object getValue() {
+		return value;
+	}
+
+	public JsonDocumentItem(JsonDocumentItemType type, Object value) {
+		this(type);
+		this.value = value;
+	}
+	public JsonDocumentItem(JsonDocumentItemType type) {
+		this.type = type;
+		this.value = null;
+	}
+
+	public JsonDocumentItemType getType() {
+		return type;
+	}
+
+	/**
+	 * Json item types
+	 */
+	public enum JsonDocumentItemType {
+		/**
+		 * Start of a Json Object '{'
+		 */
+		OBJECT_START,
+		/**
+		 * end  of a Json Object '}'
+		 */
+		OBJECT_END,
+		/**
+		 * Start of a Json array '['
+		 */
+		ARRAY_START,
+		/**
+		 * End of a Json array ']'
+		 */
+		ARRAY_END,
+		/**
+		 * key of Json attribute
+		 */
+		VALUE_KEY,
+		/**
+		 * boolean value within Json object or array
+		 */
+		BOOLEAN_VALUE,
+		/**
+		 * null value within Json object or array
+		 */
+		NULL_VALUE,
+		/**
+		 * numeric value within Json object or array
+		 */
+		NUMERIC_VALUE,
+		/**
+		 * String (quoted) value within Json object or array
+		 */
+		VALUE
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
new file mode 100644
index 000000000000..e8eb6ab92399
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
@@ -0,0 +1,148 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.JavaType;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Iterator;
+
+/**
+ * JSON document reader.
+ * Reads a JSON document (i.e., String or OSON bytes) and produce Json item type event.
+ * Calling #next() will return one of a JsonDocumentItem.JsonDocumentItemType.
+ * The sequence of return types follows JSON specification.
+ * <p>
+ * When {@link JsonDocumentItemType.VALUE_KEY} is returned #getObjectKeyName() should be called to get the key name.
+ * <p>
+ * When {@link JsonDocumentItemType.VALUE}, {@link JsonDocumentItemType.BOOLEAN_VALUE}, {@link JsonDocumentItemType.NULL_VALUE} or {@link JsonDocumentItemType.NUMERIC_VALUE} is returned one of the getxxxValue() should be called to get the value.
+ * <p>
+ *  example :
+ *  <pre>
+ *	{
+ * 	  "key1": "value1",
+ * 	  "key2": ["x","y","z"],
+ * 	  "key3": {
+ *             "key4" : ["a"],
+ *             "key5" : {}
+ *            },
+ *    "key6":12,
+ *    "key7":null
+ *  }
+ *  </pre>
+ *  This Json object could be read as follows
+ *  <pre>
+ *      while (reader.hasNext()) {}
+ *         JsonDocumentItemType type = reader.next();
+ *         switch(type) {
+ *         	   case VALUE_KEY:
+ *         	       String keyName = reader.getObjectKeyName();
+ *         	       break;
+ *         	   case VALUE:
+ *         	       String value = reader.getStringValue()
+ *         	       break
+ *         	    //...
+ *         }
+ *      }
+ *  </pre>
+ *  This Json object above would trigger this sequence of events
+ *  <pre>
+ *    JsonDocumentItemType.OBJECT_START
+ *    JsonDocumentItemType.VALUE_KEY      // "key1"
+ *    JsonDocumentItemType.VALUE          // "value1"
+ *    JsonDocumentItemType.VALUE_KEY      // "key2"
+ *    JsonDocumentItemType.ARRAY_START
+ *    JsonDocumentItemType.VALUE          // "x"
+ *    JsonDocumentItemType.VALUE          // "y"
+ *    JsonDocumentItemType.VALUE          // "z"
+ *    JsonDocumentItemType.ARRAY_END
+ *    JsonDocumentItemType.VALUE_KEY      // "key3"
+ *    JsonDocumentItemType.OBJECT_START
+ *    JsonDocumentItemType.VALUE_KEY      // "key4"
+ *    JsonDocumentItemType.ARRAY_START
+ *    JsonDocumentItemType.VALUE          // "a"
+ *    JsonDocumentItemType.ARRAY_END
+ *    JsonDocumentItemType.VALUE_KEY       // "key5"
+ *    JsonDocumentItemType.OBJECT_START
+ *    JsonDocumentItemType.OBJECT_END
+ *    JsonDocumentItemType.VALUE_KEY       // "key6"
+ *    JsonDocumentItemType.NUMERIC_VALUE
+ *    JsonDocumentItemType.VALUE_KEY       // "key7"
+ *    JsonDocumentItemType.NULL_VALUE
+ *    JsonDocumentItemType.OBJECT_END
+ *    JsonDocumentItemType.OBJECT_END
+ *  </pre>
+ *
+ * @author Emmanuel Jannetti
+ */
+public interface JsonDocumentReader extends Iterator<JsonDocumentItem.JsonDocumentItemType> {
+	default void forEachRemaining() {
+		throw new UnsupportedOperationException("forEachRemaining");
+	}
+
+	/**
+	 * Gets the key name once JsonDocumentItemType.VALUE_KEY has been received
+	 * @return the name
+	 */
+	String getObjectKeyName();
+	/**
+	 * Gets value as String
+	 * @return the value.
+	 */
+	String getStringValue();
+	/**
+	 * Gets value as BigDecimal
+	 * @return the value.
+	 */
+	BigDecimal getBigDecimalValue();
+	/**
+	 * Gets value as BigInteger
+	 * @return the value.
+	 */
+	BigInteger getBigIntegerValue();
+	/**
+	 * Gets value as double
+	 * @return the value.
+	 */
+	double getDoubleValue();
+	/**
+	 * Gets value as float
+	 * @return the value.
+	 */
+	float getFloatValue();
+	/**
+	 * Gets value as long
+	 * @return the value.
+	 */
+	long getLongValue();
+	/**
+	 * Gets value as int
+	 * @return the value.
+	 */
+	int getIntegerValue();
+	/**
+	 * Gets value as short
+	 * @return the value.
+	 */
+	short getShortValue();
+	/**
+	 * Gets value as byte
+	 * @return the value.
+	 */
+	byte getByteValue();
+	/**
+	 * Gets value as boolean
+	 * @return the value.
+	 */
+	boolean getBooleanValue();
+
+	/**
+	 * Gets value as JavaType
+	 * @return the value.
+	 */
+	<T> T getValue(JavaType<T> javaType, WrapperOptions options);
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
new file mode 100644
index 000000000000..3dcbed17f1b8
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
@@ -0,0 +1,33 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import oracle.sql.json.OracleJsonParser;
+
+/**
+ * Factory class to get proper <code>JsonDocumentReader</code>.
+ *
+ * @author Emmanuel Jannetti
+ */
+public class JsonDocumentReaderFactory {
+	/**
+	 * Gets a <code>JsonDocumentReader</code> appropriate to a given source.
+	 * Source can be a <code>String</code> or a <code>OracleJsonParser</code> instance
+	 * @param jsonSource the document source
+	 * @return the reader
+	 */
+	public static JsonDocumentReader getJsonDocumentReader(Object jsonSource) {
+		assert jsonSource != null : "jsonSource is null";
+
+		if (jsonSource instanceof String) {
+			return new StringJsonDocumentReader( (String)jsonSource );
+		}
+		if (jsonSource instanceof OracleJsonParser ) {
+			return new OsonDocumentReader( (OracleJsonParser)jsonSource );
+		}
+
+		throw new IllegalArgumentException("Unsupported type of JSON source " + jsonSource.getClass());
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
index 2a0f8df1a687..5d92084ab01a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
@@ -21,56 +21,66 @@
 public interface JsonDocumentWriter {
 	/**
 	 * Starts a new JSON Objects.
+	 * @return this instance
 	 */
-	void startObject();
+	JsonDocumentWriter startObject();
 
 	/**
 	 * Ends a new JSON Objects
+	 * @return this instance
 	 */
-	void endObject();
+	JsonDocumentWriter endObject();
 
 	/**
 	 * Starts a new JSON array.
+	 * @return this instance
 	 * @throws IOException an I/O error roccured while starting the object.
 	 */
-	void startArray();
+	JsonDocumentWriter startArray();
 
 	/**
 	 * Ends a new JSON array.
+	 * @return this instance
 	 * @throws IOException an I/O error roccured while starting the object.
 	 */
-	void endArray();
+	JsonDocumentWriter endArray();
 
 	/**
 	 * Adds a new JSON element name.
 	 * @param key the element name.
-	 * @throws IOException an I/O error roccured while starting the object.
+	 * @return this instance
+	 * @throws IllegalArgumentException key name does not follow JSON specification.
+	 * @throws IOException an I/O error occurred while starting the object.
 	 */
-	void objectKey(String key);
+	JsonDocumentWriter objectKey(String key);
 
 	/**
 	 * Adds a new JSON element null value.
+	 * @return this instance
 	 * @throws IOException an I/O error roccured while starting the object.
 	 */
-	void nullValue();
+	JsonDocumentWriter nullValue();
 
 	/**
 	 * Adds a new JSON element boolean value.
+	 * @return this instance
 	 * @param value the element boolean name.
 	 */
-	void booleanValue(boolean value);
+	JsonDocumentWriter booleanValue(boolean value);
 
 	/**
 	 * Adds a new JSON element string value.
+	 * @return this instance
 	 * @param value the element string name.
 	 */
-	void stringValue(String value);
+	JsonDocumentWriter stringValue(String value);
 
 	/**
 	 * Adds a new JSON element Number value.
+	 * @return this instance
 	 * @param value the element Number name.
 	 */
-	void numberValue(Number value);
+	JsonDocumentWriter numberValue(Number value);
 
 	/**
 	 * Adds a JSON value to the document
@@ -78,8 +88,9 @@ public interface JsonDocumentWriter {
 	 * @param javaType the Java type of the value
 	 * @param jdbcType the JDBC type for the value to be serialized
 	 * @param options the wrapping options
+	 * @return this instance
 	 */
-	void serializeJsonValue(Object value,
+	JsonDocumentWriter serializeJsonValue(Object value,
 							JavaType<Object> javaType,
 							JdbcType jdbcType,
 							WrapperOptions options);
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
new file mode 100644
index 000000000000..d1c85b0bc500
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+
+import java.sql.SQLException;
+
+/**
+ * Adapter for JSON value on given JDBC types.
+ */
+public interface JsonValueJDBCTypeAdapter {
+	/**
+	 * Gets an Object out of a JSON document reader according to a given types.
+	 * @param jdbcJavaType the desired JavaType for the return Object.
+	 * @param jdbcType the desired JdbcType for the return Object.
+	 * @param source the JSON document reader from which to get the value to be translated.
+	 * @param options the wrapping option
+	 * @return the translated value.
+	 * @throws SQLException if translation failed.
+	 */
+	Object fromValue(
+			JavaType<?> jdbcJavaType,
+			JdbcType jdbcType,
+			JsonDocumentReader source,
+			WrapperOptions options) throws SQLException;
+
+	/**
+	 * Gets an Object out of a JSON document reader according to a given types.
+	 * This method is called when the current available value in the reader is a numeric one.
+	 * @param jdbcJavaType the desired JavaType for the return Object.
+	 * @param jdbcType the desired JdbcType for the return Object.
+	 * @param source the JSON document reader from which to get the value to be translated.
+	 * @param options the wrapping option
+	 * @return the translated value.
+	 * @throws SQLException if translation failed.
+	 */
+	Object fromNumericValue(JavaType<?> jdbcJavaType,
+							JdbcType jdbcType,
+							JsonDocumentReader source,
+							WrapperOptions options) throws SQLException;
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java
new file mode 100644
index 000000000000..d1336a84cd7c
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java
@@ -0,0 +1,31 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+/**
+ * Factory class to get proper <code>JsonValueJDBCTypeAdapter</code>.
+ *
+ * @author Emmanuel Jannetti
+ */
+public class JsonValueJDBCTypeAdapterFactory {
+	/**
+	 * Gets a type adapter for a given reader
+	 * @param reader the JSON document reader from which the adapter gets its value from.
+	 * @param returnEmbeddable
+	 * @return the adapter
+	 */
+	public static JsonValueJDBCTypeAdapter getAdapter(JsonDocumentReader reader , boolean returnEmbeddable) {
+		assert reader != null : "reader is null";
+
+		if (reader instanceof StringJsonDocumentReader) {
+			return new StringJsonValueJDBCTypeAdapter( returnEmbeddable );
+		}
+		if (reader instanceof OsonDocumentReader ) {
+			return new OsonValueJDBCTypeAdapter( );
+		}
+
+		throw new IllegalArgumentException("Unsupported type of document reader " + reader.getClass());
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
new file mode 100644
index 000000000000..36bea03a26cf
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
@@ -0,0 +1,215 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import oracle.sql.json.OracleJsonParser;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.BooleanJavaType;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.NoSuchElementException;
+
+/**
+ * OSON-based implementation of <code>JsonDocumentReader</code>
+ */
+public class OsonDocumentReader implements JsonDocumentReader {
+
+	final private OracleJsonParser parser;
+	private String currentKeyName;
+	private Object currentValue;
+	private boolean currentValueIsAString; // avoid later introspection
+	/**
+	 * Creates a new <code>OsonDocumentReader</code>  on top of a <code>OracleJsonParser</code>
+	 * @param parser the parser
+	 */
+	public OsonDocumentReader(OracleJsonParser parser) {
+		this.parser = parser;
+	}
+
+	@Override
+	public boolean hasNext() {
+		return this.parser.hasNext();
+	}
+
+	@Override
+	public JsonDocumentItem.JsonDocumentItemType next() {
+		if (!this.parser.hasNext())
+			throw new NoSuchElementException("No more item in JSON document");
+		OracleJsonParser.Event evt = this.parser.next();
+		currentKeyName = null;
+		currentValue = null;
+		currentValueIsAString = false;
+		switch (evt) {
+			case OracleJsonParser.Event.START_OBJECT:
+				return JsonDocumentItem.JsonDocumentItemType.OBJECT_START;
+			case OracleJsonParser.Event.END_OBJECT:
+				return JsonDocumentItem.JsonDocumentItemType.OBJECT_END;
+			case OracleJsonParser.Event.START_ARRAY:
+				return JsonDocumentItem.JsonDocumentItemType.ARRAY_START;
+			case OracleJsonParser.Event.END_ARRAY:
+				return JsonDocumentItem.JsonDocumentItemType.ARRAY_END;
+			case OracleJsonParser.Event.KEY_NAME:
+				currentKeyName = this.parser.getString();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE_KEY;
+			case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
+				currentValue = this.parser.getOffsetDateTime();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_DATE:
+			case OracleJsonParser.Event.VALUE_TIMESTAMP:
+				currentValue = this.parser.getLocalDateTime();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_INTERVALDS:
+				currentValue = this.parser.getDuration();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_INTERVALYM:
+				currentValue = this.parser.getPeriod();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_STRING:
+				currentValue = this.parser.getString();
+				currentValueIsAString = true;
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_TRUE:
+				currentValue = Boolean.TRUE;
+				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+			case OracleJsonParser.Event.VALUE_FALSE:
+				currentValue = Boolean.FALSE;
+				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+			case OracleJsonParser.Event.VALUE_NULL:
+				currentValue = null;
+				return JsonDocumentItem.JsonDocumentItemType.NULL_VALUE;
+			case OracleJsonParser.Event.VALUE_DECIMAL:
+				currentValue = this.parser.getBigDecimal();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_DOUBLE:
+				currentValue = this.parser.getDouble();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_FLOAT:
+				currentValue = this.parser.getFloat();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			case OracleJsonParser.Event.VALUE_BINARY:
+				currentValue = this.parser.getBytes();
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			default :
+				assert false:"Unknown OSON event";
+		}
+		return null;
+	}
+
+	@Override
+	public String getObjectKeyName() {
+		if (currentKeyName == null)
+			throw new IllegalStateException("no object key available");
+		return currentKeyName;
+	}
+
+	@Override
+	public String getStringValue() {
+		return (String)currentValue;
+	}
+
+	@Override
+	public BigDecimal getBigDecimalValue() {
+		return (BigDecimal)currentValue;
+	}
+
+	@Override
+	public BigInteger getBigIntegerValue() {
+		return ((BigDecimal)currentValue).toBigInteger();
+	}
+
+	@Override
+	public double getDoubleValue() {
+		if (currentValueIsAString) return Double.parseDouble( (String)currentValue );
+		return ((Double)currentValue).doubleValue();
+	}
+
+	@Override
+	public float getFloatValue() {
+		if (currentValueIsAString) return Float.parseFloat( (String)currentValue );
+		return ((Float)currentValue).floatValue();
+	}
+
+	@Override
+	public long getLongValue() {
+		if (currentValueIsAString) return Long.parseLong( (String)currentValue );
+		return ((BigDecimal)currentValue).longValue();
+	}
+
+	@Override
+	public int getIntegerValue() {
+		if (currentValueIsAString) return Integer.parseInt( (String)currentValue );
+		return ((BigDecimal)currentValue).intValue();
+	}
+
+	@Override
+	public short getShortValue() {
+		if (currentValueIsAString) return Short.parseShort( (String)currentValue );
+		return  ((BigDecimal)currentValue).shortValue();
+	}
+
+	@Override
+	public byte getByteValue() {
+		if (currentValueIsAString) return Byte.parseByte( (String)currentValue );
+		return ((Byte)currentValue).byteValue();
+	}
+
+	@Override
+	public boolean getBooleanValue() {
+		if (currentValueIsAString) return BooleanJavaType.INSTANCE.fromEncodedString((String)currentValue);
+		return ((Boolean)currentValue).booleanValue();
+	}
+
+
+
+	@Override
+	public <T> T getValue(JavaType<T> javaType, WrapperOptions options) {
+		if ( currentValueIsAString ) {
+			if (javaType.equals(PrimitiveByteArrayJavaType.INSTANCE)) {
+				// be sure that we have only allowed characters.
+				// that may happen for string representation of UUID (i.e 53886a8a-7082-4879-b430-25cb94415be8) for instance
+				return javaType.fromEncodedString(  (((String) currentValue).replaceAll( "-","" )) );
+			}
+			return javaType.fromEncodedString( (String) currentValue );
+		}
+
+		Object theOneToBeUsed =  currentValue;
+		// handle special cases for Date things
+		if ( currentValue.getClass() == LocalDateTime.class ) {
+			if ( java.sql.Date.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
+				theOneToBeUsed = Date.valueOf( ((LocalDateTime)currentValue).toLocalDate() );
+			}
+			else if ( java.time.LocalDate.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
+				theOneToBeUsed = ((LocalDateTime)currentValue).toLocalDate();
+			}
+			else if ( java.time.LocalTime.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
+				theOneToBeUsed = ((LocalDateTime)currentValue).toLocalTime();
+			}
+			else if ( java.sql.Time.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
+				theOneToBeUsed = Time.valueOf( ((LocalDateTime)currentValue).toLocalTime() );
+			}
+			else if ( java.sql.Timestamp.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
+				theOneToBeUsed = Timestamp.valueOf( ((LocalDateTime)currentValue) );
+			}
+			else if ( java.time.LocalTime.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
+				theOneToBeUsed = ((LocalDateTime)currentValue).toLocalTime();
+			}
+			else if ( java.util.Date.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
+				// better way?
+				theOneToBeUsed = java.util.Date.from( ((LocalDateTime)currentValue).atZone( ZoneId.of( "UTC" ) ).toInstant() );
+			}
+		}
+
+		return javaType.wrap( theOneToBeUsed ,options );
+
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index 7a4588750664..e3be47512647 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -51,61 +51,71 @@ public OsonDocumentWriter(OracleJsonGenerator generator) {
 
 
 	@Override
-	public void startObject() {
+	public JsonDocumentWriter startObject() {
 		this.generator.writeStartObject();
+		return this;
 	}
 
 
 	@Override
-	public void endObject() {
+	public JsonDocumentWriter endObject() {
 		this.generator.writeEnd();
+		return this;
 	}
 
 
 	@Override
-	public void startArray() {
+	public JsonDocumentWriter startArray() {
 		generator.writeStartArray();
+		return this;
 	}
 
 
 	@Override
-	public void endArray() {
+	public JsonDocumentWriter endArray() {
 		generator.writeEnd();
+		return this;
 	}
 
 
 	@Override
-	public void objectKey(String key) {
+	public JsonDocumentWriter objectKey(String key) {
 		this.generator.writeKey( key );
+		return this;
 	}
 
 
 	@Override
-	public void nullValue() {
+	public JsonDocumentWriter nullValue() {
 		this.generator.writeNull();
+		return this;
 	}
 
 
 	@Override
-	public void booleanValue(boolean value) {
+	public JsonDocumentWriter booleanValue(boolean value) {
 		this.generator.write(value);
+		return this;
 	}
 
 
 	@Override
-	public void stringValue(String value) {
+	public JsonDocumentWriter stringValue(String value) {
 		this.generator.write(value);
+		return this;
 	}
 
 
 	@Override
-	public void numberValue(Number value) {
+	public JsonDocumentWriter numberValue(Number value) {
 		this.generator.write((BigDecimal) value );
+		return this;
 	}
 
 	@Override
-	public void serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
+	public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
 		serializeValue(value, javaType, jdbcType, options);
+		return this;
 	}
 
 	/**
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
new file mode 100644
index 000000000000..5d8a38831fd0
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.EnumJavaType;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.JdbcDateJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
+import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
+import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+
+import java.sql.SQLException;
+
+
+/**
+ * JDBC type adapter for OSON-based JSON document reader.
+ */
+public class OsonValueJDBCTypeAdapter implements JsonValueJDBCTypeAdapter {
+	@Override
+	public Object fromValue(JavaType<?> jdbcJavaType, JdbcType jdbcType, JsonDocumentReader source, WrapperOptions options)
+			throws SQLException {
+		Object valueToBeWrapped = null;
+		switch ( jdbcType.getDefaultSqlTypeCode() ) {
+			case SqlTypes.BINARY:
+			case SqlTypes.VARBINARY:
+			case SqlTypes.LONGVARBINARY:
+			case SqlTypes.LONG32VARBINARY:
+			case SqlTypes.UUID:
+				valueToBeWrapped = source.getValue( PrimitiveByteArrayJavaType.INSTANCE, options );
+				break;
+			case SqlTypes.DATE:
+				valueToBeWrapped = source.getValue( JdbcDateJavaType.INSTANCE , options);
+				break;
+			case SqlTypes.TIME:
+			case SqlTypes.TIME_WITH_TIMEZONE:
+			case SqlTypes.TIME_UTC:
+				valueToBeWrapped = source.getValue( JdbcTimeJavaType.INSTANCE , options);
+				break;
+			case SqlTypes.TIMESTAMP:
+				valueToBeWrapped = source.getValue( JdbcTimestampJavaType.INSTANCE , options);
+				break;
+			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
+			case SqlTypes.TIMESTAMP_UTC:
+				valueToBeWrapped = source.getValue( OffsetDateTimeJavaType.INSTANCE , options);
+				break;
+			case SqlTypes.TINYINT:
+			case SqlTypes.SMALLINT:
+			case SqlTypes.INTEGER:
+				if ( jdbcJavaType.getJavaTypeClass() == Boolean.class ) {
+					valueToBeWrapped = source.getIntegerValue();
+					break;
+				}
+				else if ( jdbcJavaType instanceof EnumJavaType<?> ) {
+					valueToBeWrapped = source.getIntegerValue();
+					break;
+				}
+			case SqlTypes.CHAR:
+			case SqlTypes.NCHAR:
+			case SqlTypes.VARCHAR:
+			case SqlTypes.NVARCHAR:
+				if ( jdbcJavaType.getJavaTypeClass() == Boolean.class ) {
+					valueToBeWrapped = source.getBooleanValue();
+					break;
+				}
+		}
+		if (valueToBeWrapped == null) {
+			valueToBeWrapped = source.getValue( jdbcJavaType , options);
+		}
+		return jdbcJavaType.wrap(valueToBeWrapped, options);
+	}
+
+	@Override
+	public Object fromNumericValue(JavaType<?> jdbcJavaType, JdbcType jdbcType, JsonDocumentReader source, WrapperOptions options)
+			throws SQLException {
+		return fromValue( jdbcJavaType, jdbcType, source, options );
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
new file mode 100644
index 000000000000..fe9059644220
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import org.hibernate.internal.util.collections.StandardStack;
+
+/**
+ * base class for JSON document String reader
+ * @author Emmanuel Jannetti
+ */
+public abstract class StringJsonDocument {
+	/**
+	 * Processing states. This can be (nested)Object or Arrays.
+	 * When processing objects, values are stored as [,]"key":"value"[,]. we add separator when adding new key
+	 * When processing arrays, values are stored as [,]"value"[,]. we add separator when adding new value
+	 */
+	enum PROCESSING_STATE {
+		NONE,
+		STARTING_OBJECT, // object started but no value added
+		OBJECT_KEY_NAME, // We are processing an object key name
+		OBJECT, // object started, and we've started adding key/value pairs
+		ENDING_OBJECT, // we are ending an object
+		STARTING_ARRAY,  // array started but no value added
+		ENDING_ARRAY,  // we are ending an array
+		ARRAY // we are piling array values
+	}
+	// Stack of current processing states
+	protected StandardStack<PROCESSING_STATE> processingStates = new StandardStack<>();
+
+
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java
new file mode 100644
index 000000000000..d017529f8439
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+/**
+ * Enum class for JSON object markers.
+ */
+public enum StringJsonDocumentMarker {
+	ARRAY_END(']'),
+	ARRAY_START('['),
+	OBJECT_END('}'),
+	OBJECT_START('{'),
+	SEPARATOR(','),
+	QUOTE('"'),
+	KEY_VALUE_SEPARATOR(':'),
+	OTHER();
+
+	private final char val;
+	StringJsonDocumentMarker(char val) {
+		this.val = val;
+	}
+	StringJsonDocumentMarker() {
+		this.val = 0;
+	}
+	public char getMarkerCharacter() {
+		return this.val;
+	}
+
+	public static StringJsonDocumentMarker markerOf(char ch) {
+		switch (ch) {
+			case ']':
+				return ARRAY_END;
+			case '[':
+				return ARRAY_START;
+			case '}':
+				return OBJECT_END;
+			case '{':
+				return OBJECT_START;
+			case ',':
+				return SEPARATOR;
+			case '"':
+				return QUOTE;
+			case ':':
+				return KEY_VALUE_SEPARATOR;
+			default:
+				return OTHER;
+		}
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
new file mode 100644
index 000000000000..c12407960f8a
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -0,0 +1,505 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.JavaType;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.CharBuffer;
+import java.util.NoSuchElementException;
+
+/**
+ * Implementation of <code>JsonDocumentReader</code> for String representation of JSON objects.
+ */
+public class StringJsonDocumentReader extends StringJsonDocument implements  JsonDocumentReader {
+
+	private final CharBuffer json;
+	private final CharBuffer jsonValueWindow;
+
+	/**
+	 * Creates a new <code>StringJsonDocumentReader</code>
+	 * @param json the JSON String. of the object to be parsed.
+	 */
+	public StringJsonDocumentReader(String json) {
+		if (json == null) {
+			throw new IllegalArgumentException( "json cannot be null" );
+		}
+		this.json = CharBuffer.wrap( json.toCharArray() ).asReadOnlyBuffer();
+		this.jsonValueWindow = this.json.slice();
+	}
+
+	@Override
+	public boolean hasNext() {
+		// enough for now.
+		return this.json.hasRemaining();
+	}
+
+	private void skipWhiteSpace() {
+		for (int i =  this.json.position(); i < this.json.limit(); i++ ) {
+			if (!Character.isWhitespace( this.json.get(i))) {
+				this.json.position(i);
+				return;
+			}
+		}
+	}
+
+	private void resetValueWindow() {
+		this.jsonValueWindow.position(0);
+		this.jsonValueWindow.limit( 0);
+	}
+
+	/**
+	 * Moves the state machine according to the current state and the given marker
+	 *
+	 * @param marker the marker we just read
+	 */
+	private void moveStateMachine(StringJsonDocumentMarker marker) {
+		StringJsonDocument.PROCESSING_STATE currentState = this.processingStates.getCurrent();
+		switch (marker) {
+			case OBJECT_START:
+				if (currentState == PROCESSING_STATE.STARTING_ARRAY ) {
+					// move the state machine to ARRAY as we are adding something to it
+					this.processingStates.push(PROCESSING_STATE.ARRAY);
+				}
+				this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
+				break;
+			case OBJECT_END:
+				assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT ||
+					this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_OBJECT;
+				if (this.processingStates.pop() == PROCESSING_STATE.OBJECT) {
+					assert this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_OBJECT;
+					this.processingStates.pop();
+				}
+				break;
+			case ARRAY_START:
+				this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
+				break;
+			case ARRAY_END:
+				assert this.processingStates.getCurrent() == PROCESSING_STATE.ARRAY ||
+					this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_ARRAY;
+				if (this.processingStates.pop() == PROCESSING_STATE.ARRAY) {
+					assert this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_ARRAY;
+					this.processingStates.pop();
+				}
+				break;
+			case SEPARATOR:
+				// While processing an object, following SEPARATOR that will a key
+				if (currentState == PROCESSING_STATE.OBJECT) {
+					this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
+				}
+				break;
+			case KEY_VALUE_SEPARATOR:
+				// that's the start of an attribute value
+				assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT_KEY_NAME;
+				// flush the OBJECT_KEY_NAME
+				this.processingStates.pop();
+				assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT;
+				break;
+			case QUOTE:
+				switch ( currentState ) {
+					case PROCESSING_STATE.STARTING_ARRAY:
+						this.processingStates.push( PROCESSING_STATE.ARRAY );
+						break;
+					case PROCESSING_STATE.STARTING_OBJECT:
+						this.processingStates.push( PROCESSING_STATE.OBJECT );
+						this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
+						break;
+				}
+				break;
+			case OTHER:
+				if (currentState == PROCESSING_STATE.STARTING_ARRAY) {
+					this.processingStates.push( PROCESSING_STATE.ARRAY );
+				}
+				break;
+		}
+	}
+
+	/**
+	 * Returns the next item.
+	 * @return the item
+	 * @throws NoSuchElementException no more item available
+	 * @throws IllegalStateException not a well-formed JSON string.
+	 */
+	@Override
+	public JsonDocumentItem.JsonDocumentItemType next() {
+
+		if ( !hasNext()) throw new NoSuchElementException("no more elements");
+
+		while (hasNext()) {
+			skipWhiteSpace();
+			StringJsonDocumentMarker marker = StringJsonDocumentMarker.markerOf( this.json.get() );
+			moveStateMachine( marker );
+			switch ( marker) {
+				case OBJECT_START:
+					//this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
+					resetValueWindow();
+					return JsonDocumentItem.JsonDocumentItemType.OBJECT_START;
+				case OBJECT_END:
+					resetValueWindow();
+					//this.processingStates.pop(); // closing an object or a nested one.
+					return JsonDocumentItem.JsonDocumentItemType.OBJECT_END;
+				case ARRAY_START:
+					resetValueWindow();
+					//this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
+					return JsonDocumentItem.JsonDocumentItemType.ARRAY_START;
+				case ARRAY_END:
+					resetValueWindow();
+					//this.processingStates.pop();
+					return JsonDocumentItem.JsonDocumentItemType.ARRAY_END;
+				case QUOTE:  // that's the start of an attribute key or a quoted value
+					// put back the quote
+					moveBufferPosition(-1);
+					consumeQuottedString();
+					// That's a quote:
+					//   - if we are at the beginning of an array that's a quoted value
+					//   - if we are in the middle of an array, that's a quoted value
+					//   - if we are at the beginning of an object that's a quoted key
+					//   - if we are in the middle of an object :
+					//        - if we just hit ':' that's a quoted value
+					//        - if we just hit ',' that's a quoted key
+					switch ( this.processingStates.getCurrent() ) {
+						case PROCESSING_STATE.STARTING_ARRAY:
+							//this.processingStates.push( PROCESSING_STATE.ARRAY );
+							return JsonDocumentItem.JsonDocumentItemType.VALUE;
+						case PROCESSING_STATE.ARRAY:
+							return JsonDocumentItem.JsonDocumentItemType.VALUE;
+						case PROCESSING_STATE.STARTING_OBJECT:
+							//this.processingStates.push( PROCESSING_STATE.OBJECT );
+							//this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
+							return JsonDocumentItem.JsonDocumentItemType.VALUE_KEY;
+						case PROCESSING_STATE.OBJECT: // we are processing object attribute value elements
+							return JsonDocumentItem.JsonDocumentItemType.VALUE;
+						case PROCESSING_STATE.OBJECT_KEY_NAME: // we are processing object elements key
+							return JsonDocumentItem.JsonDocumentItemType.VALUE_KEY;
+						default:
+							throw new IllegalStateException( "unexpected quote read in current processing state " +
+															this.processingStates.getCurrent() );
+					}
+				case KEY_VALUE_SEPARATOR:  // that's the start of an attribute value
+					//assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT_KEY_NAME;
+					// flush the OBJECT_KEY_NAME
+					//this.processingStates.pop();
+					break;
+				case SEPARATOR:
+					// unless we are processing an array, following SEPARATOR that will a key
+//					if (this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT) {
+//						this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
+//					}
+					break;
+				case OTHER:
+					// here we are in front of a boolean, a null or a numeric value.
+					// if none of these cases we going to raise IllegalStateException
+					// put back what we've read
+					moveBufferPosition(-1);
+					final int valueSize = consumeNonStringValue();
+					if (valueSize == -1) {
+						throw new IllegalStateException( "Unrecognized marker: " + StringJsonDocumentMarker.markerOf(
+								json.get( this.json.position() )));
+					}
+					switch ( this.processingStates.getCurrent() ) {
+						case PROCESSING_STATE.ARRAY:
+						case PROCESSING_STATE.OBJECT:
+							return getUnquotedValueType(this.jsonValueWindow);
+						default:
+							throw new IllegalStateException( "unexpected read ["+
+															this.jsonValueWindow.toString()+
+															"] in current processing state " +
+															this.processingStates.getCurrent() );
+					}
+			}
+		}
+		// no way we get here.
+		return null;
+	}
+
+	/**
+	 * Gets the type of unquoted value.
+	 * We assume that the String value follows JSON specification. I.e unquoted value that starts with 't' can't be anything else
+	 * than <code>true</code>
+	 * @param jsonValueWindow the value
+	 * @return the type of the value
+	 */
+	private JsonDocumentItem.JsonDocumentItemType getUnquotedValueType(CharBuffer jsonValueWindow) {
+		final int size = jsonValueWindow.remaining();
+		switch(jsonValueWindow.charAt( 0 )) {
+			case 't': {
+				//true
+				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+			}
+			case 'f': {
+				//false
+				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+			}
+			case 'n' : {
+					// null
+					return JsonDocumentItem.JsonDocumentItemType.NULL_VALUE;
+				}
+			case '-':
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9': {
+				return JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE;
+			}
+			default :
+				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+			}
+	}
+
+	private void moveBufferPosition(int shift) {
+		this.json.position(this.json.position() + shift);
+	}
+
+	/**
+	 * Moves the current position to a given character.
+	 * @param character the character we should stop at.
+	 * @throws IllegalStateException if we encounter an unexpected character other than white spaces before the desired one.
+	 */
+
+	private void moveTo(char character) throws IllegalStateException {
+		this.json.mark();
+		while ( this.json.hasRemaining()) {
+			char c = this.json.get();
+			if ( c == character) {
+				this.json.reset();
+				return;
+			}
+			if (!Character.isWhitespace(c)) {
+				// we did find an unexpected character
+				// let the exception raise
+				this.json.reset();
+				break;
+			}
+		}
+		throw new IllegalStateException("Can't find character: " + character);
+	}
+
+	private int locateCharacter(char character, char escape) {
+		assert character != escape;
+		this.json.mark();
+		int found = -1;
+		boolean escapeIsOn = false;
+		while ( this.json.hasRemaining()) {
+			final char c = this.json.get();
+			if (c == escape) {
+				escapeIsOn = true;
+			}
+			else {
+				if ( c == character ) {
+					if (escapeIsOn) {
+						escapeIsOn = false;
+					}
+					else {
+						found = this.json.position() - 1;
+						break;
+					}
+				}
+			}
+		}
+		this.json.reset();
+		return found;
+	}
+
+	/**
+	 * Consume a non-quotted value
+	 * @return the length of this value. can be 0, -1 in case of error
+	 */
+	private int consumeNonStringValue() {
+		int newViewLimit = 0;
+		boolean allGood = false;
+		for (int i =  this.json.position(); i < this.json.limit(); i++ ) {
+			char c = this.json.get(i);
+			if ((StringJsonDocumentMarker.markerOf( c ) != StringJsonDocumentMarker.OTHER) ||
+				Character.isWhitespace( c )) {
+				// hit a JSON marker or a space.
+				allGood = true;
+				// give that character back to the buffer
+				newViewLimit = i;
+				break;
+			}
+		}
+
+		if (allGood) {
+			this.jsonValueWindow.limit(newViewLimit);
+			this.jsonValueWindow.position( this.json.position() );
+			this.json.position(newViewLimit);
+		}
+		return allGood?(this.jsonValueWindow.remaining()):-1;
+	}
+	private void consumeQuottedString() {
+
+		// be sure we are at a meaningful place
+		// key name are unquoted
+		moveTo( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
+
+		// skip the quote we are positioned on.
+		this.json.get();
+
+		//locate ending quote
+		int endingQuote = locateCharacter( StringJsonDocumentMarker.QUOTE.getMarkerCharacter(), '\\');
+		if (endingQuote == -1) {
+			throw new IllegalStateException("Can't find ending quote of key name");
+		}
+
+		this.jsonValueWindow.limit( endingQuote );
+		this.jsonValueWindow.position(this.json.position());
+		this.json.position( endingQuote + 1);
+
+	}
+
+	private void ensureValueState() throws IllegalStateException {
+		if ((this.processingStates.getCurrent() !=  PROCESSING_STATE.OBJECT ) &&
+			this.processingStates.getCurrent() !=  PROCESSING_STATE.ARRAY)  {
+			throw new IllegalStateException( "unexpected processing state: " + this.processingStates.getCurrent() );
+		}
+	}
+	private void ensureAvailableValue() throws IllegalStateException {
+		if (this.jsonValueWindow.limit() == 0 ) {
+			throw new IllegalStateException( "No available value");
+		}
+	}
+
+	@Override
+	public String getObjectKeyName() {
+		if (this.processingStates.getCurrent() !=  PROCESSING_STATE.OBJECT_KEY_NAME ) {
+			throw new IllegalStateException( "unexpected processing state: " + this.processingStates.getCurrent() );
+		}
+		ensureAvailableValue();
+		return this.jsonValueWindow.toString();
+	}
+	@Override
+	public String getStringValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		if (hasEscape(this.jsonValueWindow)) {
+			return unescape(this.jsonValueWindow);
+		}
+		return this.jsonValueWindow.toString();
+	}
+
+
+	@Override
+	public BigDecimal getBigDecimalValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return BigDecimal.valueOf( Long.valueOf(this.jsonValueWindow.toString()) );
+	}
+
+	@Override
+	public BigInteger getBigIntegerValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return BigInteger.valueOf( Long.valueOf(this.jsonValueWindow.toString()) );
+	}
+
+	@Override
+	public double getDoubleValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return Double.valueOf(this.jsonValueWindow.toString()).doubleValue();
+	}
+
+	@Override
+	public float getFloatValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return Float.valueOf(this.jsonValueWindow.toString()).floatValue();
+	}
+
+	@Override
+	public long getLongValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return Long.valueOf(this.jsonValueWindow.toString()).longValue();
+	}
+
+	@Override
+	public int getIntegerValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return Integer.valueOf(this.jsonValueWindow.toString()).intValue();
+	}
+
+	@Override
+	public short getShortValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return Short.valueOf(this.jsonValueWindow.toString()).shortValue();
+	}
+
+	@Override
+	public byte getByteValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return Byte.valueOf(this.jsonValueWindow.toString()).byteValue();
+	}
+
+	@Override
+	public boolean getBooleanValue() {
+		ensureValueState();
+		ensureAvailableValue();
+		return Boolean.parseBoolean( this.jsonValueWindow.toString() );
+	}
+
+	@Override
+	public <T> T getValue(JavaType<T> javaType, WrapperOptions options) {
+		return javaType.fromEncodedString( this.jsonValueWindow.toString() );
+	}
+
+	private boolean hasEscape(CharBuffer jsonValueWindow) {
+		for (int i = 0;i<jsonValueWindow.remaining();i++) {
+			if (jsonValueWindow.charAt( i ) == '\\') return true;
+		}
+		return false;
+	}
+	private String unescape(CharBuffer string) {
+		final StringBuilder sb = new StringBuilder( string.remaining() );
+		for ( int i = 0; i < string.length(); i++ ) {
+			final char c = string.charAt( i );
+			if ( c == '\\' ) {
+				i++;
+				final char cNext = string.charAt( i );
+				switch ( cNext ) {
+					case '\\':
+					case '"':
+					case '/':
+						sb.append( cNext );
+						break;
+					case 'b':
+						sb.append( '\b' );
+						break;
+					case 'f':
+						sb.append( '\f' );
+						break;
+					case 'n':
+						sb.append( '\n' );
+						break;
+					case 'r':
+						sb.append( '\r' );
+						break;
+					case 't':
+						sb.append( '\t' );
+						break;
+					case 'u':
+						sb.append( (char) Integer.parseInt( string, i + 1, i + 5, 16 ) );
+						i += 4;
+						break;
+				}
+				continue;
+			}
+			sb.append( c );
+		}
+		return sb.toString();
+	}
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
index 07c15071a438..0bcbc44a71c3 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -5,7 +5,6 @@
 package org.hibernate.type.format;
 
 import org.hibernate.dialect.JsonHelper;
-import org.hibernate.internal.util.collections.StandardStack;
 import org.hibernate.type.SqlTypes;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.BooleanJavaType;
@@ -20,36 +19,17 @@
 
 
 /**
- * Implementation of <code>JsonDocumentWriter</code> for String based OSON document.
+ * Implementation of <code>JsonDocumentWriter</code> for String-based OSON document.
  * This implementation will receive a {@link JsonHelper.JsonAppender } to a serialze JSON object to it
  * @author Emmanuel Jannetti
  */
-public class StringJsonDocumentWriter implements JsonDocumentWriter{
+public class StringJsonDocumentWriter extends StringJsonDocument implements JsonDocumentWriter {
+
 
-	private static final char ARRAY_END_MARKER = ']';
-	private static final char ARRAY_START_MARKER = '[';
-	private static final char OBJECT_END_MARKER = '}';
-	private static final char OBJECT_START_MARKER = '{';
-	private static final char SEPARATOR_MARKER = ',';
-	private static final char TOKEN_QUOTE = '"';
 
 	private JsonHelper.JsonAppender appender;
 
-	/**
-	 * Processing states. This can be (nested)Object or Arrays.
-	 * When processing objects, values are stored as [,]"key":"value"[,]. we add separator when adding new key
-	 * When processing arrays, values are stored as [,]"value"[,]. we add separator when adding new value
-	 */
-	private enum PROCESSING_STATE {
-		NONE,
-		STARTING_OBJECT, // object started but no value added
-		OBJECT, // object started, and we've started adding key/value pairs
-		ENDING_OBJECT, // we are ending an object
-		STARTING_ARRAY,  // array started but no value added
-		ENDING_ARRAY,  // we are ending an array
-		ARRAY // we are piling array values
-	}
-	private StandardStack<PROCESSING_STATE> processingStates = new StandardStack<>();
+
 
 	/**
 	 * Creates a new StringJsonDocumentWriter.
@@ -64,7 +44,7 @@ public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
 	 * Callback to be called when the start of an JSON object is encountered.
 	 */
 	@Override
-	public void startObject() {
+	public JsonDocumentWriter startObject() {
 		// Note: startArray and startObject must not call moveProcessingStateMachine()
 		if (this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_ARRAY) {
 			// are we building an array of objects?
@@ -76,54 +56,63 @@ else if (this.processingStates.getCurrent() == PROCESSING_STATE.ARRAY) {
 			// That means that we ae building an array of object ([{},...])
 			// JSON object hee are treat as array item.
 			// -> add the marker first
-			this.appender.append(SEPARATOR_MARKER);
+			this.appender.append(StringJsonDocumentMarker.SEPARATOR.getMarkerCharacter());
 		}
-		this.appender.append( OBJECT_START_MARKER);
+		this.appender.append( StringJsonDocumentMarker.OBJECT_START.getMarkerCharacter());
 		this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
+		return this;
 	}
 
 	/**
 	 * Callback to be called when the end of an JSON object is encountered.
 	 */
 	@Override
-	public void endObject() {
-		this.appender.append( OBJECT_END_MARKER );
+	public JsonDocumentWriter endObject() {
+		this.appender.append( StringJsonDocumentMarker.OBJECT_END.getMarkerCharacter() );
 		this.processingStates.push( PROCESSING_STATE.ENDING_OBJECT);
 		moveProcessingStateMachine();
+		return this;
 	}
 
 	/**
 	 * Callback to be called when the start of an array is encountered.
 	 */
 	@Override
-	public void startArray() {
+	public JsonDocumentWriter startArray() {
 		this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
 		// Note: startArray and startObject do not call moveProcessingStateMachine()
-		this.appender.append( ARRAY_START_MARKER );
-
+		this.appender.append( StringJsonDocumentMarker.ARRAY_START.getMarkerCharacter() );
+		return this;
 	}
 
 	/**
 	 * Callback to be called when the end of an array is encountered.
 	 */
 	@Override
-	public void endArray() {
-		this.appender.append( ARRAY_END_MARKER );
+	public JsonDocumentWriter endArray() {
+		this.appender.append( StringJsonDocumentMarker.ARRAY_END.getMarkerCharacter() );
 		this.processingStates.push( PROCESSING_STATE.ENDING_ARRAY);
 		moveProcessingStateMachine();
+		return this;
 	}
 
 
 	@Override
-	public void objectKey(String key) {
+	public JsonDocumentWriter objectKey(String key) {
+
+		if (key == null || key.length() == 0) {
+			throw new IllegalArgumentException( "key cannot be null or empty" );
+		}
+
 		if (this.processingStates.getCurrent().equals( PROCESSING_STATE.OBJECT )) {
 			// we have started an object, and we are adding an item key: we do add a separator.
-			this.appender.append( SEPARATOR_MARKER );
+			this.appender.append( StringJsonDocumentMarker.SEPARATOR.getMarkerCharacter() );
 		}
-		this.appender.append( TOKEN_QUOTE );
+		this.appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 		this.appender.append( key );
 		this.appender.append( "\":" );
 		moveProcessingStateMachine();
+		return this;
 	}
 
 	/**
@@ -135,7 +124,7 @@ public void objectKey(String key) {
 	private void addItemsSeparator() {
 		if (this.processingStates.getCurrent().equals( PROCESSING_STATE.ARRAY )) {
 			// We started to serialize an array and already added item to it:add a separator anytime.
-			this.appender.append( SEPARATOR_MARKER );
+			this.appender.append( StringJsonDocumentMarker.SEPARATOR.getMarkerCharacter() );
 		}
 	}
 
@@ -168,7 +157,7 @@ private void moveProcessingStateMachine() {
 				this.processingStates.push( PROCESSING_STATE.OBJECT );
 				break;
 			case STARTING_ARRAY:
-				//after starting an object, we start adding value to it
+				//after starting an array, we start adding value to it
 				this.processingStates.push( PROCESSING_STATE.ARRAY );
 				break;
 			case ENDING_ARRAY:
@@ -199,46 +188,53 @@ private void moveProcessingStateMachine() {
 	}
 
 	@Override
-	public void nullValue() {
+	public JsonDocumentWriter nullValue() {
 		addItemsSeparator();
 		this.appender.append( "null" );
 		moveProcessingStateMachine();
+		return this;
 	}
 
 	@Override
-	public void booleanValue(boolean value) {
+	public JsonDocumentWriter booleanValue(boolean value) {
 		addItemsSeparator();
 		BooleanJavaType.INSTANCE.appendEncodedString( this.appender, value);
 		moveProcessingStateMachine();
+		return this;
 	}
 
 	@Override
-	public void stringValue(String value) {
+	public JsonDocumentWriter stringValue(String value) {
 		addItemsSeparator();
 
-		appender.append( TOKEN_QUOTE);
+		appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter());
 		appender.startEscaping();
 		appender.append( value );
 		appender.endEscaping();
-		appender.append(TOKEN_QUOTE );
+		appender.append(StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 
 		moveProcessingStateMachine();
-
+		return this;
 	}
 
 	@Override
-	public void numberValue(Number value) {
+	public JsonDocumentWriter numberValue(Number value) {
+		if (value == null ) {
+			throw new IllegalArgumentException( "value cannot be null" );
+		}
 		addItemsSeparator();
 		this.appender.append( value.toString() );
 		moveProcessingStateMachine();
+		return this;
 	}
 
 
 	@Override
-	public void serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
+	public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
 		addItemsSeparator();
 		convertedBasicValueToString(value, options,this.appender,javaType,jdbcType);
 		moveProcessingStateMachine();
+		return this;
 	}
 
 	/**
@@ -284,9 +280,9 @@ private  void convertedBasicValueToString(
 			case SqlTypes.NVARCHAR:
 				if ( value instanceof Boolean ) {
 					// BooleanJavaType has this as an implicit conversion
-					appender.append( TOKEN_QUOTE );
+					appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 					appender.append( (Boolean) value ? 'Y' : 'N' );
-					appender.append( TOKEN_QUOTE);
+					appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter());
 					break;
 				}
 			case SqlTypes.LONGVARCHAR:
@@ -300,55 +296,55 @@ private  void convertedBasicValueToString(
 			case SqlTypes.ENUM:
 			case SqlTypes.NAMED_ENUM:
 				// These literals can contain the '"' character, so we need to escape it
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				appender.startEscaping();
 				javaType.appendEncodedString( appender, value );
 				appender.endEscaping();
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.DATE:
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				JdbcDateJavaType.INSTANCE.appendEncodedString(
 						appender,
 						javaType.unwrap( value, java.sql.Date.class, options )
 				);
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.TIME:
 			case SqlTypes.TIME_WITH_TIMEZONE:
 			case SqlTypes.TIME_UTC:
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				JdbcTimeJavaType.INSTANCE.appendEncodedString(
 						appender,
 						javaType.unwrap( value, java.sql.Time.class, options )
 				);
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.TIMESTAMP:
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				JdbcTimestampJavaType.INSTANCE.appendEncodedString(
 						appender,
 						javaType.unwrap( value, java.sql.Timestamp.class, options )
 				);
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
 			case SqlTypes.TIMESTAMP_UTC:
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				DateTimeFormatter.ISO_OFFSET_DATE_TIME.formatTo(
 						javaType.unwrap( value, OffsetDateTime.class, options ),
 						appender
 				);
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.DECIMAL:
 			case SqlTypes.NUMERIC:
 			case SqlTypes.DURATION:
 			case SqlTypes.UUID:
 				// These types need to be serialized as JSON string, but don't have a need for escaping
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				javaType.appendEncodedString( appender, value );
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.BINARY:
 			case SqlTypes.VARBINARY:
@@ -357,9 +353,9 @@ private  void convertedBasicValueToString(
 			case SqlTypes.BLOB:
 			case SqlTypes.MATERIALIZED_BLOB:
 				// These types need to be serialized as JSON string, and for efficiency uses appendString directly
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				appender.write( javaType.unwrap( value, byte[].class, options ) );
-				appender.append( TOKEN_QUOTE );
+				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.ARRAY:
 			case SqlTypes.JSON_ARRAY:
@@ -370,4 +366,8 @@ private  void convertedBasicValueToString(
 		}
 	}
 
+	@Override
+	public String toString() {
+		return appender.toString();
+	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
new file mode 100644
index 000000000000..7f6f614869fd
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
@@ -0,0 +1,149 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+import org.hibernate.dialect.StructAttributeValues;
+import org.hibernate.dialect.StructHelper;
+import org.hibernate.internal.util.CharSequenceHelper;
+import org.hibernate.metamodel.mapping.EmbeddableMappingType;
+import org.hibernate.type.SqlTypes;
+import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.EnumJavaType;
+import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.JdbcDateJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
+import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
+import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType;
+import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
+import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
+import org.hibernate.type.descriptor.jdbc.JdbcType;
+
+import java.sql.SQLException;
+
+import static org.hibernate.dialect.StructHelper.instantiate;
+
+/**
+ * JDBC type adapter for String-based JSON document reader.
+ */
+public class StringJsonValueJDBCTypeAdapter implements JsonValueJDBCTypeAdapter {
+
+	private boolean returnEmbeddable;
+	public StringJsonValueJDBCTypeAdapter(boolean returnEmbeddable) {
+		this.returnEmbeddable = returnEmbeddable;
+	}
+
+	@Override
+	public Object fromValue(JavaType<?> jdbcJavaType, JdbcType jdbcType, JsonDocumentReader source, WrapperOptions options)
+			throws SQLException {
+		return fromAnyValue (jdbcJavaType, jdbcType, source, options);
+	}
+
+	private Object fromAnyValue(JavaType<?> jdbcJavaType, JdbcType jdbcType, JsonDocumentReader source, WrapperOptions options)
+			throws SQLException {
+
+		String string = source.getStringValue();
+
+		switch ( jdbcType.getDefaultSqlTypeCode() ) {
+			case SqlTypes.BINARY:
+			case SqlTypes.VARBINARY:
+			case SqlTypes.LONGVARBINARY:
+			case SqlTypes.LONG32VARBINARY:
+				return jdbcJavaType.wrap(
+						PrimitiveByteArrayJavaType.INSTANCE.fromEncodedString(
+								string,
+								0, string.length()),
+						options
+				);
+			case SqlTypes.UUID:
+				return jdbcJavaType.wrap(
+						PrimitiveByteArrayJavaType.INSTANCE.fromString(
+								string.substring( 0, string.length() ).replace( "-", "" )
+						),
+						options
+				);
+			case SqlTypes.DATE:
+				return jdbcJavaType.wrap(
+						JdbcDateJavaType.INSTANCE.fromEncodedString(
+								string,
+								0,
+								string.length()
+						),
+						options
+				);
+			case SqlTypes.TIME:
+			case SqlTypes.TIME_WITH_TIMEZONE:
+			case SqlTypes.TIME_UTC:
+				return jdbcJavaType.wrap(
+						JdbcTimeJavaType.INSTANCE.fromEncodedString(
+								string,
+								0,
+								string.length()
+						),
+						options
+				);
+			case SqlTypes.TIMESTAMP:
+				return jdbcJavaType.wrap(
+						JdbcTimestampJavaType.INSTANCE.fromEncodedString(
+								string,
+								0,
+								string.length()
+						),
+						options
+				);
+			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
+			case SqlTypes.TIMESTAMP_UTC:
+				return jdbcJavaType.wrap(
+						OffsetDateTimeJavaType.INSTANCE.fromEncodedString(
+								string,
+								0,
+								string.length()),
+						options
+				);
+			case SqlTypes.TINYINT:
+			case SqlTypes.SMALLINT:
+			case SqlTypes.INTEGER:
+				if ( jdbcJavaType.getJavaTypeClass() == Boolean.class ) {
+					return jdbcJavaType.wrap( Integer.parseInt( string, 0, string.length(), 10 ), options );
+				}
+				else if ( jdbcJavaType instanceof EnumJavaType<?> ) {
+					return jdbcJavaType.wrap( Integer.parseInt( string, 0, string.length(), 10 ), options );
+				}
+			case SqlTypes.CHAR:
+			case SqlTypes.NCHAR:
+			case SqlTypes.VARCHAR:
+			case SqlTypes.NVARCHAR:
+				if ( jdbcJavaType.getJavaTypeClass() == Boolean.class && (string.length() == 1 ) ) {
+					return jdbcJavaType.wrap( string.charAt( 0 ), options );
+				}
+			default:
+				if ( jdbcType instanceof AggregateJdbcType aggregateJdbcType ) {
+					final Object[] subValues = aggregateJdbcType.extractJdbcValues(
+							CharSequenceHelper.subSequence(
+									string,
+									0,
+									string.length()),
+							options
+					);
+					if ( returnEmbeddable ) {
+						final StructAttributeValues subAttributeValues = StructHelper.getAttributeValues(
+								aggregateJdbcType.getEmbeddableMappingType(),
+								subValues,
+								options
+						);
+						final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
+						return instantiate( embeddableMappingType, subAttributeValues, options.getSessionFactory() ) ;
+					}
+					return subValues;
+				}
+
+				return jdbcJavaType.fromEncodedString(string);
+		}
+	}
+
+	@Override
+	public Object fromNumericValue(JavaType<?> jdbcJavaType, JdbcType jdbcType, JsonDocumentReader source, WrapperOptions options) throws SQLException  {
+		return fromAnyValue (jdbcJavaType, jdbcType, source, options);
+	}
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index ba936e9915a8..5e9f0f232afd 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -68,7 +68,6 @@ public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, Wrap
 
 	@Override
 	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-
 		try {
 			return objectMapper.readValue( ((CharSequence)source).toString(), objectMapper.constructType( javaType.getJavaType() ) );
 		}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
new file mode 100644
index 000000000000..11770525dc07
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
@@ -0,0 +1,384 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.util;
+
+import org.hibernate.type.format.JsonDocumentItem;
+import org.hibernate.type.format.StringJsonDocumentReader;
+import org.junit.jupiter.api.Test;
+
+import java.util.NoSuchElementException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * @author Emmanuel Jannetti
+ */
+public class StringJsonDocumentReaderTest {
+	@Test
+	public void testNullDocument() {
+		assertThrows( IllegalArgumentException.class, () -> {new StringJsonDocumentReader( null );} );
+	}
+	@Test
+	public void testEmptyDocument() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "" );
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+	}
+	@Test
+	public void testEmptyJsonObject() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{}" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+	}
+	@Test
+	public void testEmptyJsonArray() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "[]" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.ARRAY_START, reader.next());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.ARRAY_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+	}
+	@Test
+	public void testWrongNext() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{}" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+	@Test
+	public void testSimpleDocument() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"key1\" :\"value1\"    }" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals("key1", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE, reader.next());
+		assertEquals("value1", reader.getStringValue());
+
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+	@Test
+	public void testSimpleDoubleValueDocument() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"key1\":\"\",\"key2\" : \" x value2 x \" }" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "key1", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( "", reader.getStringValue());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "key2", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertTrue( reader.getStringValue().equals(" x value2 x "));
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+	@Test
+	public void testNonStringValueDocument() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"aNull\":null, \"aNumber\" : 12 , \"aBoolean\" : true}" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aNull", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NULL_VALUE,reader.next());
+		assertEquals( "null", reader.getStringValue());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aNumber", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( 12, reader.getIntegerValue());
+		assertEquals( "12", reader.getStringValue());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aBoolean", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE,reader.next());
+		assertEquals( true, reader.getBooleanValue());
+		assertEquals( "true", reader.getStringValue());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+
+	@Test
+	public void testNonAvailableValueDocument() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{}" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertThrows( IllegalStateException.class, () -> {reader.getStringValue();} );
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+
+	@Test
+	public void testBooleanValueDocument() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"aBoolean\" : true}" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aBoolean", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE,reader.next());
+		assertTrue( reader.getBooleanValue() );
+		assertEquals( "true",reader.getStringValue());
+		assertTrue(reader.getBooleanValue());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+
+	@Test
+	public void testNumericValueDocument() {
+		final StringJsonDocumentReader reader =
+				new StringJsonDocumentReader( "{ \"aInteger\" : 12, \"aDouble\" : 123.456 , \"aLong\" : 123456, \"aShort\" : 1}" );
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aInteger", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( (int)12 , reader.getIntegerValue());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aDouble", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( (double)123.456 ,reader.getDoubleValue()  );
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aLong", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( (long)123456 , reader.getLongValue()  );
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "aShort", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( (short)1, reader.getLongValue()  );
+
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+
+	@Test
+	public void testArrayValueDocument() {
+		final StringJsonDocumentReader reader =
+				new StringJsonDocumentReader( "{ \"anEmptyArray\" : [], \"anArray\" : [1,2,3] }" );
+		assertTrue(reader.hasNext());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "anEmptyArray", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+
+		assertEquals(  JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "anArray", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals(1,  reader.getIntegerValue());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals(2,  reader.getIntegerValue());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals(3,  reader.getIntegerValue());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+	@Test
+	public void testObjectArrayMultipleValueDocument() {
+		final StringJsonDocumentReader reader =
+				new StringJsonDocumentReader( "{ \"anArray\" : [1, null, \"2\" ,  {\"foo\":\"bar\"}  ]  \n"
+											+ "     }" );
+		assertTrue(reader.hasNext());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "anArray", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals("1",  reader.getStringValue());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NULL_VALUE,reader.next());
+		assertEquals("null",  reader.getStringValue());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals(2,  reader.getIntegerValue());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "foo", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals("bar",  reader.getStringValue());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+	@Test
+	public void testEscapeStringDocument() {
+		final StringJsonDocumentReader reader =
+				new StringJsonDocumentReader( "{ \"str1\" : \"abc\" , \"str2\" : \"\\\"abc\\\"\" , \"str3\" : \"a\\\"b\\\"c\" }" );
+		assertTrue(reader.hasNext());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "str1", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals("abc",  reader.getStringValue());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "str2", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals("\"abc\"",  reader.getStringValue());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "str3", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals("a\"b\"c",  reader.getStringValue());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+
+		assertFalse(reader.hasNext(), "Should not have  anymore element");
+		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
+	}
+
+	@Test
+	public void testNestedDocument() {
+		final StringJsonDocumentReader reader =
+				new StringJsonDocumentReader( """
+							{
+							"nested": {
+								"converted_gender": "M",
+								"theInteger": -1
+							},
+							"doubleNested": {
+								"theNested": {
+									"theLeaf": {
+										"stringField": "String \\"<abc>A&B</abc>\\""
+									}
+								}
+							},
+							"integerField": 10
+							}
+						""");
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "nested", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "converted_gender", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals("M",  reader.getStringValue());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "theInteger", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals(-1,  reader.getIntegerValue());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "doubleNested", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "theNested", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "theLeaf", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "stringField", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals("String \"<abc>A&B</abc>\"",  reader.getStringValue());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "integerField", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals("10",  reader.getStringValue());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+	}
+
+	@Test
+	public void testNestedArrayDocument() {
+		final StringJsonDocumentReader reader =
+				new StringJsonDocumentReader( """
+							{
+							"nested": [
+										{
+											"anArray": [1]
+										}
+									]
+							}
+						""");
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "nested", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( "anArray", reader.getObjectKeyName());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals(1L,  reader.getLongValue());
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_END,reader.next());
+
+		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+
+		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+	}
+
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
new file mode 100644
index 000000000000..1e7a3207fc99
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
@@ -0,0 +1,183 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.util;
+
+import org.hibernate.dialect.JsonHelper;
+import org.hibernate.type.format.StringJsonDocumentWriter;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+/**
+ * @author Emmanuel Jannetti
+ */
+public class StringJsonDocumentWriterTest {
+
+	private static void assertEqualsIgnoreSpace(String expected, String actual) {
+		assertEquals(expected.replaceAll("\\s", ""), actual.replaceAll("\\s", ""));
+	}
+
+	@Test
+	public void testEmptyDocument() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		writer.startObject();
+		writer.endObject();
+		assertEquals( "{}" , writer.toString());
+	}
+
+	@Test
+	public void testEmptyArray() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		writer.startArray();
+		writer.endArray();
+		assertEquals( "[]" , writer.toString() );
+	}
+	@Test
+	public void testArray() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		writer.startArray();
+		writer.numberValue( Integer.valueOf( 1 ) );
+		writer.numberValue( Integer.valueOf( 2 ) );
+		writer.numberValue( Integer.valueOf( 3 ) );
+		writer.endArray();
+		assertEquals( "[1,2,3]" , writer.toString() );
+	}
+
+	@Test
+	public void testMixedArrayDocument() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		writer.startArray();
+		writer.numberValue( Integer.valueOf( 1 ) );
+		writer.nullValue();
+		writer.booleanValue( false );
+		writer.stringValue( "foo" );
+		writer.endArray();
+		assertEqualsIgnoreSpace( """
+									[1,null,false,"foo"]
+							""" , writer.toString() );
+	}
+	@Test
+	public void testSimpleDocument() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		writer.startObject();
+		writer.objectKey( "key1" );
+		writer.stringValue( "value1" );
+		writer.endObject();
+
+		assertEqualsIgnoreSpace(
+		"""
+			{
+			"key1":"value1"
+			}
+			""", writer.toString());
+
+	}
+
+	@Test
+	public void testNonStringValueDocument() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		writer.startObject();
+		writer.objectKey( "aNull" );
+		writer.nullValue();
+		writer.objectKey( "aNumber" );
+		writer.numberValue( Integer.valueOf( 12 ) );
+		writer.objectKey( "aBoolean" );
+		writer.booleanValue( true );
+		writer.endObject();
+
+		assertEqualsIgnoreSpace( """
+						{
+						"aNull":null,
+						"aNumber" : 12 ,
+							"aBoolean" : true
+						}
+						""" , writer.toString() );
+
+	}
+
+
+	@Test
+	public void testArrayValueDocument() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
+		writer.startObject();
+		writer.objectKey( "anEmptyArray" );
+		writer.startArray();
+		writer.endArray();
+		writer.objectKey( "anArray" );
+		writer.startArray();
+		writer.numberValue( Integer.valueOf( 1 ) );
+		writer.numberValue( Integer.valueOf( 2 ) );
+		writer.numberValue( Integer.valueOf( 3 ) );
+		writer.endArray();
+		writer.endObject();
+
+		assertEqualsIgnoreSpace(  """
+				{
+				"anEmptyArray" : [],
+				"anArray" : [1,2,3]
+				}
+				""", writer.toString() );
+	}
+	@Test
+	public void testObjectArrayMultipleValueDocument() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
+		writer.startObject();
+		writer.objectKey( "anArray" ).startArray().numberValue( Integer.valueOf( 1 ) ).nullValue().stringValue( "2" ).startObject()
+		.objectKey( "foo" ).stringValue( "bar" ).endObject().endArray().endObject();
+
+		assertEqualsIgnoreSpace( """
+					{
+						"anArray" : [1, null, "2" , {\"foo\":\"bar\"}  ]
+					}
+					""" , sb.toString() );
+
+	}
+
+	@Test
+	public void testNestedDocument() {
+		StringBuilder sb = new StringBuilder();
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
+		writer.startObject().objectKey( "nested" ).startObject()
+				.objectKey( "converted_gender" ).stringValue( "M" ).objectKey( "theInteger" ).numberValue( Integer.valueOf( -1 ) ).endObject()
+				.objectKey( "doubleNested" ).startObject()
+				.objectKey( "theNested" ).startObject()
+				.objectKey( "theLeaf" )
+				.startObject().objectKey( "stringField" ).stringValue( "String \"<abc>A&B</abc>\"" ).endObject()
+				.endObject()
+				.endObject()
+				.objectKey( "integerField" ).numberValue( Integer.valueOf( 10 ) )
+				.endObject();
+
+		assertEqualsIgnoreSpace( """
+							{
+							"nested": {
+								"converted_gender": "M",
+								"theInteger": -1
+							},
+							"doubleNested": {
+								"theNested": {
+									"theLeaf": {
+										"stringField": "String \\"<abc>A&B</abc>\\""
+									}
+								}
+							},
+							"integerField": 10
+							}
+						""",writer.toString());
+
+
+	}
+
+
+}

From 77ade6a5af9cc3bc44fdab0e73ed470d6e7a90ba Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Wed, 29 Jan 2025 18:54:15 +0100
Subject: [PATCH 45/81] HHH-17404 : removed dead code

---
 .../dialect/OracleOsonJacksonJdbcType.java    |   9 -
 .../org/hibernate/dialect/OsonHelper.java     | 104 -------
 .../type/descriptor/jdbc/JsonHelper.java      |  10 +-
 .../type/format/JsonDocumentHandler.java      |  62 ----
 .../type/format/JsonDocumentItem.java         |  87 ------
 .../type/format/JsonDocumentItemType.java     |  47 +++
 .../type/format/JsonDocumentReader.java       |   2 +-
 .../ObjectArrayOsonDocumentHandler.java       | 279 ------------------
 .../type/format/OsonDocumentReader.java       |  36 +--
 .../type/format/StringJsonDocumentReader.java |  32 +-
 .../util/StringJsonDocumentReaderTest.java    | 210 ++++++-------
 11 files changed, 192 insertions(+), 686 deletions(-)
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java
 create mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 4413058bfb06..eabb8cf716c5 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -149,21 +149,12 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
 					// and build the array.(as opposed to let Jackson do it as we do not
 					// have a proper object definition at that stage).
 					OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( osonBytes );
-
-
-					//ObjectArrayOsonDocumentHandler handler = new ObjectArrayOsonDocumentHandler( getEmbeddableMappingType(),
-						//	options);
-					//OsonHelper.consumeOsonTokens(osonParser, osonParser.next(), handler);
-
-					//osonBytes.reset();
-					//OracleJsonParser osonParser2 = new OracleJsonFactory().createJsonBinaryParser( osonBytes);
 					Object[] objects =  JsonHelper.deserialize(
 							getEmbeddableMappingType(),
 							osonParser,
 							javaType.getJavaTypeClass() != Object[].class,
 							options
 					);
-					//Object[] objects2 = (Object[]) handler.getObjectArray();
 					return (X) objects;
 				}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java
deleted file mode 100644
index 09c57aa9c2c0..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OsonHelper.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.dialect;
-
-import oracle.sql.json.OracleJsonParser;
-import org.hibernate.Internal;
-import org.hibernate.type.format.JsonDocumentHandler;
-import org.hibernate.type.format.ObjectArrayOsonDocumentHandler;
-
-import java.io.IOException;
-
-/**
- * A Helper for handling OSON events
- */
-@Internal
-public class OsonHelper {
-
-	/**
-	 * Process OSON parser tokens.
-	 * This method consumes one by one event coming from an OSON parser and uses the given JsonDocumentHandler
-	 * to populate values into Object array
-	 * @param osonParser the OSON parser
-	 * @param currentEvent the current of the parser
-	 * @throws IOException error while reading from underlying parser
-	 */
-	public static void consumeOsonTokens(OracleJsonParser osonParser, OracleJsonParser.Event currentEvent, JsonDocumentHandler handler)
-			throws IOException {
-
-		OracleJsonParser.Event event = currentEvent;
-
-		while ( event != null ) {
-			switch ( event ) {
-				case OracleJsonParser.Event.KEY_NAME:
-					handler.onObjectKey( osonParser.getString() );
-					break;
-				case OracleJsonParser.Event.START_ARRAY:
-					handler.onStartArray();
-					break;
-				case OracleJsonParser.Event.END_ARRAY:
-					handler.onEndArray();
-					break;
-				case OracleJsonParser.Event.VALUE_DATE:
-				case OracleJsonParser.Event.VALUE_TIMESTAMP:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonDateValue(
-							osonParser.getLocalDateTime());
-					break;
-				case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getOffsetDateTime());
-					break;
-				case OracleJsonParser.Event.VALUE_INTERVALDS:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getDuration());
-					break;
-				case OracleJsonParser.Event.VALUE_INTERVALYM:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getPeriod());
-					break;
-				case OracleJsonParser.Event.VALUE_STRING:
-					handler.onStringValue( osonParser.getString() );
-					break;
-				case OracleJsonParser.Event.VALUE_TRUE:
-					handler.onBooleanValue( true );
-					break;
-				case OracleJsonParser.Event.VALUE_FALSE:
-					handler.onBooleanValue( false );
-					break;
-				case OracleJsonParser.Event.VALUE_NULL:
-					handler.onNullValue();
-					break;
-				case OracleJsonParser.Event.VALUE_DECIMAL:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getBigDecimal());
-					break;
-				case OracleJsonParser.Event.VALUE_DOUBLE:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getDouble());
-					break;
-				case OracleJsonParser.Event.VALUE_FLOAT:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonValue(
-							osonParser.getFloat());
-					break;
-				case OracleJsonParser.Event.VALUE_BINARY:
-					((ObjectArrayOsonDocumentHandler)handler).onOsonBinaryValue(
-							osonParser.getBytes());
-					break;
-				case OracleJsonParser.Event.START_OBJECT:
-					handler.onStartObject();
-					break;
-				case OracleJsonParser.Event.END_OBJECT:
-					handler.onEndObject();
-					break;
-				default:
-					throw new IOException( "Unknown OSON event " + event );
-
-			}
-			event = osonParser.hasNext() ? osonParser.next() : null;
-		}
-
-	}
-
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 14e06bdfe668..a98536091697 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -36,7 +36,7 @@
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
-import org.hibernate.type.format.JsonDocumentItem;
+import org.hibernate.type.format.JsonDocumentItemType;
 import org.hibernate.type.format.JsonDocumentReader;
 import org.hibernate.type.format.JsonDocumentReaderFactory;
 import org.hibernate.type.format.JsonDocumentWriter;
@@ -257,7 +257,7 @@ private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, Embedda
 		objectArrays.push( objectArrayResult );
 
 		while(reader.hasNext()) {
-			JsonDocumentItem.JsonDocumentItemType type = reader.next();
+			JsonDocumentItemType type = reader.next();
 			switch (type) {
 				case VALUE_KEY:
 					currentKeyName = reader.getObjectKeyName();
@@ -429,12 +429,12 @@ public static <X> X arrayFromString(
 		JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,false);
 
 		assert reader.hasNext():"Invalid array string";
-		assert reader.next() == JsonDocumentItem.JsonDocumentItemType.ARRAY_START:"Invalid start of array";
+		assert reader.next() == JsonDocumentItemType.ARRAY_START:"Invalid start of array";
 		boolean endArrayFound = false;
 		while(reader.hasNext()) {
-			JsonDocumentItem.JsonDocumentItemType type = reader.next();
+			JsonDocumentItemType type = reader.next();
 			switch ( type ) {
-				case JsonDocumentItem.JsonDocumentItemType.ARRAY_END:
+				case JsonDocumentItemType.ARRAY_END:
 					endArrayFound=true;
 					break;
 				case NULL_VALUE:
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
deleted file mode 100644
index 139e4b3683b8..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentHandler.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.type.format;
-
-
-/**
- * JSON document handler.
- * Used to parse JSON documents. Implementors of this will define
- * proper callback implementations.
- *
- * @author Emmanuel Jannetti
- */
-
-public interface JsonDocumentHandler {
-	/**
-	 * Callback to be called when the start of an JSON object is encountered.
-	 */
-	void onStartObject();
-
-	/**
-	 * Callback to be called when the end of an JSON object is encountered.
-	 */
-	void onEndObject();
-
-	/**
-	 * Callback to be called when the start of an array is encountered.
-	 */
-	void onStartArray();
-
-	/**
-	 * Callback to be called when the end of an array is encountered.
-	 */
-	void onEndArray();
-
-	/**
-	 * Callback to be called when the key of JSON attribute is encountered.
-	 */
-	void onObjectKey(String key);
-
-	/**
-	 * Callback to be called when null value is encountered.
-	 */
-	void onNullValue();
-
-	/**
-	 * Callback to be called when boolean value is encountered.
-	 */
-	void onBooleanValue(boolean value);
-
-	/**
-	 * Callback to be called when string value is encountered.
-	 */
-	void onStringValue(String value);
-
-	/**
-	 * Callback to be called when Number value is encountered.
-	 */
-	void onNumberValue(Number value);
-
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java
deleted file mode 100644
index 693e36d64967..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItem.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.type.format;
-
-public class JsonDocumentItem {
-	public static final JsonDocumentItem OBJECT_START_ITEM = new JsonDocumentItem(JsonDocumentItemType.OBJECT_START,
-			Boolean.TRUE );
-	public static final JsonDocumentItem OBJECT_END_ITEM = new JsonDocumentItem(JsonDocumentItemType.OBJECT_END,
-			Boolean.TRUE );
-	public static final JsonDocumentItem ARRAY_START_ITEM = new JsonDocumentItem(JsonDocumentItemType.ARRAY_START,
-			Boolean.TRUE );
-	public static final JsonDocumentItem ARRAY_END_ITEM = new JsonDocumentItem(JsonDocumentItemType.ARRAY_END,
-			Boolean.TRUE );
-	public static final JsonDocumentItem KEY_NAME_ITEM = new JsonDocumentItem(JsonDocumentItemType.VALUE_KEY,
-			Boolean.TRUE );
-
-	public static final JsonDocumentItem TRUE_VALUE_ITEM = new JsonDocumentItem(JsonDocumentItemType.BOOLEAN_VALUE,Boolean.TRUE);
-	public static final JsonDocumentItem FALSE_VALUE_ITEM = new JsonDocumentItem(JsonDocumentItemType.BOOLEAN_VALUE,Boolean.FALSE);
-	public static final JsonDocumentItem NULL_VALUE_ITEM = new JsonDocumentItem(JsonDocumentItemType.NULL_VALUE,null);
-
-
-
-	private JsonDocumentItemType type;
-	private Object value;
-
-
-	public Object getValue() {
-		return value;
-	}
-
-	public JsonDocumentItem(JsonDocumentItemType type, Object value) {
-		this(type);
-		this.value = value;
-	}
-	public JsonDocumentItem(JsonDocumentItemType type) {
-		this.type = type;
-		this.value = null;
-	}
-
-	public JsonDocumentItemType getType() {
-		return type;
-	}
-
-	/**
-	 * Json item types
-	 */
-	public enum JsonDocumentItemType {
-		/**
-		 * Start of a Json Object '{'
-		 */
-		OBJECT_START,
-		/**
-		 * end  of a Json Object '}'
-		 */
-		OBJECT_END,
-		/**
-		 * Start of a Json array '['
-		 */
-		ARRAY_START,
-		/**
-		 * End of a Json array ']'
-		 */
-		ARRAY_END,
-		/**
-		 * key of Json attribute
-		 */
-		VALUE_KEY,
-		/**
-		 * boolean value within Json object or array
-		 */
-		BOOLEAN_VALUE,
-		/**
-		 * null value within Json object or array
-		 */
-		NULL_VALUE,
-		/**
-		 * numeric value within Json object or array
-		 */
-		NUMERIC_VALUE,
-		/**
-		 * String (quoted) value within Json object or array
-		 */
-		VALUE
-	}
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java
new file mode 100644
index 000000000000..a2256446f95d
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java
@@ -0,0 +1,47 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.type.format;
+
+/**
+ * Json item types
+ */
+public enum JsonDocumentItemType {
+	/**
+	 * Start of a Json Object '{'
+	 */
+	OBJECT_START,
+	/**
+	 * end  of a Json Object '}'
+	 */
+	OBJECT_END,
+	/**
+	 * Start of a Json array '['
+	 */
+	ARRAY_START,
+	/**
+	 * End of a Json array ']'
+	 */
+	ARRAY_END,
+	/**
+	 * key of Json attribute
+	 */
+	VALUE_KEY,
+	/**
+	 * boolean value within Json object or array
+	 */
+	BOOLEAN_VALUE,
+	/**
+	 * null value within Json object or array
+	 */
+	NULL_VALUE,
+	/**
+	 * numeric value within Json object or array
+	 */
+	NUMERIC_VALUE,
+	/**
+	 * String (quoted) value within Json object or array
+	 */
+	VALUE
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
index e8eb6ab92399..f97b51e3f474 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
@@ -79,7 +79,7 @@
  *
  * @author Emmanuel Jannetti
  */
-public interface JsonDocumentReader extends Iterator<JsonDocumentItem.JsonDocumentItemType> {
+public interface JsonDocumentReader extends Iterator<JsonDocumentItemType> {
 	default void forEachRemaining() {
 		throw new UnsupportedOperationException("forEachRemaining");
 	}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java b/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
deleted file mode 100644
index 608467bcc43f..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/type/format/ObjectArrayOsonDocumentHandler.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.type.format;
-
-import org.hibernate.internal.util.collections.StandardStack;
-import org.hibernate.metamodel.mapping.EmbeddableMappingType;
-import org.hibernate.metamodel.mapping.SelectableMapping;
-import org.hibernate.type.BasicPluralType;
-import org.hibernate.type.descriptor.WrapperOptions;
-import org.hibernate.type.descriptor.java.UUIDJavaType;
-import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
-
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * Implementation of <code>JsonDocumentHandler</code> for OSON document.
- * This implementation will produce an Object Array based on
- * embeddable mapping
- * Once All JSON document is handle the mapped Object array can be retrieved using the
- * <code>getObjectArray()</code> method.
- *
- */
-public class ObjectArrayOsonDocumentHandler implements JsonDocumentHandler {
-
-	// final result of a mapped object array
-	private final Object [] objectArrayResult;
-	// current mapping to be used
-	SelectableMapping currentSelectableMapping = null;
-	String currentKeyName = null;
-	List<Object> subArrayObjectList = null;
-	BasicPluralType<?, ?> subArrayObjectTypes = null;
-
-	// mapping definitions are in a tree
-	// Each mapping definition may contain sub mappings (sub embeddable mapping)
-	// This stack is used to keep a pointer on the current mapping to be used to assign correct types.
-	// see onStartObject()/onEndObject() methods
-	StandardStack<EmbeddableMappingType> embeddableMappingTypes = new StandardStack<>();
-	// As for mapping definitions, when "sub embeddable" is encountered, the array
-	// that needs to be filled with Objects is the one we allocate in the final result array slot.
-	// We use a stack to keep track of array ref
-	StandardStack<Object[]> objectArrays = new StandardStack<>();
-
-
-	WrapperOptions wrapperOptions;
-
-	// index within objectArrayResult
-	int currentSelectableIndexInResultArray = -1;
-
-	public ObjectArrayOsonDocumentHandler(EmbeddableMappingType embeddableMappingType, WrapperOptions wrapperOptions) {
-		this.embeddableMappingTypes.push(embeddableMappingType);
-		this.wrapperOptions = wrapperOptions;
-		this.objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()+ ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
-		this.objectArrays.push( this.objectArrayResult );
-	}
-
-	/**
-	 * Gets the Object array built from document handling
-	 * @return the array of objects
-	 */
-	public Object [] getObjectArray() {
-		return this.objectArrayResult;
-	}
-
-	@Override
-	public void onStartObject() {
-		if (currentKeyName != null) {
-			// We are dealing with a sub-object, allocate space for it then,
-			// otherwise, we have nothing to do.
-			// Push the new (sub)mapping definition.
-			assert embeddableMappingTypes.getCurrent() != null;
-			this.currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
-			assert currentSelectableIndexInResultArray != -1: "Cannot get index of " + currentKeyName;
-
-			final SelectableMapping selectable = embeddableMappingTypes.getCurrent().getJdbcValueSelectable(
-					currentSelectableIndexInResultArray );
-			final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
-					.getJdbcType();
-			final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-			assert this.objectArrays.getCurrent() != null;
-			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
-					new Object[subMappingType.getJdbcValueCount()];
-			this.embeddableMappingTypes.push( subMappingType );
-			this.objectArrays.push( (Object[]) this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] );
-		}
-	}
-
-	@Override
-	public void onEndObject() {
-		// go back in the mapping definition tree
-		this.embeddableMappingTypes.pop();
-		this.objectArrays.pop();
-	}
-
-	@Override
-	public void onStartArray() {
-		assert (subArrayObjectList == null && subArrayObjectTypes == null) : "onStartArray called twice ?";
-
-		// initialize an array to gather values
-		subArrayObjectList = new ArrayList<>();
-		assert (currentSelectableMapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
-				: "Array event received for non plural type";
-		// initialize array's element type
-		subArrayObjectTypes = (BasicPluralType<?, ?>) currentSelectableMapping.getJdbcMapping();
-	}
-
-	@Override
-	public void onEndArray() {
-		assert (subArrayObjectList != null && subArrayObjectTypes != null) : "onEndArray called before onStartArray";
-		// flush array values
-		this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, wrapperOptions );
-		// reset until we encounter next array element
-		subArrayObjectList = null;
-		subArrayObjectTypes = null;
-	}
-
-	@Override
-	public void onObjectKey(String key) {
-		this.currentKeyName = key;
-
-		currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
-		if ( currentSelectableIndexInResultArray >= 0 ) {
-			// we may not have a selectable mapping for that key
-			currentSelectableMapping = embeddableMappingTypes.getCurrent().getJdbcValueSelectable( currentSelectableIndexInResultArray );
-		}
-		else {
-			throw new IllegalArgumentException(
-					String.format(
-							"Could not find selectable [%s] in embeddable type [%s] for JSON processing.",
-							currentKeyName,
-							embeddableMappingTypes.getCurrent().getMappedJavaType().getJavaTypeClass().getName()
-					)
-			);
-		}
-	}
-
-	@Override
-	public void onNullValue() {
-		if ( subArrayObjectList != null ) {
-			// dealing with arrays
-			subArrayObjectList.add( null );
-		}
-		else {
-			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = null;
-		}
-	}
-
-	@Override
-	public void onBooleanValue(boolean value) {
-		if ( subArrayObjectList != null ) {
-			// dealing with arrays
-			subArrayObjectList.add( value?Boolean.TRUE:Boolean.FALSE);
-		}
-		else {
-			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = value?Boolean.TRUE:Boolean.FALSE;
-		}
-	}
-
-	@Override
-	public void onStringValue(String value) {
-		if ( subArrayObjectList != null ) {
-			// dealing with arrays
-			subArrayObjectList.add(
-					subArrayObjectTypes.getElementType().getJdbcJavaType().fromEncodedString( value ,0,value.length()) );
-		}
-		else {
-			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
-					currentSelectableMapping.getJdbcMapping().getJdbcJavaType().fromEncodedString( value,0,value.length());
-		}
-	}
-
-	@Override
-	public void onNumberValue(Number value) {
-		onOsonValue(value);
-	}
-
-	/**
-	 * Callback for OSON values
-	 * @param value the OSON value
-	 * @param <T> the type of the value as returned by OracleJsonParser
-	 */
-	public <T> void onOsonValue(T value) {
-		if ( subArrayObjectList != null ) {
-			// dealing with arrays
-			subArrayObjectList.add( value );
-		}
-		else {
-			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
-					currentSelectableMapping.getJdbcMapping().convertToDomainValue(
-					currentSelectableMapping.getJdbcMapping().getJdbcJavaType()
-							.wrap( value, wrapperOptions ) );
-		}
-	}
-
-	/**
-	 * Callback for OSON binary value
-	 * @param bytes the OSON byters
-	 */
-	public void onOsonBinaryValue(byte[] bytes) {
-		Class underlyingType;
-		Object theOneToBeUsed;
-		if(subArrayObjectTypes!=null) {
-			underlyingType = subArrayObjectTypes.getElementType().getJavaType();
-		}
-		else {
-			underlyingType = (Class) currentSelectableMapping.getJdbcMapping().getJdbcJavaType().getJavaType();
-		}
-
-		if (java.util.UUID.class.isAssignableFrom( underlyingType ))  {
-			theOneToBeUsed = UUIDJavaType.INSTANCE.wrap( bytes, wrapperOptions );
-		}
-		else {
-			theOneToBeUsed = bytes;
-		}
-
-		if ( subArrayObjectList != null ) {
-			// dealing with arrays
-			subArrayObjectList.add( theOneToBeUsed );
-		}
-		else {
-			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = theOneToBeUsed;
-		}
-	}
-
-	/**
-	 * Callback for OracleJsonParser.Event.VALUE_DATE and OracleJsonParser.Event.VALUE_TIMESTAMP:
-	 * @param localDateTime the time
-	 */
-	public void onOsonDateValue(LocalDateTime localDateTime) {
-
-		Class underlyingType;
-		Object theOneToBeUsed = localDateTime;
-
-		if(subArrayObjectTypes!=null) {
-			underlyingType = subArrayObjectTypes.getElementType().getJavaType();
-		}
-		else {
-			underlyingType = (Class) currentSelectableMapping.getJdbcMapping().getJdbcJavaType().getJavaType();
-		}
-		if (java.sql.Date.class.isAssignableFrom( underlyingType )) {
-			theOneToBeUsed = Date.valueOf( localDateTime.toLocalDate());
-		}
-		else if (java.time.LocalDate.class.isAssignableFrom( underlyingType )) {
-			theOneToBeUsed = localDateTime.toLocalDate();
-		}
-		else if (java.time.LocalTime.class.isAssignableFrom( underlyingType )) {
-			theOneToBeUsed = localDateTime.toLocalTime();
-		}
-		else if (java.sql.Time.class.isAssignableFrom( underlyingType )) {
-			theOneToBeUsed = Time.valueOf( localDateTime.toLocalTime() );
-		}
-		else if (java.sql.Timestamp.class.isAssignableFrom( underlyingType )) {
-			theOneToBeUsed = Timestamp.valueOf( localDateTime );
-		}
-		else if(java.time.LocalTime.class.isAssignableFrom( underlyingType )) {
-			theOneToBeUsed = localDateTime.toLocalTime();
-		}
-		else if ( java.util.Date.class.isAssignableFrom( underlyingType ) ) {
-			// better way?
-			theOneToBeUsed = java.util.Date.from( localDateTime.atZone( ZoneId.of( "UTC" ) ).toInstant());
-		}
-
-		if ( subArrayObjectList != null ) {
-			// dealing with arrays
-			subArrayObjectList.add( theOneToBeUsed );
-		}
-		else {
-			this.objectArrays.getCurrent()[currentSelectableIndexInResultArray] = theOneToBeUsed;
-		}
-	}
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
index 36bea03a26cf..b14bdc7f3702 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
@@ -42,7 +42,7 @@ public boolean hasNext() {
 	}
 
 	@Override
-	public JsonDocumentItem.JsonDocumentItemType next() {
+	public JsonDocumentItemType next() {
 		if (!this.parser.hasNext())
 			throw new NoSuchElementException("No more item in JSON document");
 		OracleJsonParser.Event evt = this.parser.next();
@@ -51,54 +51,54 @@ public JsonDocumentItem.JsonDocumentItemType next() {
 		currentValueIsAString = false;
 		switch (evt) {
 			case OracleJsonParser.Event.START_OBJECT:
-				return JsonDocumentItem.JsonDocumentItemType.OBJECT_START;
+				return JsonDocumentItemType.OBJECT_START;
 			case OracleJsonParser.Event.END_OBJECT:
-				return JsonDocumentItem.JsonDocumentItemType.OBJECT_END;
+				return JsonDocumentItemType.OBJECT_END;
 			case OracleJsonParser.Event.START_ARRAY:
-				return JsonDocumentItem.JsonDocumentItemType.ARRAY_START;
+				return JsonDocumentItemType.ARRAY_START;
 			case OracleJsonParser.Event.END_ARRAY:
-				return JsonDocumentItem.JsonDocumentItemType.ARRAY_END;
+				return JsonDocumentItemType.ARRAY_END;
 			case OracleJsonParser.Event.KEY_NAME:
 				currentKeyName = this.parser.getString();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE_KEY;
+				return JsonDocumentItemType.VALUE_KEY;
 			case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
 				currentValue = this.parser.getOffsetDateTime();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_DATE:
 			case OracleJsonParser.Event.VALUE_TIMESTAMP:
 				currentValue = this.parser.getLocalDateTime();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_INTERVALDS:
 				currentValue = this.parser.getDuration();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_INTERVALYM:
 				currentValue = this.parser.getPeriod();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_STRING:
 				currentValue = this.parser.getString();
 				currentValueIsAString = true;
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_TRUE:
 				currentValue = Boolean.TRUE;
-				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+				return JsonDocumentItemType.BOOLEAN_VALUE;
 			case OracleJsonParser.Event.VALUE_FALSE:
 				currentValue = Boolean.FALSE;
-				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+				return JsonDocumentItemType.BOOLEAN_VALUE;
 			case OracleJsonParser.Event.VALUE_NULL:
 				currentValue = null;
-				return JsonDocumentItem.JsonDocumentItemType.NULL_VALUE;
+				return JsonDocumentItemType.NULL_VALUE;
 			case OracleJsonParser.Event.VALUE_DECIMAL:
 				currentValue = this.parser.getBigDecimal();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_DOUBLE:
 				currentValue = this.parser.getDouble();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_FLOAT:
 				currentValue = this.parser.getFloat();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_BINARY:
 				currentValue = this.parser.getBytes();
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			default :
 				assert false:"Unknown OSON event";
 		}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index c12407960f8a..68aa98df47d8 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -125,7 +125,7 @@ private void moveStateMachine(StringJsonDocumentMarker marker) {
 	 * @throws IllegalStateException not a well-formed JSON string.
 	 */
 	@Override
-	public JsonDocumentItem.JsonDocumentItemType next() {
+	public JsonDocumentItemType next() {
 
 		if ( !hasNext()) throw new NoSuchElementException("no more elements");
 
@@ -137,19 +137,19 @@ public JsonDocumentItem.JsonDocumentItemType next() {
 				case OBJECT_START:
 					//this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
 					resetValueWindow();
-					return JsonDocumentItem.JsonDocumentItemType.OBJECT_START;
+					return JsonDocumentItemType.OBJECT_START;
 				case OBJECT_END:
 					resetValueWindow();
 					//this.processingStates.pop(); // closing an object or a nested one.
-					return JsonDocumentItem.JsonDocumentItemType.OBJECT_END;
+					return JsonDocumentItemType.OBJECT_END;
 				case ARRAY_START:
 					resetValueWindow();
 					//this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
-					return JsonDocumentItem.JsonDocumentItemType.ARRAY_START;
+					return JsonDocumentItemType.ARRAY_START;
 				case ARRAY_END:
 					resetValueWindow();
 					//this.processingStates.pop();
-					return JsonDocumentItem.JsonDocumentItemType.ARRAY_END;
+					return JsonDocumentItemType.ARRAY_END;
 				case QUOTE:  // that's the start of an attribute key or a quoted value
 					// put back the quote
 					moveBufferPosition(-1);
@@ -164,17 +164,17 @@ public JsonDocumentItem.JsonDocumentItemType next() {
 					switch ( this.processingStates.getCurrent() ) {
 						case PROCESSING_STATE.STARTING_ARRAY:
 							//this.processingStates.push( PROCESSING_STATE.ARRAY );
-							return JsonDocumentItem.JsonDocumentItemType.VALUE;
+							return JsonDocumentItemType.VALUE;
 						case PROCESSING_STATE.ARRAY:
-							return JsonDocumentItem.JsonDocumentItemType.VALUE;
+							return JsonDocumentItemType.VALUE;
 						case PROCESSING_STATE.STARTING_OBJECT:
 							//this.processingStates.push( PROCESSING_STATE.OBJECT );
 							//this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
-							return JsonDocumentItem.JsonDocumentItemType.VALUE_KEY;
+							return JsonDocumentItemType.VALUE_KEY;
 						case PROCESSING_STATE.OBJECT: // we are processing object attribute value elements
-							return JsonDocumentItem.JsonDocumentItemType.VALUE;
+							return JsonDocumentItemType.VALUE;
 						case PROCESSING_STATE.OBJECT_KEY_NAME: // we are processing object elements key
-							return JsonDocumentItem.JsonDocumentItemType.VALUE_KEY;
+							return JsonDocumentItemType.VALUE_KEY;
 						default:
 							throw new IllegalStateException( "unexpected quote read in current processing state " +
 															this.processingStates.getCurrent() );
@@ -223,20 +223,20 @@ public JsonDocumentItem.JsonDocumentItemType next() {
 	 * @param jsonValueWindow the value
 	 * @return the type of the value
 	 */
-	private JsonDocumentItem.JsonDocumentItemType getUnquotedValueType(CharBuffer jsonValueWindow) {
+	private JsonDocumentItemType getUnquotedValueType(CharBuffer jsonValueWindow) {
 		final int size = jsonValueWindow.remaining();
 		switch(jsonValueWindow.charAt( 0 )) {
 			case 't': {
 				//true
-				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+				return JsonDocumentItemType.BOOLEAN_VALUE;
 			}
 			case 'f': {
 				//false
-				return JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE;
+				return JsonDocumentItemType.BOOLEAN_VALUE;
 			}
 			case 'n' : {
 					// null
-					return JsonDocumentItem.JsonDocumentItemType.NULL_VALUE;
+					return JsonDocumentItemType.NULL_VALUE;
 				}
 			case '-':
 			case '0':
@@ -249,10 +249,10 @@ private JsonDocumentItem.JsonDocumentItemType getUnquotedValueType(CharBuffer js
 			case '7':
 			case '8':
 			case '9': {
-				return JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE;
+				return JsonDocumentItemType.NUMERIC_VALUE;
 			}
 			default :
-				return JsonDocumentItem.JsonDocumentItemType.VALUE;
+				return JsonDocumentItemType.VALUE;
 			}
 	}
 
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
index 11770525dc07..a614b63efb29 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
@@ -4,7 +4,7 @@
  */
 package org.hibernate.orm.test.util;
 
-import org.hibernate.type.format.JsonDocumentItem;
+import org.hibernate.type.format.JsonDocumentItemType;
 import org.hibernate.type.format.StringJsonDocumentReader;
 import org.junit.jupiter.api.Test;
 
@@ -32,24 +32,24 @@ public void testEmptyDocument() {
 	public void testEmptyJsonObject() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{}" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 	}
 	@Test
 	public void testEmptyJsonArray() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "[]" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.ARRAY_START, reader.next());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.ARRAY_END, reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_START, reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 	}
 	@Test
 	public void testWrongNext() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{}" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -57,15 +57,15 @@ public void testWrongNext() {
 	public void testSimpleDocument() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"key1\" :\"value1\"    }" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals("key1", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE, reader.next());
+		assertEquals( JsonDocumentItemType.VALUE, reader.next());
 		assertEquals("value1", reader.getStringValue());
 
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -73,19 +73,19 @@ public void testSimpleDocument() {
 	public void testSimpleDoubleValueDocument() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"key1\":\"\",\"key2\" : \" x value2 x \" }" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "key1", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals( "", reader.getStringValue());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "key2", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertTrue( reader.getStringValue().equals(" x value2 x "));
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -93,27 +93,27 @@ public void testSimpleDoubleValueDocument() {
 	public void testNonStringValueDocument() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"aNull\":null, \"aNumber\" : 12 , \"aBoolean\" : true}" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aNull", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NULL_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NULL_VALUE,reader.next());
 		assertEquals( "null", reader.getStringValue());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aNumber", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals( 12, reader.getIntegerValue());
 		assertEquals( "12", reader.getStringValue());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aBoolean", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.BOOLEAN_VALUE,reader.next());
 		assertEquals( true, reader.getBooleanValue());
 		assertEquals( "true", reader.getStringValue());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -122,11 +122,11 @@ public void testNonStringValueDocument() {
 	public void testNonAvailableValueDocument() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{}" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
 		assertThrows( IllegalStateException.class, () -> {reader.getStringValue();} );
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -135,16 +135,16 @@ public void testNonAvailableValueDocument() {
 	public void testBooleanValueDocument() {
 		final StringJsonDocumentReader reader = new StringJsonDocumentReader( "{ \"aBoolean\" : true}" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aBoolean", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.BOOLEAN_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.BOOLEAN_VALUE,reader.next());
 		assertTrue( reader.getBooleanValue() );
 		assertEquals( "true",reader.getStringValue());
 		assertTrue(reader.getBooleanValue());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -154,31 +154,31 @@ public void testNumericValueDocument() {
 		final StringJsonDocumentReader reader =
 				new StringJsonDocumentReader( "{ \"aInteger\" : 12, \"aDouble\" : 123.456 , \"aLong\" : 123456, \"aShort\" : 1}" );
 		assertTrue(reader.hasNext(), "should have more element");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aInteger", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals( (int)12 , reader.getIntegerValue());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aDouble", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals( (double)123.456 ,reader.getDoubleValue()  );
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aLong", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals( (long)123456 , reader.getLongValue()  );
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "aShort", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals( (short)1, reader.getLongValue()  );
 
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -188,25 +188,25 @@ public void testArrayValueDocument() {
 		final StringJsonDocumentReader reader =
 				new StringJsonDocumentReader( "{ \"anEmptyArray\" : [], \"anArray\" : [1,2,3] }" );
 		assertTrue(reader.hasNext());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "anEmptyArray", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_END,reader.next());
 
-		assertEquals(  JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals(  JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "anArray", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals(1,  reader.getIntegerValue());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals(2,  reader.getIntegerValue());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals(3,  reader.getIntegerValue());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_END,reader.next());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
 	}
@@ -216,30 +216,30 @@ public void testObjectArrayMultipleValueDocument() {
 				new StringJsonDocumentReader( "{ \"anArray\" : [1, null, \"2\" ,  {\"foo\":\"bar\"}  ]  \n"
 											+ "     }" );
 		assertTrue(reader.hasNext());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "anArray", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_START,reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals("1",  reader.getStringValue());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NULL_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NULL_VALUE,reader.next());
 		assertEquals("null",  reader.getStringValue());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals(2,  reader.getIntegerValue());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "foo", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals("bar",  reader.getStringValue());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_END,reader.next());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
@@ -249,24 +249,24 @@ public void testEscapeStringDocument() {
 		final StringJsonDocumentReader reader =
 				new StringJsonDocumentReader( "{ \"str1\" : \"abc\" , \"str2\" : \"\\\"abc\\\"\" , \"str3\" : \"a\\\"b\\\"c\" }" );
 		assertTrue(reader.hasNext());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "str1", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals("abc",  reader.getStringValue());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "str2", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals("\"abc\"",  reader.getStringValue());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "str3", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals("a\"b\"c",  reader.getStringValue());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 
 		assertFalse(reader.hasNext(), "Should not have  anymore element");
 		assertThrows( NoSuchElementException.class, () -> {reader.next();} );
@@ -292,57 +292,57 @@ public void testNestedDocument() {
 							}
 						""");
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "nested", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START,reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "converted_gender", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals("M",  reader.getStringValue());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "theInteger", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals(-1,  reader.getIntegerValue());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "doubleNested", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START,reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "theNested", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START,reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "theLeaf", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_START,reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START,reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "stringField", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE,reader.next());
 		assertEquals("String \"<abc>A&B</abc>\"",  reader.getStringValue());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "integerField", reader.getObjectKeyName());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals("10",  reader.getStringValue());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 	}
 
 	@Test
@@ -357,28 +357,28 @@ public void testNestedArrayDocument() {
 									]
 							}
 						""");
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "nested", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_START,reader.next());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_START, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
 		assertEquals( "anArray", reader.getObjectKeyName());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_START,reader.next());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.NUMERIC_VALUE,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_START,reader.next());
+		assertEquals( JsonDocumentItemType.NUMERIC_VALUE,reader.next());
 		assertEquals(1L,  reader.getLongValue());
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_END,reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.OBJECT_END,reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END,reader.next());
 
-		assertEquals( JsonDocumentItem.JsonDocumentItemType.ARRAY_END,reader.next());
+		assertEquals( JsonDocumentItemType.ARRAY_END,reader.next());
 
-		assertEquals(JsonDocumentItem.JsonDocumentItemType.OBJECT_END, reader.next());
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 	}
 
 }

From 4592c417eba1ab4f46a36b4ed56a63102cc7400c Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Tue, 11 Feb 2025 15:55:52 +0530
Subject: [PATCH 46/81] HHH-17404- Remove DURATION mapping to INTERVALDS

---
 .../org/hibernate/dialect/OracleDialect.java  |   3 -
 .../dialect/OracleDurationJavaType.java       |  43 ------
 .../dialect/OracleDurationJdbcType.java       | 126 ------------------
 3 files changed, 172 deletions(-)
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index a91990e0a856..1e893633a285 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -1068,9 +1068,6 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 		// Oracle requires a custom binder for binding untyped nulls with the NULL type
 		typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
 		typeContributions.contributeJdbcType( ObjectNullAsNullTypeJdbcType.INSTANCE );
-		// Oracle Stores the duration is ISO-8601 format.
-		typeContributions.contributeJdbcType( OracleDurationJdbcType.INSTANCE );
-		typeContributions.contributeJavaType( OracleDurationJavaType.INSTANCE );
 
 		// Until we remove StandardBasicTypes, we have to keep this
 		typeContributions.contributeType(
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
deleted file mode 100644
index ea64ae7353c8..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJavaType.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.dialect;
-
-import oracle.sql.INTERVALDS;
-import org.hibernate.type.descriptor.WrapperOptions;
-import org.hibernate.type.descriptor.java.DurationJavaType;
-
-import java.time.Duration;
-
-/**
- * Oracle sub-implementation of {@link DurationJavaType}
- * which is a descriptor for {@link Duration}
- * This implementation brings the support of <code>oracle.sql.INTERVALDS</code>
- * as source type.
- * @see #wrap(Object, WrapperOptions)
- *
- * @author ejannett
- * @author Bidyadhar Mohanty
- */
-public class OracleDurationJavaType extends DurationJavaType {
-
-	public static final OracleDurationJavaType INSTANCE = new OracleDurationJavaType();
-
-	public OracleDurationJavaType() {
-		super();
-	}
-
-	@Override
-	public <X> Duration wrap(X value, WrapperOptions options) {
-		if(value == null) {
-			return null;
-		}
-
-		if ( value instanceof INTERVALDS ) {
-			return INTERVALDS.toDuration( ((INTERVALDS) value).toBytes() );
-		}
-
-		return super.wrap( value, options );
-	}
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
deleted file mode 100644
index 69eb2686ffb8..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDurationJdbcType.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * SPDX-License-Identifier: LGPL-2.1-or-later
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.dialect;
-
-import oracle.jdbc.OracleTypes;
-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.JavaType;
-import org.hibernate.type.descriptor.jdbc.BasicBinder;
-import org.hibernate.type.descriptor.jdbc.BasicExtractor;
-import org.hibernate.type.descriptor.jdbc.DurationJdbcType;
-import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
-
-import java.sql.CallableStatement;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.time.Duration;
-
-/**
- * Oracle sub-implementation of {@link DurationJdbcType}
- * which is a descriptor for {@link java.time.Duration}.
- * In Oracle databse Duration is stored as {@link OracleTypes.INTERVALDS}
- *
- * @author ejannett
- * @author Bidyadhar Mohanty
- */
-public class OracleDurationJdbcType extends DurationJdbcType {
-
-	public static final OracleDurationJdbcType INSTANCE = new OracleDurationJdbcType();
-
-	public OracleDurationJdbcType() {
-		super();
-	}
-
-	@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 {
-				st.setObject( index, javaType.unwrap( value, Duration.class, options ) );
-			}
-
-			@Override
-			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
-					throws SQLException {
-				st.setObject( name, javaType.unwrap( value, Duration.class, options ) );
-			}
-
-
-		};
-	}
-
-	@Override
-	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
-		return new BasicExtractor<>( javaType, this ) {
-			@Override
-			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-				// Handle the fact that a duration could also come as number of nanoseconds
-				final Object nativeValue = rs.getObject( paramIndex );
-				if ( nativeValue instanceof Number ) {
-					return javaType.wrap( nativeValue, options );
-				}
-				return javaType.wrap( rs.getObject( paramIndex, Duration.class ), options );
-			}
-
-			@Override
-			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-				// Handle the fact that a duration could also come as number of nanoseconds
-				final Object nativeValue = statement.getObject( index );
-				if ( nativeValue instanceof Number ) {
-					return javaType.wrap( nativeValue, options );
-				}
-				return javaType.wrap( statement.getObject( index, Duration.class ), options );
-			}
-
-			@Override
-			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
-					throws SQLException {
-				// Handle the fact that a duration could also come as number of nanoseconds
-				final Object nativeValue = statement.getObject( name );
-				if ( nativeValue instanceof Number ) {
-					return javaType.wrap( nativeValue, options );
-				}
-				return javaType.wrap( statement.getObject( name, Duration.class ), options );
-			}
-		};
-	}
-
-	@Override
-	public int getJdbcTypeCode() {
-		return SqlTypes.DURATION;
-	}
-
-	@Override
-	public int getDdlTypeCode() {
-		return OracleTypes.INTERVALDS;
-	}
-
-	@Override
-	public int getDefaultSqlTypeCode() {
-		return OracleTypes.INTERVALDS;
-	}
-
-	@Override
-	public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
-		return Duration.class;
-	}
-
-	@Override
-	public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
-		return (appender, value, dialect, wrapperOptions) -> dialect.appendIntervalLiteral(
-				appender,
-				javaType.unwrap(
-						value,
-						Duration.class,
-						wrapperOptions
-				)
-		);
-	}
-}

From e938fda07f9543d3adea8913427158dfd78ebd0e Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Tue, 11 Feb 2025 17:52:35 +0530
Subject: [PATCH 47/81] HHH-17404- Remove DURATION mapping to INTERVALDS (2)

---
 .../src/main/java/org/hibernate/dialect/OracleDialect.java     | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 1e893633a285..00d379ebcad6 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -856,7 +856,6 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
 		}
 		// We need the DDL type during runtime to produce the proper encoding in certain functions
 		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( BIT, "number(1,0)", this ) );
-		ddlTypeRegistry.addDescriptor( new DdlTypeImpl( oracle.jdbc.OracleTypes.INTERVALDS, "interval day to second", this ) );
 
 	}
 
@@ -889,8 +888,6 @@ public JdbcType resolveSqlTypeDescriptor(
 		switch ( jdbcTypeCode ) {
 			case OracleTypes.JSON:
 				return jdbcTypeRegistry.getDescriptor( JSON );
-			case oracle.jdbc.OracleTypes.INTERVALDS:
-				return jdbcTypeRegistry.getDescriptor( DURATION );
 			case STRUCT:
 				if ( "MDSYS.SDO_GEOMETRY".equals( columnTypeName ) ) {
 					jdbcTypeCode = GEOMETRY;

From 0182f5d1d83ae9a2c3cf338cb6f4be6b716bca34 Mon Sep 17 00:00:00 2001
From: Bidyadhar Mohanty <bidyadhar.mohanty@oracle.com>
Date: Tue, 11 Feb 2025 20:41:59 +0530
Subject: [PATCH 48/81] HHH-17404: Undo Test Skips

---
 .../org/hibernate/orm/test/mapping/embeddable/Aggregate.java  | 4 ----
 .../test/mapping/embeddable/NestedStructEmbeddableTest.java   | 1 -
 .../embeddable/NestedStructWithArrayEmbeddableTest.java       | 1 -
 .../test/mapping/embeddable/StructEmbeddableArrayTest.java    | 1 -
 4 files changed, 7 deletions(-)

diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java
index c0d229411aa8..c761c25904af 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/Aggregate.java
@@ -149,10 +149,6 @@ public void setConvertedGender(EntityOfBasics.Gender convertedGender) {
 		this.convertedGender = convertedGender;
 	}
 
-	public Boolean getTheBoolean() {
-		return theBoolean;
-	}
-
 	@Column(name = "ordinal_gender")
 	public EntityOfBasics.Gender getOrdinalGender() {
 		return ordinalGender;
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java
index 3ea0197f0107..32014e193fb6 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructEmbeddableTest.java
@@ -79,7 +79,6 @@
 @RequiresDialect( PostgreSQLDialect.class )
 @RequiresDialect( OracleDialect.class )
 @RequiresDialect( DB2Dialect.class )
-@SkipForDialect(dialectClass = OracleDialect.class, reason = "Waiting for the fix of a bug that prevent creation of INTERVALDS from Duration")
 public class NestedStructEmbeddableTest implements AdditionalMappingContributor {
 
 	@Override
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java
index acef9d18de66..bd9b0bcdde3e 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/NestedStructWithArrayEmbeddableTest.java
@@ -56,7 +56,6 @@
 
 @RequiresDialect( PostgreSQLDialect.class )
 @RequiresDialect( OracleDialect.class )
-@SkipForDialect(dialectClass = OracleDialect.class, reason = "Waiting for the fix of a bug that prevent creation of INTERVALDS from Duration")
 @BootstrapServiceRegistry(
 		// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
 		integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java
index 8eb9b5fb2abe..28847fee8230 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/StructEmbeddableArrayTest.java
@@ -83,7 +83,6 @@
 @SessionFactory
 @RequiresDialect( PostgreSQLDialect.class )
 @RequiresDialect( OracleDialect.class )
-@SkipForDialect(dialectClass = OracleDialect.class, reason = "Waiting for the fix of a bug that prevent creation of INTERVALDS from Duration")
 public class StructEmbeddableArrayTest implements AdditionalMappingContributor {
 
 	@Override

From 1f947db186c3509addd1c9bfbf2da13d3b8609c6 Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Tue, 18 Feb 2025 10:22:35 +0100
Subject: [PATCH 49/81] HHH-17404 : applied eview comment

---
 .../type/descriptor/jdbc/JsonHelper.java      | 25 ++++++++++++++++-
 .../type/format/OsonDocumentReader.java       |  3 +--
 .../type/format/OsonDocumentWriter.java       | 27 ++++++++-----------
 3 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index a98536091697..64e3e3b6167b 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -72,8 +72,31 @@ public static void serializeArray(MappingType elementMappingType, Object[] value
 				if (value == null) {
 					writer.nullValue();
 				}
-				else {
+				else if ( elementMappingType instanceof EmbeddableMappingType ) {
 					JsonHelper.serialize( (EmbeddableMappingType) elementMappingType, value, options, writer );
+				} else if ( elementMappingType instanceof BasicType<?> ) {
+					//noinspection unchecked
+					final BasicType<Object> basicType = (BasicType<Object>) elementMappingType;
+
+					if ( isArrayType(basicType.getJdbcType())) {
+						final int length = Array.getLength( value );
+						if ( length != 0 ) {
+							//noinspection unchecked
+							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);
+							}
+						}
+					}
+					else {
+						writer.serializeJsonValue(basicType.convertToRelationalValue( value),
+								(JavaType<Object>)basicType.getJdbcJavaType(),basicType.getJdbcType(), options);
+					}
+				}
+				else {
+					throw new UnsupportedOperationException( "Support for mapping type not yet implemented: " + elementMappingType.getClass().getName() );
 				}
 			}
 			catch (IOException e) {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
index b14bdc7f3702..750a3b884f27 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
@@ -100,9 +100,8 @@ public JsonDocumentItemType next() {
 				currentValue = this.parser.getBytes();
 				return JsonDocumentItemType.VALUE;
 			default :
-				assert false:"Unknown OSON event";
+				throw new IllegalStateException( "Unknown OSON event: " + evt );
 		}
-		return null;
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index e3be47512647..89a7a3da8867 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -22,9 +22,7 @@
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.time.Duration;
-import java.time.Instant;
 import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
 import java.util.UUID;
 
 /**
@@ -108,7 +106,14 @@ public JsonDocumentWriter stringValue(String value) {
 
 	@Override
 	public JsonDocumentWriter numberValue(Number value) {
-		this.generator.write((BigDecimal) value );
+		if (value instanceof BigDecimal) {
+			this.generator.write((BigDecimal) value );
+		} else if (value instanceof BigInteger) {
+			this.generator.write((BigInteger) value );
+		} else {
+			//fallback.
+			this.generator.write( value.longValue() );
+		}
 		return this;
 	}
 
@@ -214,17 +219,8 @@ private void serializeValue(Object value,
 				}
 				break;
 			case SqlTypes.TIMESTAMP_UTC:
-				if( value instanceof OffsetDateTime ) {
-					OffsetDateTime odt = javaType.unwrap( value, OffsetDateTime.class, options );
-					generator.write( odt );
-					break;
-				}
-				else if (value instanceof Instant ) {
-					Instant instant = javaType.unwrap( value, Instant.class, options );
-					generator.write(instant.atOffset( ZoneOffset.UTC )  );
-					break;
-				}
-				generator.write( javaType.unwrap( value,String.class,options ) );
+				OffsetDateTime odt = javaType.unwrap( value, OffsetDateTime.class, options );
+				generator.write( odt );
 				break;
 			case SqlTypes.NUMERIC:
 			case SqlTypes.DECIMAL:
@@ -251,8 +247,7 @@ else if (value instanceof Instant ) {
 				break;
 			case SqlTypes.ARRAY:
 			case SqlTypes.JSON_ARRAY:
-				assert false:"array case should be treated at upper level";
-				break;
+				throw new IllegalStateException( "array case should be treated at upper level" );
 			default:
 				throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
 		}

From fd04e1cdb89f859334bf3cb34bd2d6567306c17b Mon Sep 17 00:00:00 2001
From: Emmanuel Jannetti <emmanuel.jannetti@oracle.com>
Date: Thu, 13 Mar 2025 19:35:29 +0100
Subject: [PATCH 50/81] HHH-17404 : applied review comments. removed use of
 valueIsAString

---
 .../type/format/OsonDocumentReader.java       | 23 +++++++++----------
 .../type/format/OsonDocumentWriter.java       |  4 +---
 2 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
index 750a3b884f27..6a7a2a7e2841 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
@@ -27,7 +27,7 @@ public class OsonDocumentReader implements JsonDocumentReader {
 	final private OracleJsonParser parser;
 	private String currentKeyName;
 	private Object currentValue;
-	private boolean currentValueIsAString; // avoid later introspection
+
 	/**
 	 * Creates a new <code>OsonDocumentReader</code>  on top of a <code>OracleJsonParser</code>
 	 * @param parser the parser
@@ -48,7 +48,6 @@ public JsonDocumentItemType next() {
 		OracleJsonParser.Event evt = this.parser.next();
 		currentKeyName = null;
 		currentValue = null;
-		currentValueIsAString = false;
 		switch (evt) {
 			case OracleJsonParser.Event.START_OBJECT:
 				return JsonDocumentItemType.OBJECT_START;
@@ -76,7 +75,6 @@ public JsonDocumentItemType next() {
 				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_STRING:
 				currentValue = this.parser.getString();
-				currentValueIsAString = true;
 				return JsonDocumentItemType.VALUE;
 			case OracleJsonParser.Event.VALUE_TRUE:
 				currentValue = Boolean.TRUE;
@@ -128,43 +126,44 @@ public BigInteger getBigIntegerValue() {
 
 	@Override
 	public double getDoubleValue() {
-		if (currentValueIsAString) return Double.parseDouble( (String)currentValue );
+		if (currentValue instanceof String)
+			return Double.parseDouble( (String)currentValue );
 		return ((Double)currentValue).doubleValue();
 	}
 
 	@Override
 	public float getFloatValue() {
-		if (currentValueIsAString) return Float.parseFloat( (String)currentValue );
+		if (currentValue instanceof String) return Float.parseFloat( (String)currentValue );
 		return ((Float)currentValue).floatValue();
 	}
 
 	@Override
 	public long getLongValue() {
-		if (currentValueIsAString) return Long.parseLong( (String)currentValue );
+		if (currentValue instanceof String) return Long.parseLong( (String)currentValue );
 		return ((BigDecimal)currentValue).longValue();
 	}
 
 	@Override
 	public int getIntegerValue() {
-		if (currentValueIsAString) return Integer.parseInt( (String)currentValue );
+		if (currentValue instanceof String) return Integer.parseInt( (String)currentValue );
 		return ((BigDecimal)currentValue).intValue();
 	}
 
 	@Override
 	public short getShortValue() {
-		if (currentValueIsAString) return Short.parseShort( (String)currentValue );
+		if (currentValue instanceof String) return Short.parseShort( (String)currentValue );
 		return  ((BigDecimal)currentValue).shortValue();
 	}
 
 	@Override
 	public byte getByteValue() {
-		if (currentValueIsAString) return Byte.parseByte( (String)currentValue );
+		if (currentValue instanceof String) return Byte.parseByte( (String)currentValue );
 		return ((Byte)currentValue).byteValue();
 	}
 
 	@Override
 	public boolean getBooleanValue() {
-		if (currentValueIsAString) return BooleanJavaType.INSTANCE.fromEncodedString((String)currentValue);
+		if (currentValue instanceof String) return BooleanJavaType.INSTANCE.fromEncodedString((String)currentValue);
 		return ((Boolean)currentValue).booleanValue();
 	}
 
@@ -172,7 +171,7 @@ public boolean getBooleanValue() {
 
 	@Override
 	public <T> T getValue(JavaType<T> javaType, WrapperOptions options) {
-		if ( currentValueIsAString ) {
+		if ( currentValue instanceof String ) {
 			if (javaType.equals(PrimitiveByteArrayJavaType.INSTANCE)) {
 				// be sure that we have only allowed characters.
 				// that may happen for string representation of UUID (i.e 53886a8a-7082-4879-b430-25cb94415be8) for instance
@@ -183,7 +182,7 @@ public <T> T getValue(JavaType<T> javaType, WrapperOptions options) {
 
 		Object theOneToBeUsed =  currentValue;
 		// handle special cases for Date things
-		if ( currentValue.getClass() == LocalDateTime.class ) {
+		if ( currentValue instanceof LocalDateTime ) {
 			if ( java.sql.Date.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
 				theOneToBeUsed = Date.valueOf( ((LocalDateTime)currentValue).toLocalDate() );
 			}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index 89a7a3da8867..37ba46e26f15 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -14,7 +14,6 @@
 import org.hibernate.type.SqlTypes;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
-import org.hibernate.type.descriptor.java.UUIDJavaType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 
 import java.math.BigDecimal;
@@ -23,7 +22,6 @@
 import java.sql.Timestamp;
 import java.time.Duration;
 import java.time.OffsetDateTime;
-import java.util.UUID;
 
 /**
  * Implementation of <code>JsonDocumentWriter</code> for OSON document.
@@ -233,7 +231,7 @@ private void serializeValue(Object value,
 				generator.write( duration );
 				break;
 			case SqlTypes.UUID:
-				generator.write( UUIDJavaType.INSTANCE.unwrap( (UUID)value, byte[].class, options ) );
+				generator.write( javaType.unwrap( value, String.class, options ) );
 				break;
 			case SqlTypes.BINARY:
 			case SqlTypes.VARBINARY:

From 9598bd2fb76014cd8b8422e9a3a436b1c51958b0 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Wed, 26 Mar 2025 08:31:01 +0100
Subject: [PATCH 51/81] HHH-17404 : fix build after merge

---
 .../boot/internal/SessionFactoryOptionsBuilder.java  |  2 +-
 .../java/org/hibernate/dialect/OracleDialect.java    |  2 +-
 .../dialect/OracleOsonArrayJdbcTypeConstructor.java  |  2 +-
 .../dialect/OracleOsonJacksonArrayJdbcType.java      |  4 ++--
 .../hibernate/dialect/OracleOsonJacksonJdbcType.java | 12 ++++++------
 .../dialect/aggregate/OracleAggregateSupport.java    |  2 +-
 .../hibernate/type/descriptor/jdbc/JsonHelper.java   |  2 +-
 .../hibernate/type/format/JsonDocumentItemType.java  |  2 +-
 .../hibernate/type/format/JsonDocumentReader.java    |  2 +-
 .../type/format/JsonDocumentReaderFactory.java       |  2 +-
 .../hibernate/type/format/JsonDocumentWriter.java    |  2 +-
 .../type/format/JsonValueJDBCTypeAdapter.java        |  2 +-
 .../type/format/JsonValueJDBCTypeAdapterFactory.java |  2 +-
 .../hibernate/type/format/OsonDocumentReader.java    |  2 +-
 .../hibernate/type/format/OsonDocumentWriter.java    |  2 +-
 .../type/format/OsonValueJDBCTypeAdapter.java        |  2 +-
 .../hibernate/type/format/StringJsonDocument.java    |  2 +-
 .../type/format/StringJsonDocumentMarker.java        |  2 +-
 .../type/format/StringJsonDocumentReader.java        |  2 +-
 .../type/format/StringJsonDocumentWriter.java        |  2 +-
 .../type/format/StringJsonValueJDBCTypeAdapter.java  |  4 ++--
 .../type/format/jackson/JacksonOsonFormatMapper.java |  2 +-
 .../orm/test/util/StringJsonDocumentReaderTest.java  |  2 +-
 .../orm/test/util/StringJsonDocumentWriterTest.java  |  2 +-
 24 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
index 75d07ae139f4..2684a90f168d 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
@@ -789,7 +789,7 @@ private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
 						.getDefaultConnectionHandlingMode();
 	}
 
-	private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector, ConfigurationService configurationService) {
+	private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector) {
 		return strategySelector.resolveDefaultableStrategy(
 				FormatMapper.class,
 				setting,
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 00d379ebcad6..054e9ccb4e0d 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -92,7 +92,6 @@
 import org.hibernate.type.NullType;
 import org.hibernate.type.StandardBasicTypes;
 import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
-import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.descriptor.jdbc.NullJdbcType;
 import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
@@ -1019,6 +1018,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 			);
 		}
 
+
 		final String mapperName = configurationService.getSetting( "hibernate.type.json_format_mapper",
 				StandardConverters.STRING,JACKSON_MAPPER_NAME);
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
index b91c6a86f931..806d9f98d8a8 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.dialect;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index 282798f062aa..b123fc26988f 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.dialect;
@@ -133,7 +133,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
-				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				FormatMapper mapper = options.getJsonFormatMapper();
 				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
 				JsonParser osonParser = osonFactory.createParser( osonBytes );
 				return mapper.readFromSource(  getJavaType(), osonParser, options);
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index eabb8cf716c5..7922a665c83f 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.dialect;
@@ -85,7 +85,7 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 			private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
 
-				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				FormatMapper mapper = options.getJsonFormatMapper();
 
 				if (getEmbeddableMappingType() != null) {
 					ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -96,7 +96,7 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 					}
 					return out.toByteArray();
 				}
-				
+
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
 				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
 				try (JsonGenerator osonGen = osonFactory.createGenerator( out )) {
@@ -140,7 +140,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 
-				FormatMapper mapper = options.getSession().getSessionFactory().getFastSessionServices().getJsonFormatMapper();
+				FormatMapper mapper = options.getJsonFormatMapper();
 
 				if (getEmbeddableMappingType() != null &&
 						getJavaType().getJavaTypeClass() == Object[].class) {
@@ -185,8 +185,8 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-				 OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-				 return doExtraction(ojd,options);
+				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+				return doExtraction(ojd,options);
 
 			}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index 82e89624a3a9..a36edcf8f1b7 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -90,7 +90,7 @@ public static AggregateSupport valueOf(Dialect dialect, boolean useDateStoredAsS
 			default -> version.isSameOrAfter( 23 )
 				? useDateStoredAsString?OracleAggregateSupport.V23_INSTANCE:
 				OracleAggregateSupport.V23_OSON_EXT_INSTANCE
-                                : OracleAggregateSupport.LEGACY_INSTANCE;
+								: OracleAggregateSupport.LEGACY_INSTANCE;
 		};
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 64e3e3b6167b..173acab3d86b 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -17,8 +17,8 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-
 import org.hibernate.Internal;
+import org.hibernate.internal.build.AllowReflection;
 import org.hibernate.internal.util.collections.ArrayHelper;
 import org.hibernate.internal.util.collections.StandardStack;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java
index a2256446f95d..1c57718619e1 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentItemType.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
index f97b51e3f474..f276e8d69aad 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReader.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
index 3dcbed17f1b8..92bf2350b236 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
index 5d92084ab01a..71b66cf9c9e3 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
index d1c85b0bc500..dbee32e4ed2f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java
index d1336a84cd7c..92cf62eb5697 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapterFactory.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
index 6a7a2a7e2841..dd7405d8fb0e 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index 37ba46e26f15..cfa9841bcb0a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
index 5d8a38831fd0..d94af51b883d 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
index fe9059644220..6eda9b3ce7e6 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java
index d017529f8439..4aefcbb3f6dd 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentMarker.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index 68aa98df47d8..2f749a08fa7f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
index 0bcbc44a71c3..b537c05c7cc5 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
index 7f6f614869fd..90c41e3d58eb 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format;
@@ -133,7 +133,7 @@ else if ( jdbcJavaType instanceof EnumJavaType<?> ) {
 								options
 						);
 						final EmbeddableMappingType embeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
-						return instantiate( embeddableMappingType, subAttributeValues, options.getSessionFactory() ) ;
+						return instantiate( embeddableMappingType, subAttributeValues ) ;
 					}
 					return subValues;
 				}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 883179173648..f6cc9fbe62c4 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.type.format.jackson;
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
index a614b63efb29..2371a39ffab3 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.orm.test.util;
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
index 1e7a3207fc99..0c4981b610eb 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
@@ -1,5 +1,5 @@
 /*
- * SPDX-License-Identifier: LGPL-2.1-or-later
+ * SPDX-License-Identifier: Apache-2.0
  * Copyright Red Hat Inc. and Hibernate Authors
  */
 package org.hibernate.orm.test.util;

From 941c8e7835f94a2f9d3bbfa40aba4b0ace22670c Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Thu, 27 Mar 2025 00:17:42 +0100
Subject: [PATCH 52/81] HHH-17404 : refactor StringJsonDocumentReader to remove
 usage of CharBuffer

---
 .../cfg/DialectSpecificSettings.java          |  15 +
 .../org/hibernate/dialect/OracleDialect.java  |  10 +-
 .../OracleOsonJacksonArrayJdbcType.java       |  17 +-
 .../dialect/OracleOsonJacksonJdbcType.java    |  81 +++++-
 .../dialect/OracleServerConfiguration.java    |  17 +-
 .../hibernate/internal/CoreMessageLogger.java |   7 +
 .../type/descriptor/jdbc/JsonHelper.java      |  16 +-
 .../type/descriptor/jdbc/JsonJdbcType.java    |   5 +-
 .../type/format/AbstractJsonFormatMapper.java |  20 --
 .../hibernate/type/format/FormatMapper.java   |  16 +-
 .../format/JsonDocumentReaderFactory.java     |  33 ---
 .../type/format/JsonDocumentWriter.java       |   7 -
 .../type/format/JsonValueJDBCTypeAdapter.java |   1 +
 .../type/format/OsonDocumentReader.java       |   3 +-
 .../type/format/OsonDocumentWriter.java       |  14 -
 .../type/format/StringJsonDocument.java       |   4 +-
 .../type/format/StringJsonDocumentReader.java | 265 ++++++++++--------
 .../type/format/StringJsonDocumentWriter.java |  48 ++--
 .../jackson/JacksonXmlFormatMapper.java       |  21 --
 .../util/StringJsonDocumentWriterTest.java    |  38 ++-
 20 files changed, 333 insertions(+), 305 deletions(-)
 delete mode 100644 hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java

diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java
index d7dca4889247..8e9db591048c 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java
@@ -52,6 +52,21 @@ public interface DialectSpecificSettings {
 	 */
 	String ORACLE_USE_BINARY_FLOATS = "hibernate.dialect.oracle.use_binary_floats";
 
+	/**
+	 * Specifies whether usage of the Oracle JSON binary format (aka OSON) should be disabled.
+	 * <p>
+	 * Starting to 21c, if the ojdbc-provider-jackson-oson extension is available. JSON data in an oracle
+	 * database are stored using the OSON binary format. This setting can be used to fallback to the old implementation
+	 * based on String serialization.
+	 *
+	 * @settingDefault {@code false}
+	 * @since 7.0
+	 *
+	 * @see <a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/adjsn/json-oracle-database.html">Orace OSON format</a>
+	 * @see <a href="https://github.com/oracle/ojdbc-extensions/blob/main/ojdbc-provider-jackson-oson/README.md">Jackson OSON provider</a>
+	 */
+	String ORACLE_OSON_DISABLED = "hibernate.dialect.oracle.oson_format_disabled";
+
 	/**
 	 * Specifies whether the {@code ansinull} setting is enabled on Sybase.
 	 * <p>
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 054e9ccb4e0d..54ece231e171 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -221,6 +221,10 @@ protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggr
 
 	// Is the database accessed using a database service protected by Application Continuity.
 	protected final boolean applicationContinuity;
+
+	// Is the database OSON format should be disabled.
+	protected final boolean isOracleOsonDisabled;
+
 	protected final int driverMajorVersion;
 	protected final int driverMinorVersion;
 	private boolean useBinaryFloat;
@@ -234,6 +238,7 @@ public OracleDialect(DatabaseVersion version) {
 		autonomous = false;
 		extended = false;
 		applicationContinuity = false;
+		isOracleOsonDisabled = false;
 		driverMajorVersion = 19;
 		driverMinorVersion = 0;
 	}
@@ -247,6 +252,7 @@ public OracleDialect(DialectResolutionInfo info, OracleServerConfiguration confi
 		autonomous = configuration.isAutonomous();
 		extended = configuration.isExtended();
 		applicationContinuity = configuration.isApplicationContinuity();
+		isOracleOsonDisabled = configuration.isOSONEnabled();
 		driverMinorVersion = configuration.getDriverMinorVersion();
 		driverMajorVersion = configuration.getDriverMajorVersion();
 	}
@@ -263,6 +269,8 @@ public boolean isApplicationContinuity() {
 		return applicationContinuity;
 	}
 
+	public boolean isOracleOsonDisabled() {return isOracleOsonDisabled;}
+
 	@Override
 	protected DatabaseVersion getMinimumSupportedVersion() {
 		return MINIMUM_VERSION;
@@ -1024,7 +1032,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 		if ( getVersion().isSameOrAfter( 21 ) ) {
 
-			if ( JacksonIntegration.isOracleOsonExtensionAvailable() && JACKSON_MAPPER_NAME.equalsIgnoreCase( mapperName )) {
+			if ( !isOracleOsonDisabled() && JacksonIntegration.isOracleOsonExtensionAvailable() && JACKSON_MAPPER_NAME.equalsIgnoreCase( mapperName )) {
 				// We must check that that extension is available and actually used.
 				typeContributions.contributeJdbcType( OracleOsonJacksonJdbcType.INSTANCE );
 				typeContributions.contributeJdbcTypeConstructor( OracleOsonArrayJdbcTypeConstructor.INSTANCE );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index b123fc26988f..be465fab4378 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -45,17 +45,17 @@
 public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
 
 
-	private static final Class osonFactoryKlass;
-
+	private static final Object osonFactory;
 	static {
 		try {
-			osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
+			Class osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
+			osonFactory = osonFactoryKlass.getDeclaredConstructor().newInstance();
 		}
-		catch (ClassNotFoundException | LinkageError e) {
-			// should not happen as OracleOsonJacksonArrayJdbcType is loaded
-			// only when an Oracle OSON JDBC extension is present
+		catch (Exception | LinkageError e) {
+			// should not happen as OracleOsonJacksonJdbcType is loaded
+			// only when Oracle OSON JDBC extension is present
 			// see OracleDialect class.
-			throw new ExceptionInInitializerError( "OracleOsonJacksonArrayJdbcType class loaded without OSON extension: " + e.getClass()+ " " + e.getMessage());
+			throw new ExceptionInInitializerError( "OracleOsonJacksonJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
 		}
 	}
 
@@ -134,8 +134,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getJsonFormatMapper();
-				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
-				JsonParser osonParser = osonFactory.createParser( osonBytes );
+				JsonParser osonParser = ((JsonFactory)osonFactory).createParser( osonBytes );
 				return mapper.readFromSource(  getJavaType(), osonParser, options);
 			}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 7922a665c83f..55f6dfd66f5b 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -8,10 +8,13 @@
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
+import oracle.jdbc.driver.DatabaseError;
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
 import oracle.sql.json.OracleJsonParser;
+import org.hibernate.internal.CoreLogging;
+import org.hibernate.internal.CoreMessageLogger;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
 import org.hibernate.type.descriptor.ValueBinder;
@@ -22,9 +25,11 @@
 import org.hibernate.type.descriptor.jdbc.BasicBinder;
 import org.hibernate.type.descriptor.jdbc.BasicExtractor;
 import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.OsonDocumentReader;
 import org.hibernate.type.format.OsonDocumentWriter;
 import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.sql.CallableStatement;
@@ -43,12 +48,15 @@
 public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
 	public static final OracleOsonJacksonJdbcType INSTANCE = new OracleOsonJacksonJdbcType( null );
 
-	private static final Class osonFactoryKlass;
+	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonJdbcType.class );
+
+	private static final Object osonFactory;
 	static {
 		try {
-			osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
+			Class osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
+			osonFactory = osonFactoryKlass.getDeclaredConstructor().newInstance();
 		}
-		catch (ClassNotFoundException | LinkageError e) {
+		catch (Exception | LinkageError e) {
 			// should not happen as OracleOsonJacksonJdbcType is loaded
 			// only when Oracle OSON JDBC extension is present
 			// see OracleDialect class.
@@ -98,8 +106,7 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 				}
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
-				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
-				try (JsonGenerator osonGen = osonFactory.createGenerator( out )) {
+				try (JsonGenerator osonGen = ((JsonFactory)osonFactory).createGenerator( out )) {
 					mapper.writeToTarget( value, javaType, osonGen, options );
 				}
 				return out.toByteArray();
@@ -151,7 +158,7 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
 					OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( osonBytes );
 					Object[] objects =  JsonHelper.deserialize(
 							getEmbeddableMappingType(),
-							osonParser,
+							new OsonDocumentReader(osonParser),
 							javaType.getJavaTypeClass() != Object[].class,
 							options
 					);
@@ -164,12 +171,23 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
 					type = (JavaType<X>) getEmbeddableMappingType().getJavaType();
 				}
 
-				JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
-				try (JsonParser osonParser = osonFactory.createParser(  osonBytes )) {
+				try (JsonParser osonParser = ((JsonFactory)osonFactory).createParser(  osonBytes )) {
 					return mapper.readFromSource( type, osonParser, options );
 				}
 			}
 
+			private X doExtraction(byte[] bytes,  WrapperOptions options) throws SQLException {
+				if ( bytes == null ) {
+					return null;
+				}
+
+				try {
+					return fromOson( new ByteArrayInputStream(bytes) ,options);
+				}
+				catch (Exception e) {
+					throw new SQLException( e );
+				}
+			}
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
 				if ( datum == null ) {
 					return null;
@@ -185,22 +203,55 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-				return doExtraction(ojd,options);
-
+				try {
+					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+					return doExtraction(ojd,options);
+				} catch (SQLException exc) {
+					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+						// this may happen if we are fetching data from an existing schema
+						// that use CBLOB for JSON column
+						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+						return doExtraction(rs.getBytes( paramIndex ), options);
+					} else {
+						throw exc;
+					}
+				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
-				return doExtraction(ojd,options);
+				try {
+					OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
+					return doExtraction(ojd,options);
+				} catch (SQLException exc) {
+					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+						// this may happen if we are fetching data from an existing schema
+						// that use CBLOB for JSON column
+						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+						return doExtraction(statement.getBytes( index ), options);
+					} else {
+						throw exc;
+					}
+				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
-				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
-				return doExtraction(ojd,options);
+				try {
+					OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
+					return doExtraction(ojd,options);
+				} catch (SQLException exc) {
+					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+						// this may happen if we are fetching data from an existing schema
+						// that use CBLOB for JSON column
+						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+						return doExtraction(statement.getBytes( name ), options);
+					} else {
+						throw exc;
+					}
+				}
+
 			}
 
 		};
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java
index e0f748e8add5..c2f41088fe2d 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java
@@ -20,6 +20,7 @@
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_APPLICATION_CONTINUITY;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
+import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
 
 /**
@@ -33,6 +34,7 @@ public class OracleServerConfiguration {
 	private final boolean autonomous;
 	private final boolean extended;
 	private final boolean applicationContinuity;
+	private final boolean osonDisabled;
 	private final int driverMajorVersion;
 	private final int driverMinorVersion;
 
@@ -48,6 +50,10 @@ public boolean isApplicationContinuity() {
 		return applicationContinuity;
 	}
 
+	public boolean isOSONEnabled() {
+		return osonDisabled;
+	}
+
 	public int getDriverMajorVersion() {
 		return driverMajorVersion;
 	}
@@ -57,7 +63,7 @@ public int getDriverMinorVersion() {
 	}
 
 	public OracleServerConfiguration(boolean autonomous, boolean extended) {
-		this( autonomous, extended, false, 19, 0 );
+		this( autonomous, extended, false, false, 19, 0 );
 	}
 
 	public OracleServerConfiguration(
@@ -65,18 +71,20 @@ public OracleServerConfiguration(
 			boolean extended,
 			int driverMajorVersion,
 			int driverMinorVersion) {
-		this( autonomous, extended, false, driverMajorVersion, driverMinorVersion );
+		this( autonomous, extended, false, false, driverMajorVersion, driverMinorVersion );
 	}
 
 	public OracleServerConfiguration(
 			boolean autonomous,
 			boolean extended,
 			boolean applicationContinuity,
+			boolean osonDisabled,
 			int driverMajorVersion,
 			int driverMinorVersion) {
 		this.autonomous = autonomous;
 		this.extended = extended;
 		this.applicationContinuity = applicationContinuity;
+		this.osonDisabled = osonDisabled;
 		this.driverMajorVersion = driverMajorVersion;
 		this.driverMinorVersion = driverMinorVersion;
 	}
@@ -88,10 +96,13 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut
 		final boolean defaultExtended = getBoolean( ORACLE_EXTENDED_STRING_SIZE, configuration, false );
 		final boolean defaultAutonomous =  getBoolean( ORACLE_AUTONOMOUS_DATABASE, configuration, false );
 		final boolean defaultContinuity = getBoolean( ORACLE_APPLICATION_CONTINUITY, configuration, false );
+		final boolean defaultOsonDisabled = getBoolean( ORACLE_OSON_DISABLED , configuration, false );
 
 		boolean extended;
 		boolean autonomous;
 		boolean applicationContinuity;
+		boolean osonDisabled = defaultOsonDisabled;
+
 		int majorVersion;
 		int minorVersion;
 		final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
@@ -127,7 +138,7 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut
 			}
 		}
 
-		return new OracleServerConfiguration( autonomous, extended, applicationContinuity, majorVersion, minorVersion );
+		return new OracleServerConfiguration( autonomous, extended, applicationContinuity, osonDisabled,majorVersion, minorVersion );
 	}
 
 	private static boolean isExtended(Statement statement) {
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java
index f4e02ec60ee9..e4c891cec5c7 100644
--- a/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/CoreMessageLogger.java
@@ -760,6 +760,13 @@ void unableToLocateStaticMetamodelField(
 	)
 	void duplicatedPersistenceUnitName(String name);
 
+	@LogMessage(level = WARN)
+	@Message(
+			id = 15019,
+			value = "Invalid JSON column type [%s], was expecting [%s]; for efficiency schema should be migrate to JSON DDL type"
+	)
+	void invalidJSONColumnType(String actual, String expected);
+
 	@LogMessage(level = DEBUG)
 	@Message(
 			id = 455,
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 173acab3d86b..98c6a7b3b3e9 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -38,12 +38,12 @@
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.format.JsonDocumentItemType;
 import org.hibernate.type.format.JsonDocumentReader;
-import org.hibernate.type.format.JsonDocumentReaderFactory;
 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;
+import org.hibernate.type.format.StringJsonDocumentReader;
 
 /**
  * A Helper for serializing and deserializing JSON, based on an {@link org.hibernate.metamodel.mapping.EmbeddableMappingType}.
@@ -395,7 +395,7 @@ private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, Embedda
 	/**
 	 * Deserialize a JSON value to Java Object
 	 * @param embeddableMappingType the mapping type
-	 * @param source the JSON value
+	 * @param reader the JSON reader
 	 * @param returnEmbeddable do we return an Embeddable object or array of Objects
 	 * @param options wrappping options
 	 * @return the deserialized value
@@ -404,14 +404,10 @@ private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, Embedda
 	 */
 	public static <X> X deserialize(
 			EmbeddableMappingType embeddableMappingType,
-			Object source,
+			JsonDocumentReader reader,
 			boolean returnEmbeddable,
 			WrapperOptions options) throws SQLException {
 
-		if ( source == null ) {
-			return null;
-		}
-		JsonDocumentReader reader = JsonDocumentReaderFactory.getJsonDocumentReader(source);
 
 		final Object[] values = consumeJsonDocumentItems(reader, embeddableMappingType, returnEmbeddable, options);
 		if ( returnEmbeddable ) {
@@ -448,7 +444,7 @@ public static <X> X arrayFromString(
 		else {
 			jdbcJavaType = options.getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor( preferredJavaTypeClass );
 		}
-		JsonDocumentReader reader = JsonDocumentReaderFactory.getJsonDocumentReader(string);
+		JsonDocumentReader reader = new StringJsonDocumentReader(string);
 		JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,false);
 
 		assert reader.hasNext():"Invalid array string";
@@ -457,7 +453,7 @@ public static <X> X arrayFromString(
 		while(reader.hasNext()) {
 			JsonDocumentItemType type = reader.next();
 			switch ( type ) {
-				case JsonDocumentItemType.ARRAY_END:
+				case ARRAY_END:
 					endArrayFound=true;
 					break;
 				case NULL_VALUE:
@@ -473,7 +469,7 @@ public static <X> X arrayFromString(
 					arrayList.add( adapter.fromValue(jdbcJavaType, elementJdbcType ,reader, options) );
 					break;
 				default:
-					assert false : "Unexpected type " + type;
+					throw new UnsupportedOperationException( "Unexpected JSON type " + type );
 			}
 		}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
index ebc6c3b1b3e0..c927c262f38a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
@@ -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.format.StringJsonDocumentReader;
 import org.hibernate.type.format.StringJsonDocumentWriter;
 
 /**
@@ -78,7 +79,7 @@ protected <X> X fromString(String string, JavaType<X> javaType, WrapperOptions o
 		if ( embeddableMappingType != null ) {
 			return (X) JsonHelper.deserialize(
 					embeddableMappingType,
-					string,
+					new StringJsonDocumentReader(string),
 					javaType.getJavaTypeClass() != Object[].class,
 					options
 			);
@@ -103,7 +104,7 @@ public Object createJdbcValue(Object domainValue, WrapperOptions options) throws
 	@Override
 	public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) throws SQLException {
 		assert embeddableMappingType != null;
-		return JsonHelper.deserialize( embeddableMappingType, (String) rawJdbcValue, false, options );
+		return JsonHelper.deserialize( embeddableMappingType, new StringJsonDocumentReader( (String)rawJdbcValue ), false, options );
 	}
 
 	protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
index c6cd1a0b5819..eceee2682a1b 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/AbstractJsonFormatMapper.java
@@ -7,7 +7,6 @@
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
 
-import java.io.IOException;
 import java.lang.reflect.Type;
 
 /**
@@ -38,23 +37,4 @@ public final <T> String toString(T value, JavaType<T> javaType, WrapperOptions w
 
 	protected abstract <T> String toString(T value, Type type);
 
-	@Override
-	public boolean supportsSourceType(Class<?> sourceType) {
-		return CharSequence.class.isAssignableFrom(sourceType);
-	}
-
-	@Override
-	public boolean supportsTargetType(Class<?> targetType) {
-		return String.class.isAssignableFrom( targetType );
-	}
-
-	@Override
-	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
-			throws IOException {
-	}
-
-	@Override
-	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-		return null;
-	}
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java
index bff9726cb0b5..e2f324b6719d 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/FormatMapper.java
@@ -49,16 +49,24 @@ public interface FormatMapper {
 	 * @param sourceType the source type
 	 * @return <code>true</code> if the type is supported, false otherwise.
 	 */
-	boolean supportsSourceType(Class<?> sourceType);
+	default boolean supportsSourceType(Class<?> sourceType) {
+		return false;
+	};
 
 	/**
 	 * Checks that this mapper supports a type as a target type.
 	 * @param targetType the target type
 	 * @return <code>true</code> if the type is supported, false otherwise.
 	 */
-	boolean supportsTargetType(Class<?> targetType);
+	default boolean supportsTargetType(Class<?> targetType) {
+		return false;
+	}
 
-	<T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options) throws IOException;
+	default <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options) throws IOException {
+		throw new UnsupportedOperationException( "Unsupportd target type " + target.getClass() );
+	};
 
-	<T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException;
+	default <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
+		throw new UnsupportedOperationException( "Unsupportd source type " + source.getClass() );
+	};
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
deleted file mode 100644
index 92bf2350b236..000000000000
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentReaderFactory.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SPDX-License-Identifier: Apache-2.0
- * Copyright Red Hat Inc. and Hibernate Authors
- */
-package org.hibernate.type.format;
-
-import oracle.sql.json.OracleJsonParser;
-
-/**
- * Factory class to get proper <code>JsonDocumentReader</code>.
- *
- * @author Emmanuel Jannetti
- */
-public class JsonDocumentReaderFactory {
-	/**
-	 * Gets a <code>JsonDocumentReader</code> appropriate to a given source.
-	 * Source can be a <code>String</code> or a <code>OracleJsonParser</code> instance
-	 * @param jsonSource the document source
-	 * @return the reader
-	 */
-	public static JsonDocumentReader getJsonDocumentReader(Object jsonSource) {
-		assert jsonSource != null : "jsonSource is null";
-
-		if (jsonSource instanceof String) {
-			return new StringJsonDocumentReader( (String)jsonSource );
-		}
-		if (jsonSource instanceof OracleJsonParser ) {
-			return new OsonDocumentReader( (OracleJsonParser)jsonSource );
-		}
-
-		throw new IllegalArgumentException("Unsupported type of JSON source " + jsonSource.getClass());
-	}
-}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
index 71b66cf9c9e3..93cef54dac51 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
@@ -75,13 +75,6 @@ public interface JsonDocumentWriter {
 	 */
 	JsonDocumentWriter stringValue(String value);
 
-	/**
-	 * Adds a new JSON element Number value.
-	 * @return this instance
-	 * @param value the element Number name.
-	 */
-	JsonDocumentWriter numberValue(Number value);
-
 	/**
 	 * Adds a JSON value to the document
 	 * @param value the value to be serialized
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
index dbee32e4ed2f..7f292bc0e16c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonValueJDBCTypeAdapter.java
@@ -12,6 +12,7 @@
 
 /**
  * Adapter for JSON value on given JDBC types.
+ * @author emmanuel Jannetti
  */
 public interface JsonValueJDBCTypeAdapter {
 	/**
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
index dd7405d8fb0e..bf01bfd1fd55 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
@@ -21,6 +21,7 @@
 
 /**
  * OSON-based implementation of <code>JsonDocumentReader</code>
+ * @author Emmanuel Jannetti
  */
 public class OsonDocumentReader implements JsonDocumentReader {
 
@@ -172,7 +173,7 @@ public boolean getBooleanValue() {
 	@Override
 	public <T> T getValue(JavaType<T> javaType, WrapperOptions options) {
 		if ( currentValue instanceof String ) {
-			if (javaType.equals(PrimitiveByteArrayJavaType.INSTANCE)) {
+			if (((String)currentValue).length() == 36 && javaType == PrimitiveByteArrayJavaType.INSTANCE) {
 				// be sure that we have only allowed characters.
 				// that may happen for string representation of UUID (i.e 53886a8a-7082-4879-b430-25cb94415be8) for instance
 				return javaType.fromEncodedString(  (((String) currentValue).replaceAll( "-","" )) );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index cfa9841bcb0a..804fd0c48f0e 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -101,20 +101,6 @@ public JsonDocumentWriter stringValue(String value) {
 		return this;
 	}
 
-
-	@Override
-	public JsonDocumentWriter numberValue(Number value) {
-		if (value instanceof BigDecimal) {
-			this.generator.write((BigDecimal) value );
-		} else if (value instanceof BigInteger) {
-			this.generator.write((BigInteger) value );
-		} else {
-			//fallback.
-			this.generator.write( value.longValue() );
-		}
-		return this;
-	}
-
 	@Override
 	public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
 		serializeValue(value, javaType, jdbcType, options);
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
index 6eda9b3ce7e6..74efb38c60d3 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocument.java
@@ -16,7 +16,7 @@ public abstract class StringJsonDocument {
 	 * When processing objects, values are stored as [,]"key":"value"[,]. we add separator when adding new key
 	 * When processing arrays, values are stored as [,]"value"[,]. we add separator when adding new value
 	 */
-	enum PROCESSING_STATE {
+	enum JsonProcessingState {
 		NONE,
 		STARTING_OBJECT, // object started but no value added
 		OBJECT_KEY_NAME, // We are processing an object key name
@@ -27,7 +27,7 @@ enum PROCESSING_STATE {
 		ARRAY // we are piling array values
 	}
 	// Stack of current processing states
-	protected StandardStack<PROCESSING_STATE> processingStates = new StandardStack<>();
+	protected final StandardStack<JsonProcessingState> processingStates = new StandardStack<>();
 
 
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index 2f749a08fa7f..f9fdc85d5d74 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -5,20 +5,31 @@
 package org.hibernate.type.format;
 
 import org.hibernate.type.descriptor.WrapperOptions;
+import org.hibernate.type.descriptor.java.BigDecimalJavaType;
+import org.hibernate.type.descriptor.java.BigIntegerJavaType;
+import org.hibernate.type.descriptor.java.BooleanJavaType;
+import org.hibernate.type.descriptor.java.ByteJavaType;
+import org.hibernate.type.descriptor.java.DoubleJavaType;
+import org.hibernate.type.descriptor.java.FloatJavaType;
+import org.hibernate.type.descriptor.java.IntegerJavaType;
 import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.descriptor.java.LongJavaType;
+import org.hibernate.type.descriptor.java.ShortJavaType;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.nio.CharBuffer;
 import java.util.NoSuchElementException;
 
 /**
  * Implementation of <code>JsonDocumentReader</code> for String representation of JSON objects.
  */
-public class StringJsonDocumentReader extends StringJsonDocument implements  JsonDocumentReader {
+public class StringJsonDocumentReader extends StringJsonDocument implements JsonDocumentReader {
 
-	private final CharBuffer json;
-	private final CharBuffer jsonValueWindow;
+	private final String jsonString;
+	private final int limit;
+	private int position;
+	private int jsonValueStart;
+	private int jsonValueEnd;
 
 	/**
 	 * Creates a new <code>StringJsonDocumentReader</code>
@@ -28,28 +39,29 @@ public StringJsonDocumentReader(String json) {
 		if (json == null) {
 			throw new IllegalArgumentException( "json cannot be null" );
 		}
-		this.json = CharBuffer.wrap( json.toCharArray() ).asReadOnlyBuffer();
-		this.jsonValueWindow = this.json.slice();
+		this.jsonString = json;
+		this.position = 0;
+		this.limit = jsonString.length();
+		this.jsonValueStart = 0;
+		this.jsonValueEnd = 0;
 	}
 
 	@Override
 	public boolean hasNext() {
-		// enough for now.
-		return this.json.hasRemaining();
+		return this.position < this.limit;
 	}
 
 	private void skipWhiteSpace() {
-		for (int i =  this.json.position(); i < this.json.limit(); i++ ) {
-			if (!Character.isWhitespace( this.json.get(i))) {
-				this.json.position(i);
+		for (;this.position  < this.limit; this.position++ ) {
+			if (!Character.isWhitespace( this.jsonString.charAt(this.position))) {
 				return;
 			}
 		}
 	}
 
 	private void resetValueWindow() {
-		this.jsonValueWindow.position(0);
-		this.jsonValueWindow.limit( 0);
+		this.jsonValueStart = 0;
+		this.jsonValueEnd = 0;
 	}
 
 	/**
@@ -58,61 +70,61 @@ private void resetValueWindow() {
 	 * @param marker the marker we just read
 	 */
 	private void moveStateMachine(StringJsonDocumentMarker marker) {
-		StringJsonDocument.PROCESSING_STATE currentState = this.processingStates.getCurrent();
+		JsonProcessingState currentState = this.processingStates.getCurrent();
 		switch (marker) {
 			case OBJECT_START:
-				if (currentState == PROCESSING_STATE.STARTING_ARRAY ) {
+				if ( currentState == JsonProcessingState.STARTING_ARRAY ) {
 					// move the state machine to ARRAY as we are adding something to it
-					this.processingStates.push(PROCESSING_STATE.ARRAY);
+					this.processingStates.push( JsonProcessingState.ARRAY);
 				}
-				this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
+				this.processingStates.push( JsonProcessingState.STARTING_OBJECT );
 				break;
 			case OBJECT_END:
-				assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT ||
-					this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_OBJECT;
-				if (this.processingStates.pop() == PROCESSING_STATE.OBJECT) {
-					assert this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_OBJECT;
+				assert this.processingStates.getCurrent() == JsonProcessingState.OBJECT ||
+					this.processingStates.getCurrent() == JsonProcessingState.STARTING_OBJECT;
+				if ( this.processingStates.pop() == JsonProcessingState.OBJECT) {
+					assert this.processingStates.getCurrent() == JsonProcessingState.STARTING_OBJECT;
 					this.processingStates.pop();
 				}
 				break;
 			case ARRAY_START:
-				this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
+				this.processingStates.push( JsonProcessingState.STARTING_ARRAY );
 				break;
 			case ARRAY_END:
-				assert this.processingStates.getCurrent() == PROCESSING_STATE.ARRAY ||
-					this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_ARRAY;
-				if (this.processingStates.pop() == PROCESSING_STATE.ARRAY) {
-					assert this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_ARRAY;
+				assert this.processingStates.getCurrent() == JsonProcessingState.ARRAY ||
+					this.processingStates.getCurrent() == JsonProcessingState.STARTING_ARRAY;
+				if ( this.processingStates.pop() == JsonProcessingState.ARRAY) {
+					assert this.processingStates.getCurrent() == JsonProcessingState.STARTING_ARRAY;
 					this.processingStates.pop();
 				}
 				break;
 			case SEPARATOR:
 				// While processing an object, following SEPARATOR that will a key
-				if (currentState == PROCESSING_STATE.OBJECT) {
-					this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
+				if ( currentState == JsonProcessingState.OBJECT) {
+					this.processingStates.push( JsonProcessingState.OBJECT_KEY_NAME );
 				}
 				break;
 			case KEY_VALUE_SEPARATOR:
 				// that's the start of an attribute value
-				assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT_KEY_NAME;
+				assert this.processingStates.getCurrent() == JsonProcessingState.OBJECT_KEY_NAME;
 				// flush the OBJECT_KEY_NAME
 				this.processingStates.pop();
-				assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT;
+				assert this.processingStates.getCurrent() == JsonProcessingState.OBJECT;
 				break;
 			case QUOTE:
 				switch ( currentState ) {
-					case PROCESSING_STATE.STARTING_ARRAY:
-						this.processingStates.push( PROCESSING_STATE.ARRAY );
+					case JsonProcessingState.STARTING_ARRAY:
+						this.processingStates.push( JsonProcessingState.ARRAY );
 						break;
-					case PROCESSING_STATE.STARTING_OBJECT:
-						this.processingStates.push( PROCESSING_STATE.OBJECT );
-						this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
+					case JsonProcessingState.STARTING_OBJECT:
+						this.processingStates.push( JsonProcessingState.OBJECT );
+						this.processingStates.push( JsonProcessingState.OBJECT_KEY_NAME );
 						break;
 				}
 				break;
 			case OTHER:
-				if (currentState == PROCESSING_STATE.STARTING_ARRAY) {
-					this.processingStates.push( PROCESSING_STATE.ARRAY );
+				if ( currentState == JsonProcessingState.STARTING_ARRAY) {
+					this.processingStates.push( JsonProcessingState.ARRAY );
 				}
 				break;
 		}
@@ -131,11 +143,10 @@ public JsonDocumentItemType next() {
 
 		while (hasNext()) {
 			skipWhiteSpace();
-			StringJsonDocumentMarker marker = StringJsonDocumentMarker.markerOf( this.json.get() );
+			StringJsonDocumentMarker marker = StringJsonDocumentMarker.markerOf( this.jsonString.charAt( this.position++ ) );
 			moveStateMachine( marker );
 			switch ( marker) {
 				case OBJECT_START:
-					//this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
 					resetValueWindow();
 					return JsonDocumentItemType.OBJECT_START;
 				case OBJECT_END:
@@ -144,7 +155,7 @@ public JsonDocumentItemType next() {
 					return JsonDocumentItemType.OBJECT_END;
 				case ARRAY_START:
 					resetValueWindow();
-					//this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
+					//this.processingStates.push( JsonProcessingState.STARTING_ARRAY );
 					return JsonDocumentItemType.ARRAY_START;
 				case ARRAY_END:
 					resetValueWindow();
@@ -162,33 +173,30 @@ public JsonDocumentItemType next() {
 					//        - if we just hit ':' that's a quoted value
 					//        - if we just hit ',' that's a quoted key
 					switch ( this.processingStates.getCurrent() ) {
-						case PROCESSING_STATE.STARTING_ARRAY:
-							//this.processingStates.push( PROCESSING_STATE.ARRAY );
+						case JsonProcessingState.STARTING_ARRAY:
+							//this.processingStates.push( JsonProcessingState.ARRAY );
 							return JsonDocumentItemType.VALUE;
-						case PROCESSING_STATE.ARRAY:
+						case JsonProcessingState.ARRAY:
 							return JsonDocumentItemType.VALUE;
-						case PROCESSING_STATE.STARTING_OBJECT:
-							//this.processingStates.push( PROCESSING_STATE.OBJECT );
-							//this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
+						case JsonProcessingState.STARTING_OBJECT:
+							//this.processingStates.push( JsonProcessingState.OBJECT );
+							//this.processingStates.push( JsonProcessingState.OBJECT_KEY_NAME );
 							return JsonDocumentItemType.VALUE_KEY;
-						case PROCESSING_STATE.OBJECT: // we are processing object attribute value elements
+						case JsonProcessingState.OBJECT: // we are processing object attribute value elements
 							return JsonDocumentItemType.VALUE;
-						case PROCESSING_STATE.OBJECT_KEY_NAME: // we are processing object elements key
+						case JsonProcessingState.OBJECT_KEY_NAME: // we are processing object elements key
 							return JsonDocumentItemType.VALUE_KEY;
 						default:
 							throw new IllegalStateException( "unexpected quote read in current processing state " +
 															this.processingStates.getCurrent() );
 					}
 				case KEY_VALUE_SEPARATOR:  // that's the start of an attribute value
-					//assert this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT_KEY_NAME;
+					//assert this.processingStates.getCurrent() == JsonProcessingState.OBJECT_KEY_NAME;
 					// flush the OBJECT_KEY_NAME
 					//this.processingStates.pop();
 					break;
 				case SEPARATOR:
 					// unless we are processing an array, following SEPARATOR that will a key
-//					if (this.processingStates.getCurrent() == PROCESSING_STATE.OBJECT) {
-//						this.processingStates.push( PROCESSING_STATE.OBJECT_KEY_NAME );
-//					}
 					break;
 				case OTHER:
 					// here we are in front of a boolean, a null or a numeric value.
@@ -198,15 +206,15 @@ public JsonDocumentItemType next() {
 					final int valueSize = consumeNonStringValue();
 					if (valueSize == -1) {
 						throw new IllegalStateException( "Unrecognized marker: " + StringJsonDocumentMarker.markerOf(
-								json.get( this.json.position() )));
+								this.jsonString.charAt( this.position )));
 					}
 					switch ( this.processingStates.getCurrent() ) {
-						case PROCESSING_STATE.ARRAY:
-						case PROCESSING_STATE.OBJECT:
-							return getUnquotedValueType(this.jsonValueWindow);
+						case JsonProcessingState.ARRAY:
+						case JsonProcessingState.OBJECT:
+							return getUnquotedValueType(this.jsonString.charAt( this.jsonValueStart));
 						default:
 							throw new IllegalStateException( "unexpected read ["+
-															this.jsonValueWindow.toString()+
+															this.jsonString.substring( this.jsonValueStart,this.jsonValueEnd )+
 															"] in current processing state " +
 															this.processingStates.getCurrent() );
 					}
@@ -220,12 +228,11 @@ public JsonDocumentItemType next() {
 	 * Gets the type of unquoted value.
 	 * We assume that the String value follows JSON specification. I.e unquoted value that starts with 't' can't be anything else
 	 * than <code>true</code>
-	 * @param jsonValueWindow the value
+	 * @param jsonValueChar the value
 	 * @return the type of the value
 	 */
-	private JsonDocumentItemType getUnquotedValueType(CharBuffer jsonValueWindow) {
-		final int size = jsonValueWindow.remaining();
-		switch(jsonValueWindow.charAt( 0 )) {
+	private JsonDocumentItemType getUnquotedValueType(char jsonValueChar) {
+		switch(jsonValueChar) {
 			case 't': {
 				//true
 				return JsonDocumentItemType.BOOLEAN_VALUE;
@@ -257,40 +264,48 @@ private JsonDocumentItemType getUnquotedValueType(CharBuffer jsonValueWindow) {
 	}
 
 	private void moveBufferPosition(int shift) {
-		this.json.position(this.json.position() + shift);
+		this.position += shift;
 	}
 
 	/**
-	 * Moves the current position to a given character.
+	 * Moves the current position to a given character, skipping any whitespace
+	 * We expect the character to be the next non-blank character in the sequence
 	 * @param character the character we should stop at.
 	 * @throws IllegalStateException if we encounter an unexpected character other than white spaces before the desired one.
 	 */
 
 	private void moveTo(char character) throws IllegalStateException {
-		this.json.mark();
-		while ( this.json.hasRemaining()) {
-			char c = this.json.get();
+		int pointer = this.position;
+		while ( pointer < this.limit) {
+			char c = this.jsonString.charAt( pointer );
 			if ( c == character) {
-				this.json.reset();
+				this.position = pointer == this.position?this.position:pointer - 1;
 				return;
 			}
 			if (!Character.isWhitespace(c)) {
 				// we did find an unexpected character
 				// let the exception raise
-				this.json.reset();
+				//this.json.reset();
 				break;
 			}
+			pointer++;
 		}
-		throw new IllegalStateException("Can't find character: " + character);
+		throw new IllegalStateException("character [" + character + "] is not the next non-blank character");
 	}
 
+	/**
+	 * Goes through the json string to locate a character.
+	 * @param character character to be found
+	 * @param escape character to be found
+	 * @return the position of the character or -1 if not found.
+	 */
 	private int locateCharacter(char character, char escape) {
 		assert character != escape;
-		this.json.mark();
-		int found = -1;
+		int pointer = this.position;
+
 		boolean escapeIsOn = false;
-		while ( this.json.hasRemaining()) {
-			final char c = this.json.get();
+		while ( pointer< this.limit) {
+			final char c = this.jsonString.charAt( pointer );
 			if (c == escape) {
 				escapeIsOn = true;
 			}
@@ -300,14 +315,14 @@ private int locateCharacter(char character, char escape) {
 						escapeIsOn = false;
 					}
 					else {
-						found = this.json.position() - 1;
-						break;
+						// found
+						return pointer;
 					}
 				}
 			}
+			pointer++;
 		}
-		this.json.reset();
-		return found;
+		return -1;
 	}
 
 	/**
@@ -317,8 +332,8 @@ private int locateCharacter(char character, char escape) {
 	private int consumeNonStringValue() {
 		int newViewLimit = 0;
 		boolean allGood = false;
-		for (int i =  this.json.position(); i < this.json.limit(); i++ ) {
-			char c = this.json.get(i);
+		for (int i =  this.position; i < this.limit; i++ ) {
+			char c = this.jsonString.charAt(i);
 			if ((StringJsonDocumentMarker.markerOf( c ) != StringJsonDocumentMarker.OTHER) ||
 				Character.isWhitespace( c )) {
 				// hit a JSON marker or a space.
@@ -330,12 +345,16 @@ private int consumeNonStringValue() {
 		}
 
 		if (allGood) {
-			this.jsonValueWindow.limit(newViewLimit);
-			this.jsonValueWindow.position( this.json.position() );
-			this.json.position(newViewLimit);
+			this.jsonValueEnd = newViewLimit;
+			this.jsonValueStart = position;
+			this.position = newViewLimit;
 		}
-		return allGood?(this.jsonValueWindow.remaining()):-1;
+		return allGood?(this.jsonValueEnd-this.jsonValueStart):-1;
 	}
+	/**
+	 * Consume a quotted value
+	 * @return the length of this value. can be 0, -1 in case of error
+	 */
 	private void consumeQuottedString() {
 
 		// be sure we are at a meaningful place
@@ -343,7 +362,7 @@ private void consumeQuottedString() {
 		moveTo( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 
 		// skip the quote we are positioned on.
-		this.json.get();
+		this.position++;
 
 		//locate ending quote
 		int endingQuote = locateCharacter( StringJsonDocumentMarker.QUOTE.getMarkerCharacter(), '\\');
@@ -351,40 +370,50 @@ private void consumeQuottedString() {
 			throw new IllegalStateException("Can't find ending quote of key name");
 		}
 
-		this.jsonValueWindow.limit( endingQuote );
-		this.jsonValueWindow.position(this.json.position());
-		this.json.position( endingQuote + 1);
+		this.jsonValueEnd = endingQuote;
+		this.jsonValueStart = position;
+
+		this.position =  endingQuote + 1;
 
 	}
 
+	/**
+	 * Ensures that the current state is on value.
+	 * @throws IllegalStateException if not on "value" state
+	 */
 	private void ensureValueState() throws IllegalStateException {
-		if ((this.processingStates.getCurrent() !=  PROCESSING_STATE.OBJECT ) &&
-			this.processingStates.getCurrent() !=  PROCESSING_STATE.ARRAY)  {
+		if ( (this.processingStates.getCurrent() != JsonProcessingState.OBJECT ) &&
+			this.processingStates.getCurrent() != JsonProcessingState.ARRAY)  {
 			throw new IllegalStateException( "unexpected processing state: " + this.processingStates.getCurrent() );
 		}
 	}
+	/**
+	 * Ensures that we have a value ready to be exposed. i.e we just consume one.
+	 * @throws IllegalStateException if no value available
+	 */
 	private void ensureAvailableValue() throws IllegalStateException {
-		if (this.jsonValueWindow.limit() == 0 ) {
+		if (this.jsonValueEnd == 0 ) {
 			throw new IllegalStateException( "No available value");
 		}
 	}
 
 	@Override
 	public String getObjectKeyName() {
-		if (this.processingStates.getCurrent() !=  PROCESSING_STATE.OBJECT_KEY_NAME ) {
+		if ( this.processingStates.getCurrent() != JsonProcessingState.OBJECT_KEY_NAME ) {
 			throw new IllegalStateException( "unexpected processing state: " + this.processingStates.getCurrent() );
 		}
 		ensureAvailableValue();
-		return this.jsonValueWindow.toString();
+		return this.jsonString.substring( this.jsonValueStart, this.jsonValueEnd);
 	}
+
 	@Override
 	public String getStringValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		if (hasEscape(this.jsonValueWindow)) {
-			return unescape(this.jsonValueWindow);
+		if ( currentValueHasEscape()) {
+			return unescape(this.jsonString, this.jsonValueStart , this.jsonValueEnd);
 		}
-		return this.jsonValueWindow.toString();
+		return this.jsonString.substring( this.jsonValueStart, this.jsonValueEnd);
 	}
 
 
@@ -392,79 +421,92 @@ public String getStringValue() {
 	public BigDecimal getBigDecimalValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return BigDecimal.valueOf( Long.valueOf(this.jsonValueWindow.toString()) );
+		return BigDecimalJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public BigInteger getBigIntegerValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return BigInteger.valueOf( Long.valueOf(this.jsonValueWindow.toString()) );
+		return BigIntegerJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public double getDoubleValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return Double.valueOf(this.jsonValueWindow.toString()).doubleValue();
+		return DoubleJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public float getFloatValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return Float.valueOf(this.jsonValueWindow.toString()).floatValue();
+		return FloatJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public long getLongValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return Long.valueOf(this.jsonValueWindow.toString()).longValue();
+		return LongJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public int getIntegerValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return Integer.valueOf(this.jsonValueWindow.toString()).intValue();
+		return IntegerJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public short getShortValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return Short.valueOf(this.jsonValueWindow.toString()).shortValue();
+		return ShortJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public byte getByteValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return Byte.valueOf(this.jsonValueWindow.toString()).byteValue();
+		return ByteJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public boolean getBooleanValue() {
 		ensureValueState();
 		ensureAvailableValue();
-		return Boolean.parseBoolean( this.jsonValueWindow.toString() );
+		return BooleanJavaType.INSTANCE.fromEncodedString( this.jsonString,this.jsonValueStart,this.jsonValueEnd );
 	}
 
 	@Override
 	public <T> T getValue(JavaType<T> javaType, WrapperOptions options) {
-		return javaType.fromEncodedString( this.jsonValueWindow.toString() );
+		return javaType.fromEncodedString( this.jsonString.subSequence( this.jsonValueStart,this.jsonValueEnd ));
 	}
 
-	private boolean hasEscape(CharBuffer jsonValueWindow) {
-		for (int i = 0;i<jsonValueWindow.remaining();i++) {
-			if (jsonValueWindow.charAt( i ) == '\\') return true;
+	/**
+	 * Walks through JSON value currently located on JSON string and check if escape is used
+	 *
+	 * @return <code>true</code> if escape is found
+	 */
+	private boolean currentValueHasEscape() {
+		for (int i = this.jsonValueStart; i<this.jsonValueEnd; i++) {
+			if (this.jsonString.charAt( i ) == '\\') return true;
 		}
 		return false;
 	}
-	private String unescape(CharBuffer string) {
-		final StringBuilder sb = new StringBuilder( string.remaining() );
-		for ( int i = 0; i < string.length(); i++ ) {
+
+	/**
+	 * Returns unescaped string
+	 * @param string the string to be unescaped
+	 * @param start the begin index within the string
+	 * @param end the end index within the string
+	 * @return the unescaped string
+	 */
+	private static String unescape(String string, int start, int end) {
+		final StringBuilder sb = new StringBuilder( end - start );
+		for ( int i = start; i < end; i++ ) {
 			final char c = string.charAt( i );
 			if ( c == '\\' ) {
 				i++;
@@ -502,4 +544,5 @@ private String unescape(CharBuffer string) {
 		return sb.toString();
 	}
 
+
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
index b537c05c7cc5..c4e0aae2c8b1 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -36,7 +36,7 @@ public class StringJsonDocumentWriter extends StringJsonDocument implements Json
 	 * @param appender the appender to receive the serialze JSON object
 	 */
 	public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
-		this.processingStates.push( PROCESSING_STATE.NONE );
+		this.processingStates.push( JsonProcessingState.NONE );
 		this.appender = appender;
 	}
 
@@ -46,20 +46,20 @@ public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
 	@Override
 	public JsonDocumentWriter startObject() {
 		// Note: startArray and startObject must not call moveProcessingStateMachine()
-		if (this.processingStates.getCurrent() == PROCESSING_STATE.STARTING_ARRAY) {
+		if ( this.processingStates.getCurrent() == JsonProcessingState.STARTING_ARRAY) {
 			// are we building an array of objects?
 			// i.e, [{},...]
-			// move to PROCESSING_STATE.ARRAY first
-			this.processingStates.push( PROCESSING_STATE.ARRAY);
+			// move to JsonProcessingState.ARRAY first
+			this.processingStates.push( JsonProcessingState.ARRAY);
 		}
-		else if (this.processingStates.getCurrent() == PROCESSING_STATE.ARRAY) {
+		else if ( this.processingStates.getCurrent() == JsonProcessingState.ARRAY) {
 			// That means that we ae building an array of object ([{},...])
 			// JSON object hee are treat as array item.
 			// -> add the marker first
 			this.appender.append(StringJsonDocumentMarker.SEPARATOR.getMarkerCharacter());
 		}
 		this.appender.append( StringJsonDocumentMarker.OBJECT_START.getMarkerCharacter());
-		this.processingStates.push( PROCESSING_STATE.STARTING_OBJECT );
+		this.processingStates.push( JsonProcessingState.STARTING_OBJECT );
 		return this;
 	}
 
@@ -69,7 +69,7 @@ else if (this.processingStates.getCurrent() == PROCESSING_STATE.ARRAY) {
 	@Override
 	public JsonDocumentWriter endObject() {
 		this.appender.append( StringJsonDocumentMarker.OBJECT_END.getMarkerCharacter() );
-		this.processingStates.push( PROCESSING_STATE.ENDING_OBJECT);
+		this.processingStates.push( JsonProcessingState.ENDING_OBJECT);
 		moveProcessingStateMachine();
 		return this;
 	}
@@ -79,7 +79,7 @@ public JsonDocumentWriter endObject() {
 	 */
 	@Override
 	public JsonDocumentWriter startArray() {
-		this.processingStates.push( PROCESSING_STATE.STARTING_ARRAY );
+		this.processingStates.push( JsonProcessingState.STARTING_ARRAY );
 		// Note: startArray and startObject do not call moveProcessingStateMachine()
 		this.appender.append( StringJsonDocumentMarker.ARRAY_START.getMarkerCharacter() );
 		return this;
@@ -91,7 +91,7 @@ public JsonDocumentWriter startArray() {
 	@Override
 	public JsonDocumentWriter endArray() {
 		this.appender.append( StringJsonDocumentMarker.ARRAY_END.getMarkerCharacter() );
-		this.processingStates.push( PROCESSING_STATE.ENDING_ARRAY);
+		this.processingStates.push( JsonProcessingState.ENDING_ARRAY);
 		moveProcessingStateMachine();
 		return this;
 	}
@@ -104,7 +104,7 @@ public JsonDocumentWriter objectKey(String key) {
 			throw new IllegalArgumentException( "key cannot be null or empty" );
 		}
 
-		if (this.processingStates.getCurrent().equals( PROCESSING_STATE.OBJECT )) {
+		if (this.processingStates.getCurrent().equals( JsonProcessingState.OBJECT )) {
 			// we have started an object, and we are adding an item key: we do add a separator.
 			this.appender.append( StringJsonDocumentMarker.SEPARATOR.getMarkerCharacter() );
 		}
@@ -122,7 +122,7 @@ public JsonDocumentWriter objectKey(String key) {
 	 * Separator is to separate array items or key/value pairs in an object.
 	 */
 	private void addItemsSeparator() {
-		if (this.processingStates.getCurrent().equals( PROCESSING_STATE.ARRAY )) {
+		if (this.processingStates.getCurrent().equals( JsonProcessingState.ARRAY )) {
 			// We started to serialize an array and already added item to it:add a separator anytime.
 			this.appender.append( StringJsonDocumentMarker.SEPARATOR.getMarkerCharacter() );
 		}
@@ -154,11 +154,11 @@ private void moveProcessingStateMachine() {
 		switch (this.processingStates.getCurrent()) {
 			case STARTING_OBJECT:
 				//after starting an object, we start adding key/value pairs
-				this.processingStates.push( PROCESSING_STATE.OBJECT );
+				this.processingStates.push( JsonProcessingState.OBJECT );
 				break;
 			case STARTING_ARRAY:
 				//after starting an array, we start adding value to it
-				this.processingStates.push( PROCESSING_STATE.ARRAY );
+				this.processingStates.push( JsonProcessingState.ARRAY );
 				break;
 			case ENDING_ARRAY:
 				// when ending an array, we have one or two states.
@@ -167,9 +167,9 @@ private void moveProcessingStateMachine() {
 				// first pop ENDING_ARRAY
 				this.processingStates.pop();
 				// if we have ARRAY, so that's not an empty array. pop that state
-				if (this.processingStates.getCurrent().equals( PROCESSING_STATE.ARRAY ))
+				if (this.processingStates.getCurrent().equals( JsonProcessingState.ARRAY ))
 					this.processingStates.pop();
-				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_ARRAY );
+				assert this.processingStates.pop().equals( JsonProcessingState.STARTING_ARRAY );
 				break;
 			case ENDING_OBJECT:
 				// when ending an object, we have one or two states.
@@ -178,9 +178,9 @@ private void moveProcessingStateMachine() {
 				// first pop ENDING_OBJECT
 				this.processingStates.pop();
 				// if we have OBJECT, so that's not an empty object. pop that state
-				if (this.processingStates.getCurrent().equals( PROCESSING_STATE.OBJECT ))
+				if (this.processingStates.getCurrent().equals( JsonProcessingState.OBJECT ))
 					this.processingStates.pop();
-				assert this.processingStates.pop().equals( PROCESSING_STATE.STARTING_OBJECT );
+				assert this.processingStates.pop().equals( JsonProcessingState.STARTING_OBJECT );
 				break;
 			default:
 				//nothing to do for the other ones.
@@ -217,18 +217,6 @@ public JsonDocumentWriter stringValue(String value) {
 		return this;
 	}
 
-	@Override
-	public JsonDocumentWriter numberValue(Number value) {
-		if (value == null ) {
-			throw new IllegalArgumentException( "value cannot be null" );
-		}
-		addItemsSeparator();
-		this.appender.append( value.toString() );
-		moveProcessingStateMachine();
-		return this;
-	}
-
-
 	@Override
 	public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
 		addItemsSeparator();
@@ -360,7 +348,7 @@ private  void convertedBasicValueToString(
 			case SqlTypes.ARRAY:
 			case SqlTypes.JSON_ARRAY:
 				// Caller handles this. We should never end up here actually.
-				break;
+				throw new IllegalStateException("unexpected JSON array type");
 			default:
 				throw new UnsupportedOperationException( "Unsupported JdbcType nested in JSON: " + jdbcType );
 		}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
index 8b70c7138d98..a7c84e6c694c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java
@@ -204,27 +204,6 @@ else if ( javaType.getJavaTypeClass().isArray() ) {
 		return writeValueAsString( value, javaType, javaType.getJavaType() );
 	}
 
-	@Override
-	public boolean supportsSourceType(Class<?> sourceType) {
-		return CharSequence.class.isAssignableFrom(sourceType);
-	}
-
-	@Override
-	public boolean supportsTargetType(Class<?> targetType) {
-		return String.class.isAssignableFrom( targetType );
-	}
-
-	@Override
-	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
-			throws IOException {
-		target = toString(value, javaType, options);
-	}
-
-	@Override
-	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-		return fromString((CharSequence) source, javaType, options);
-	}
-
 	private <T> String writeValueAsString(Object value, JavaType<T> javaType, Type type) {
 		try {
 			return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
index 0c4981b610eb..d890c9215b15 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
@@ -42,11 +42,11 @@ public void testArray() {
 		StringBuilder sb = new StringBuilder();
 		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
 		writer.startArray();
-		writer.numberValue( Integer.valueOf( 1 ) );
-		writer.numberValue( Integer.valueOf( 2 ) );
-		writer.numberValue( Integer.valueOf( 3 ) );
+		writer.booleanValue( false );
+		writer.booleanValue( true );
+		writer.booleanValue( false );
 		writer.endArray();
-		assertEquals( "[1,2,3]" , writer.toString() );
+		assertEquals( "[false,true,false]" , writer.toString() );
 	}
 
 	@Test
@@ -54,13 +54,12 @@ public void testMixedArrayDocument() {
 		StringBuilder sb = new StringBuilder();
 		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
 		writer.startArray();
-		writer.numberValue( Integer.valueOf( 1 ) );
 		writer.nullValue();
 		writer.booleanValue( false );
 		writer.stringValue( "foo" );
 		writer.endArray();
 		assertEqualsIgnoreSpace( """
-									[1,null,false,"foo"]
+									[null,false,"foo"]
 							""" , writer.toString() );
 	}
 	@Test
@@ -88,8 +87,6 @@ public void testNonStringValueDocument() {
 		writer.startObject();
 		writer.objectKey( "aNull" );
 		writer.nullValue();
-		writer.objectKey( "aNumber" );
-		writer.numberValue( Integer.valueOf( 12 ) );
 		writer.objectKey( "aBoolean" );
 		writer.booleanValue( true );
 		writer.endObject();
@@ -97,8 +94,7 @@ public void testNonStringValueDocument() {
 		assertEqualsIgnoreSpace( """
 						{
 						"aNull":null,
-						"aNumber" : 12 ,
-							"aBoolean" : true
+						"aBoolean" : true
 						}
 						""" , writer.toString() );
 
@@ -115,16 +111,16 @@ public void testArrayValueDocument() {
 		writer.endArray();
 		writer.objectKey( "anArray" );
 		writer.startArray();
-		writer.numberValue( Integer.valueOf( 1 ) );
-		writer.numberValue( Integer.valueOf( 2 ) );
-		writer.numberValue( Integer.valueOf( 3 ) );
+		writer.stringValue( "1" );
+		writer.stringValue( "2" );
+		writer.stringValue( "3" );
 		writer.endArray();
 		writer.endObject();
 
 		assertEqualsIgnoreSpace(  """
 				{
 				"anEmptyArray" : [],
-				"anArray" : [1,2,3]
+				"anArray" : ["1","2","3"]
 				}
 				""", writer.toString() );
 	}
@@ -133,12 +129,12 @@ public void testObjectArrayMultipleValueDocument() {
 		StringBuilder sb = new StringBuilder();
 		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
 		writer.startObject();
-		writer.objectKey( "anArray" ).startArray().numberValue( Integer.valueOf( 1 ) ).nullValue().stringValue( "2" ).startObject()
+		writer.objectKey( "anArray" ).startArray().nullValue().stringValue( "2" ).startObject()
 		.objectKey( "foo" ).stringValue( "bar" ).endObject().endArray().endObject();
 
 		assertEqualsIgnoreSpace( """
 					{
-						"anArray" : [1, null, "2" , {\"foo\":\"bar\"}  ]
+						"anArray" : [null, "2" , {\"foo\":\"bar\"}  ]
 					}
 					""" , sb.toString() );
 
@@ -149,21 +145,20 @@ public void testNestedDocument() {
 		StringBuilder sb = new StringBuilder();
 		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
 		writer.startObject().objectKey( "nested" ).startObject()
-				.objectKey( "converted_gender" ).stringValue( "M" ).objectKey( "theInteger" ).numberValue( Integer.valueOf( -1 ) ).endObject()
+				.objectKey( "converted_gender" ).stringValue( "M" )
+				.endObject()
 				.objectKey( "doubleNested" ).startObject()
 				.objectKey( "theNested" ).startObject()
 				.objectKey( "theLeaf" )
 				.startObject().objectKey( "stringField" ).stringValue( "String \"<abc>A&B</abc>\"" ).endObject()
 				.endObject()
 				.endObject()
-				.objectKey( "integerField" ).numberValue( Integer.valueOf( 10 ) )
 				.endObject();
 
 		assertEqualsIgnoreSpace( """
 							{
 							"nested": {
-								"converted_gender": "M",
-								"theInteger": -1
+								"converted_gender": "M"
 							},
 							"doubleNested": {
 								"theNested": {
@@ -171,8 +166,7 @@ public void testNestedDocument() {
 										"stringField": "String \\"<abc>A&B</abc>\\""
 									}
 								}
-							},
-							"integerField": 10
+							}
 							}
 						""",writer.toString());
 

From 8015c4923464a7b06c405472acd64477d755377c Mon Sep 17 00:00:00 2001
From: BidyadharM <bidyadhar.mohanty@oracle.com>
Date: Thu, 27 Mar 2025 13:49:23 +0530
Subject: [PATCH 53/81] HHH-17404 : Address review comment to remove try-catch
 for SqlTypes.TIMESTAMP_WITH_TIMEZONE.

---
 .../hibernate/type/format/OsonDocumentWriter.java    | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index 804fd0c48f0e..fcc6cb09038f 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -191,16 +191,8 @@ private void serializeValue(Object value,
 				generator.write(writeTimeStamp);
 				break;
 			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
-				try {
-					OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
-					generator.write( dateTime );
-				}
-				catch (Exception e) {
-					Timestamp tswtz = javaType.unwrap( value, Timestamp.class, options );
-					TIMESTAMP TSWTZ = new TIMESTAMP(tswtz);
-					OracleJsonTimestamp writeTimeStampWTZ = new OracleJsonTimestampImpl(TSWTZ.shareBytes());
-					generator.write(writeTimeStampWTZ);
-				}
+				OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
+				generator.write( dateTime );
 				break;
 			case SqlTypes.TIMESTAMP_UTC:
 				OffsetDateTime odt = javaType.unwrap( value, OffsetDateTime.class, options );

From 32cfdfc728d310aa5d0570d5b5a24201ec11461b Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Thu, 27 Mar 2025 19:33:29 +0100
Subject: [PATCH 54/81] HHH-17404 add flag to disable OSON extension

---
 .../cfg/DialectSpecificSettings.java          |  6 +-
 .../type/descriptor/jdbc/JsonHelper.java      | 99 ++++++++-----------
 .../type/format/StringJsonDocumentReader.java | 34 ++++---
 .../jackson/JacksonJsonFormatMapper.java      | 28 ------
 .../resolver/DialectSpecificConfigTest.java   | 23 +++++
 5 files changed, 86 insertions(+), 104 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java
index 8e9db591048c..114faabf6303 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java
@@ -53,10 +53,10 @@ public interface DialectSpecificSettings {
 	String ORACLE_USE_BINARY_FLOATS = "hibernate.dialect.oracle.use_binary_floats";
 
 	/**
-	 * Specifies whether usage of the Oracle JSON binary format (aka OSON) should be disabled.
+	 * Specifies whether usage of the Oracle JSON binary format (also known as OSON) should be disabled.
 	 * <p>
-	 * Starting to 21c, if the ojdbc-provider-jackson-oson extension is available. JSON data in an oracle
-	 * database are stored using the OSON binary format. This setting can be used to fallback to the old implementation
+	 * Starting in 21c, if the ojdbc-provider-jackson-oson extension is available, JSON data in an oracle
+	 * database is stored using the OSON binary format. This setting can be used to fallback to the old implementation
 	 * based on String serialization.
 	 *
 	 * @settingDefault {@code false}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 98c6a7b3b3e9..5859173f76df 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -69,39 +69,10 @@ public static void serializeArray(MappingType elementMappingType, Object[] value
 		}
 		for ( Object value : values ) {
 			try {
-				if (value == null) {
-					writer.nullValue();
-				}
-				else if ( elementMappingType instanceof EmbeddableMappingType ) {
-					JsonHelper.serialize( (EmbeddableMappingType) elementMappingType, value, options, writer );
-				} else if ( elementMappingType instanceof BasicType<?> ) {
-					//noinspection unchecked
-					final BasicType<Object> basicType = (BasicType<Object>) elementMappingType;
-
-					if ( isArrayType(basicType.getJdbcType())) {
-						final int length = Array.getLength( value );
-						if ( length != 0 ) {
-							//noinspection unchecked
-							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);
-							}
-						}
-					}
-					else {
-						writer.serializeJsonValue(basicType.convertToRelationalValue( value),
-								(JavaType<Object>)basicType.getJdbcJavaType(),basicType.getJdbcType(), options);
-					}
-				}
-				else {
-					throw new UnsupportedOperationException( "Support for mapping type not yet implemented: " + elementMappingType.getClass().getName() );
-				}
+				serialize(elementMappingType, value, options, writer);
 			}
 			catch (IOException e) {
-				// TODO : do better than this
-				throw new RuntimeException( e );
+				throw new IllegalArgumentException( "Could not serialize JSON array value" , e );
 			}
 		}
 		writer.endArray();
@@ -158,6 +129,42 @@ public static void serialize(EmbeddableMappingType embeddableMappingType,
 		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<?> ) {
+			//noinspection unchecked
+			final BasicType<Object> basicType = (BasicType<Object>) mappedType;
+
+			if ( isArrayType(basicType.getJdbcType())) {
+				final int length = Array.getLength( value );
+				writer.startArray();
+				if ( length != 0 ) {
+					//noinspection unchecked
+					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)
@@ -175,40 +182,18 @@ private static void serializeMapping(EmbeddableMappingType embeddableMappingType
 			if ( attributeMapping instanceof SelectableMapping ) {
 				final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
 				writer.objectKey( name );
-				if (values[i] == null) {
-					writer.nullValue();
-				}
-				else if (attributeMapping.getMappedType() instanceof BasicType<?>) {
-					final BasicType<Object> basicType = (BasicType<Object>) attributeMapping.getMappedType();
-					if ( isArrayType(basicType.getJdbcType())) {
-						final int length = Array.getLength( values[i] );
-						writer.startArray();
-						if ( length != 0 ) {
-							//noinspection unchecked
-							final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) basicType.getJdbcJavaType() ).getElementJavaType();
-							final JdbcType elementJdbcType = ( (ArrayJdbcType) basicType.getJdbcType() ).getElementJdbcType();
-							final Object domainArray = basicType.convertToRelationalValue(   values[i] );
-							for ( int j = 0; j < length; j++ ) {
-								writer.serializeJsonValue(Array.get(domainArray,j), elementJavaType, elementJdbcType, options);
-							}
-						}
-						writer.endArray();
-					}
-					else {
-						writer.serializeJsonValue(basicType.convertToRelationalValue( values[i]),
-								(JavaType<Object>)basicType.getJdbcJavaType(),basicType.getJdbcType(), options);
-					}
-				}
-				else if ( attributeMapping.getMappedType() instanceof EmbeddableMappingType ) {
+
+				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 ) {
-					//writer.nullValue();
 					continue;
 				}
 				final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index f9fdc85d5d74..3f04d6561c82 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -25,6 +25,8 @@
  */
 public class StringJsonDocumentReader extends StringJsonDocument implements JsonDocumentReader {
 
+	private static final char ESCAPE_CHAR = '\\';
+
 	private final String jsonString;
 	private final int limit;
 	private int position;
@@ -294,30 +296,30 @@ private void moveTo(char character) throws IllegalStateException {
 	}
 
 	/**
-	 * Goes through the json string to locate a character.
+	 * Goes through the JSON string to locate a character.
+	 * Escaped characters are taken into account.
+	 * ex: on 'AB\"C"' this method returns 5 (not 3)
+	 *
 	 * @param character character to be found
-	 * @param escape character to be found
 	 * @return the position of the character or -1 if not found.
 	 */
-	private int locateCharacter(char character, char escape) {
-		assert character != escape;
+	private int locateCharacter(char character) {
 		int pointer = this.position;
 
-		boolean escapeIsOn = false;
-		while ( pointer< this.limit) {
+		while ( pointer < this.limit) {
 			final char c = this.jsonString.charAt( pointer );
-			if (c == escape) {
-				escapeIsOn = true;
+			if (c == ESCAPE_CHAR) {
+				// We encountered an escape character.
+				// We should just skip the next one as it is either the expected character
+				// but as escaped one, we should ignore it, either this is something else
+				// and we should ignore it also
+				pointer += 2;
+				continue;
 			}
 			else {
 				if ( c == character ) {
-					if (escapeIsOn) {
-						escapeIsOn = false;
-					}
-					else {
-						// found
-						return pointer;
-					}
+					// found
+					return pointer;
 				}
 			}
 			pointer++;
@@ -365,7 +367,7 @@ private void consumeQuottedString() {
 		this.position++;
 
 		//locate ending quote
-		int endingQuote = locateCharacter( StringJsonDocumentMarker.QUOTE.getMarkerCharacter(), '\\');
+		int endingQuote = locateCharacter( StringJsonDocumentMarker.QUOTE.getMarkerCharacter());
 		if (endingQuote == -1) {
 			throw new IllegalStateException("Can't find ending quote of key name");
 		}
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index 5e9f0f232afd..b71107c0949e 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -4,14 +4,11 @@
  */
 package org.hibernate.type.format.jackson;
 
-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.io.IOException;
 import java.lang.reflect.Type;
 
 /**
@@ -51,29 +48,4 @@ public <T> String toString(T value, Type type) {
 			throw new IllegalArgumentException( "Could not serialize object of java type: " + type, e );
 		}
 	}
-
-	@Override
-	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
-			throws IOException {
-
-		try {
-			objectMapper.writerFor(
-					objectMapper.constructType( javaType.getJavaType() ) ).writeValueAsString( value );
-		}
-		catch (JsonProcessingException e) {
-			throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType.getJavaType(), e );
-		}
-
-	}
-
-	@Override
-	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-		try {
-			return objectMapper.readValue( ((CharSequence)source).toString(), objectMapper.constructType( javaType.getJavaType() ) );
-		}
-		catch (JsonProcessingException e) {
-			throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType.getJavaType(), e );
-		}
-
-	}
 }
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java
index 2100ce863849..5002351bcce1 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java
@@ -24,6 +24,7 @@
 import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_NO_BACKSLASH_ESCAPES;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
+import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.cfg.DialectSpecificSettings.SYBASE_ANSI_NULL;
 import static org.hibernate.dialect.DatabaseVersion.NO_VERSION;
 
@@ -55,6 +56,28 @@ public void testOracleIsAutonomous() {
 		assertThat( ( (OracleDialect) dialect ).isAutonomous() ).isTrue();
 	}
 
+	@Test
+	public void testOracleIsOsonEnabled() {
+		final Dialect dialect = resolveDialect(
+				"Oracle",
+				values -> values.put( "emptyOne", "true" )
+		);
+
+		assertThat( dialect ).isInstanceOf( OracleDialect.class );
+		assertThat( ( (OracleDialect) dialect ).isOracleOsonDisabled() ).isFalse();
+	}
+
+	@Test
+	public void testOracleIsOsonDisabled() {
+		final Dialect dialect = resolveDialect(
+				"Oracle",
+				values -> values.put( ORACLE_OSON_DISABLED, "true" )
+		);
+
+		assertThat( dialect ).isInstanceOf( OracleDialect.class );
+		assertThat( ( (OracleDialect) dialect ).isOracleOsonDisabled() ).isTrue();
+	}
+
 	@Test
 	public void testSybaseASEIsAnsiNull() {
 		final Dialect dialect = resolveDialect(

From 13f45eb8d703b05b0598dd07680e17c0dc6c5812 Mon Sep 17 00:00:00 2001
From: BidyadharM <bidyadhar.mohanty@oracle.com>
Date: Fri, 28 Mar 2025 14:53:07 +0530
Subject: [PATCH 55/81] HHH-17404 : Since thhe UUID is stored as string ,
 retrieve it accordingly.

---
 .../dialect/aggregate/OracleAggregateSupport.java      | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index a36edcf8f1b7..0eb12dbdef3e 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -209,12 +209,10 @@ public String aggregateComponentCustomReadExpression(
 									);
 								}
 							case UUID:
-								if (this.dateTypesStoreAsString) {
-									return template.replace(
-											placeholder,
-											"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
-									);
-								}
+								return template.replace(
+										placeholder,
+										"hextoraw(replace(json_value(" + parentPartExpression + columnExpression + "'),'-',''))"
+								);
 							case BINARY:
 							case VARBINARY:
 							case LONG32VARBINARY:

From 1071b7618f2a4abdb1d6d797a506812ae2883f4a Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Tue, 1 Apr 2025 17:59:39 +0200
Subject: [PATCH 56/81] HHH-17404 remove use of reflection

---
 .../org/hibernate/dialect/OracleDialect.java  | 12 +++-
 .../OracleOsonJacksonArrayJdbcType.java       | 20 +------
 .../dialect/OracleOsonJacksonJdbcType.java    | 57 +++++++------------
 .../dialect/OracleServerConfiguration.java    | 16 +-----
 .../aggregate/OracleAggregateSupport.java     |  2 +-
 .../type/format/StringJsonDocumentReader.java | 15 +++--
 .../jackson/JacksonOsonFormatMapper.java      | 24 +-------
 .../resolver/DialectSpecificConfigTest.java   | 23 --------
 .../src/main/groovy/local.java-module.gradle  |  3 +
 settings.gradle                               |  2 +
 10 files changed, 55 insertions(+), 119 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 54ece231e171..0d1b586b042a 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -12,6 +12,7 @@
 import org.hibernate.Timeouts;
 import org.hibernate.boot.model.FunctionContributions;
 import org.hibernate.boot.model.TypeContributions;
+import org.hibernate.cfg.MappingSettings;
 import org.hibernate.dialect.aggregate.AggregateSupport;
 import org.hibernate.dialect.aggregate.OracleAggregateSupport;
 import org.hibernate.dialect.function.CommonFunctionFactory;
@@ -105,6 +106,7 @@
 import org.hibernate.type.descriptor.sql.internal.NamedNativeOrdinalEnumDdlTypeImpl;
 import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
 import org.hibernate.type.format.jackson.JacksonIntegration;
+import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
 import org.hibernate.type.spi.TypeConfiguration;
 
 import java.sql.CallableStatement;
@@ -121,6 +123,7 @@
 
 import static java.util.regex.Pattern.CASE_INSENSITIVE;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS;
+import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.dialect.DialectLogging.DIALECT_MESSAGE_LOGGER;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getArrayJdbcTypeConstructor;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getNestedTableJdbcTypeConstructor;
@@ -268,9 +271,16 @@ public boolean isExtended() {
 	public boolean isApplicationContinuity() {
 		return applicationContinuity;
 	}
-
 	public boolean isOracleOsonDisabled() {return isOracleOsonDisabled;}
 
+	private static boolean isJacksonJsonFormatMapper(ConfigurationService configService) {
+		// Mirror the behavior of SessionFactoryOptionsBuilder#determineJsonFormatMapper
+		final String mapperName = configService.getSetting( MappingSettings.JSON_FORMAT_MAPPER,
+				StandardConverters.STRING,JacksonJsonFormatMapper.SHORT_NAME);
+		return JacksonJsonFormatMapper.SHORT_NAME.equalsIgnoreCase( mapperName )
+			|| mapperName == null && JacksonIntegration.getJsonJacksonFormatMapperOrNull() != null;
+	}
+
 	@Override
 	protected DatabaseVersion getMinimumSupportedVersion() {
 		return MINIMUM_VERSION;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index be465fab4378..d847e534d771 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -4,9 +4,9 @@
  */
 package org.hibernate.dialect;
 
-import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
+import oracle.jdbc.provider.oson.OsonFactory;
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
@@ -24,7 +24,6 @@
 import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.OsonDocumentWriter;
-import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
@@ -45,20 +44,7 @@
 public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
 
 
-	private static final Object osonFactory;
-	static {
-		try {
-			Class osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
-			osonFactory = osonFactoryKlass.getDeclaredConstructor().newInstance();
-		}
-		catch (Exception | LinkageError e) {
-			// should not happen as OracleOsonJacksonJdbcType is loaded
-			// only when Oracle OSON JDBC extension is present
-			// see OracleDialect class.
-			throw new ExceptionInInitializerError( "OracleOsonJacksonJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
-		}
-	}
-
+	private static final OsonFactory osonFactory = new OsonFactory();
 
 	public OracleOsonJacksonArrayJdbcType(JdbcType elementJdbcType) {
 		super(elementJdbcType);
@@ -134,7 +120,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getJsonFormatMapper();
-				JsonParser osonParser = ((JsonFactory)osonFactory).createParser( osonBytes );
+				JsonParser osonParser = osonFactory.createParser( osonBytes );
 				return mapper.readFromSource(  getJavaType(), osonParser, options);
 			}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 55f6dfd66f5b..bfe5b5dfb0e4 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -9,6 +9,7 @@
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
 import oracle.jdbc.driver.DatabaseError;
+import oracle.jdbc.provider.oson.OsonFactory;
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
@@ -27,11 +28,10 @@
 import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.OsonDocumentReader;
 import org.hibernate.type.format.OsonDocumentWriter;
-import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -50,20 +50,7 @@ public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
 
 	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonJdbcType.class );
 
-	private static final Object osonFactory;
-	static {
-		try {
-			Class osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
-			osonFactory = osonFactoryKlass.getDeclaredConstructor().newInstance();
-		}
-		catch (Exception | LinkageError e) {
-			// should not happen as OracleOsonJacksonJdbcType is loaded
-			// only when Oracle OSON JDBC extension is present
-			// see OracleDialect class.
-			throw new ExceptionInInitializerError( "OracleOsonJacksonJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
-		}
-	}
-
+	private static final OsonFactory osonFactory = new OsonFactory();
 
 	private OracleOsonJacksonJdbcType(EmbeddableMappingType embeddableMappingType) {
 		super( embeddableMappingType );
@@ -106,7 +93,7 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
 				}
 
 				ByteArrayOutputStream out = new ByteArrayOutputStream();
-				try (JsonGenerator osonGen = ((JsonFactory)osonFactory).createGenerator( out )) {
+				try (JsonGenerator osonGen = osonFactory.createGenerator( out )) {
 					mapper.writeToTarget( value, javaType, osonGen, options );
 				}
 				return out.toByteArray();
@@ -176,18 +163,6 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
 				}
 			}
 
-			private X doExtraction(byte[] bytes,  WrapperOptions options) throws SQLException {
-				if ( bytes == null ) {
-					return null;
-				}
-
-				try {
-					return fromOson( new ByteArrayInputStream(bytes) ,options);
-				}
-				catch (Exception e) {
-					throw new SQLException( e );
-				}
-			}
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
 				if ( datum == null ) {
 					return null;
@@ -209,9 +184,13 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// this may happen if we are fetching data from an existing schema
-						// that use CBLOB for JSON column
+						// that use CBLOB for JSON column In that case we assume byte are
+						// UTF-8 bytes (i.e not OSON)
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return doExtraction(rs.getBytes( paramIndex ), options);
+						return OracleOsonJacksonJdbcType.this.fromString(
+								new String( rs.getBytes( paramIndex ), StandardCharsets.UTF_8 ),
+								getJavaType(),
+								options);
 					} else {
 						throw exc;
 					}
@@ -226,9 +205,13 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// this may happen if we are fetching data from an existing schema
-						// that use CBLOB for JSON column
+						// that use CBLOB for JSON column. In that case we assume byte are
+						// UTF-8 bytes (i.e not OSON)
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return doExtraction(statement.getBytes( index ), options);
+						return OracleOsonJacksonJdbcType.this.fromString(
+								new String( statement.getBytes( index ), StandardCharsets.UTF_8 ),
+								getJavaType(),
+								options);
 					} else {
 						throw exc;
 					}
@@ -244,9 +227,13 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// this may happen if we are fetching data from an existing schema
-						// that use CBLOB for JSON column
+						// that use CBLOB for JSON column In that case we assume byte are
+						//						// UTF-8 bytes (i.e not OSON)
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return doExtraction(statement.getBytes( name ), options);
+						return OracleOsonJacksonJdbcType.this.fromString(
+								new String( statement.getBytes( name ), StandardCharsets.UTF_8 ),
+								getJavaType(),
+								options);
 					} else {
 						throw exc;
 					}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java
index c2f41088fe2d..a9caf5c387b1 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java
@@ -20,7 +20,6 @@
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_APPLICATION_CONTINUITY;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
-import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
 
 /**
@@ -34,7 +33,6 @@ public class OracleServerConfiguration {
 	private final boolean autonomous;
 	private final boolean extended;
 	private final boolean applicationContinuity;
-	private final boolean osonDisabled;
 	private final int driverMajorVersion;
 	private final int driverMinorVersion;
 
@@ -50,10 +48,6 @@ public boolean isApplicationContinuity() {
 		return applicationContinuity;
 	}
 
-	public boolean isOSONEnabled() {
-		return osonDisabled;
-	}
-
 	public int getDriverMajorVersion() {
 		return driverMajorVersion;
 	}
@@ -63,7 +57,7 @@ public int getDriverMinorVersion() {
 	}
 
 	public OracleServerConfiguration(boolean autonomous, boolean extended) {
-		this( autonomous, extended, false, false, 19, 0 );
+		this( autonomous, extended, false, 19, 0 );
 	}
 
 	public OracleServerConfiguration(
@@ -71,20 +65,18 @@ public OracleServerConfiguration(
 			boolean extended,
 			int driverMajorVersion,
 			int driverMinorVersion) {
-		this( autonomous, extended, false, false, driverMajorVersion, driverMinorVersion );
+		this( autonomous, extended, false, driverMajorVersion, driverMinorVersion );
 	}
 
 	public OracleServerConfiguration(
 			boolean autonomous,
 			boolean extended,
 			boolean applicationContinuity,
-			boolean osonDisabled,
 			int driverMajorVersion,
 			int driverMinorVersion) {
 		this.autonomous = autonomous;
 		this.extended = extended;
 		this.applicationContinuity = applicationContinuity;
-		this.osonDisabled = osonDisabled;
 		this.driverMajorVersion = driverMajorVersion;
 		this.driverMinorVersion = driverMinorVersion;
 	}
@@ -96,12 +88,10 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut
 		final boolean defaultExtended = getBoolean( ORACLE_EXTENDED_STRING_SIZE, configuration, false );
 		final boolean defaultAutonomous =  getBoolean( ORACLE_AUTONOMOUS_DATABASE, configuration, false );
 		final boolean defaultContinuity = getBoolean( ORACLE_APPLICATION_CONTINUITY, configuration, false );
-		final boolean defaultOsonDisabled = getBoolean( ORACLE_OSON_DISABLED , configuration, false );
 
 		boolean extended;
 		boolean autonomous;
 		boolean applicationContinuity;
-		boolean osonDisabled = defaultOsonDisabled;
 
 		int majorVersion;
 		int minorVersion;
@@ -138,7 +128,7 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut
 			}
 		}
 
-		return new OracleServerConfiguration( autonomous, extended, applicationContinuity, osonDisabled,majorVersion, minorVersion );
+		return new OracleServerConfiguration( autonomous, extended, applicationContinuity, majorVersion, minorVersion );
 	}
 
 	private static boolean isExtended(Statement statement) {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index 0eb12dbdef3e..d4c296ff0c13 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -72,7 +72,7 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
 	private final JsonSupport jsonSupport;
 	private final boolean dateTypesStoreAsString;
 
-	OracleAggregateSupport(boolean checkConstraintSupport, JsonSupport jsonSupport, boolean dateTypesStoreAsString) {
+	private OracleAggregateSupport(boolean checkConstraintSupport, JsonSupport jsonSupport, boolean dateTypesStoreAsString) {
 		this.checkConstraintSupport = checkConstraintSupport;
 		this.jsonSupport = jsonSupport;
 		// this flag tell us if data is serialized/de-serialized as String. As opposed to using OSON
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index 3f04d6561c82..d5336103f002 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -166,7 +166,7 @@ public JsonDocumentItemType next() {
 				case QUOTE:  // that's the start of an attribute key or a quoted value
 					// put back the quote
 					moveBufferPosition(-1);
-					consumeQuottedString();
+					consumeQuotedString();
 					// That's a quote:
 					//   - if we are at the beginning of an array that's a quoted value
 					//   - if we are in the middle of an array, that's a quoted value
@@ -202,7 +202,7 @@ public JsonDocumentItemType next() {
 					break;
 				case OTHER:
 					// here we are in front of a boolean, a null or a numeric value.
-					// if none of these cases we going to raise IllegalStateException
+					// if none of these cases we're going to raise IllegalStateException
 					// put back what we've read
 					moveBufferPosition(-1);
 					final int valueSize = consumeNonStringValue();
@@ -296,9 +296,8 @@ private void moveTo(char character) throws IllegalStateException {
 	}
 
 	/**
-	 * Goes through the JSON string to locate a character.
-	 * Escaped characters are taken into account.
-	 * ex: on 'AB\"C"' this method returns 5 (not 3)
+	 * Goes through the JSON string to locate a non-escaped instance of a given character.
+	 * Ex: on 'AB\"C"' this method returns 5 (not 3)
 	 *
 	 * @param character character to be found
 	 * @return the position of the character or -1 if not found.
@@ -328,7 +327,7 @@ private int locateCharacter(char character) {
 	}
 
 	/**
-	 * Consume a non-quotted value
+	 * Consume a non-quoted value
 	 * @return the length of this value. can be 0, -1 in case of error
 	 */
 	private int consumeNonStringValue() {
@@ -354,10 +353,10 @@ private int consumeNonStringValue() {
 		return allGood?(this.jsonValueEnd-this.jsonValueStart):-1;
 	}
 	/**
-	 * Consume a quotted value
+	 * Consumes a quoted value
 	 * @return the length of this value. can be 0, -1 in case of error
 	 */
-	private void consumeQuottedString() {
+	private void consumeQuotedString() {
 
 		// be sure we are at a meaningful place
 		// key name are unquoted
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index f6cc9fbe62c4..86498ae67554 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -6,8 +6,8 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.Module;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import oracle.jdbc.provider.oson.OsonModule;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
 
@@ -15,7 +15,7 @@
 
 
 /**
- * Implementation of FormatMapper for Orale OSON support
+ * Implementation of FormatMapper for Oracle OSON support
  *
  * @author Emmanuel Jannetti
  * @author Bidyadhar Mohanty
@@ -25,30 +25,12 @@ public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
 	public static final String SHORT_NAME = "jackson";
 
 
-	private static final Class osonModuleKlass;
-	static {
-		try {
-			osonModuleKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonModule" );
-		}
-		catch (ClassNotFoundException | LinkageError e) {
-			// should not happen as JacksonOsonFormatMapper is loaded
-			// only when Oracle OSON JDBC extension is present
-			// see OracleDialect class.
-			throw new ExceptionInInitializerError( "JacksonOsonFormatMapper class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
-		}
-	}
-
 	/**
 	 * Creates a new JacksonOsonFormatMapper
 	 */
 	public JacksonOsonFormatMapper() {
 		super();
-		try {
-			objectMapper.registerModule( (Module) osonModuleKlass.getDeclaredConstructor().newInstance() );
-		}
-		catch (Exception e) {
-			throw new RuntimeException( "Cannot instanciate " + osonModuleKlass.getCanonicalName(), e );
-		}
+		objectMapper.registerModule( new OsonModule() );
 		objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
 	}
 
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java
index 5002351bcce1..2100ce863849 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/resolver/DialectSpecificConfigTest.java
@@ -24,7 +24,6 @@
 import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_NO_BACKSLASH_ESCAPES;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
-import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.cfg.DialectSpecificSettings.SYBASE_ANSI_NULL;
 import static org.hibernate.dialect.DatabaseVersion.NO_VERSION;
 
@@ -56,28 +55,6 @@ public void testOracleIsAutonomous() {
 		assertThat( ( (OracleDialect) dialect ).isAutonomous() ).isTrue();
 	}
 
-	@Test
-	public void testOracleIsOsonEnabled() {
-		final Dialect dialect = resolveDialect(
-				"Oracle",
-				values -> values.put( "emptyOne", "true" )
-		);
-
-		assertThat( dialect ).isInstanceOf( OracleDialect.class );
-		assertThat( ( (OracleDialect) dialect ).isOracleOsonDisabled() ).isFalse();
-	}
-
-	@Test
-	public void testOracleIsOsonDisabled() {
-		final Dialect dialect = resolveDialect(
-				"Oracle",
-				values -> values.put( ORACLE_OSON_DISABLED, "true" )
-		);
-
-		assertThat( dialect ).isInstanceOf( OracleDialect.class );
-		assertThat( ( (OracleDialect) dialect ).isOracleOsonDisabled() ).isTrue();
-	}
-
 	@Test
 	public void testSybaseASEIsAnsiNull() {
 		final Dialect dialect = resolveDialect(
diff --git a/local-build-plugins/src/main/groovy/local.java-module.gradle b/local-build-plugins/src/main/groovy/local.java-module.gradle
index 40499736c718..291d4a8a4f2c 100644
--- a/local-build-plugins/src/main/groovy/local.java-module.gradle
+++ b/local-build-plugins/src/main/groovy/local.java-module.gradle
@@ -48,6 +48,9 @@ dependencies {
     compileOnly libs.loggingAnnotations
     // Used for compiling some Oracle specific JdbcTypes
     compileOnly jdbcLibs.oracle
+    compileOnly (jdbcLibs.oracleJdbcJacksonOsonExtension) {
+        exclude group: 'com.oracle.database.jdbc', module: 'ojdbc8'
+    }
 
     // JUnit dependencies made up of:
     // 		* JUnit 5
diff --git a/settings.gradle b/settings.gradle
index e6363ceeda6c..7a6754bf3256 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -229,6 +229,7 @@ dependencyResolutionManagement {
             def mssqlVersion = version "mssql", "12.8.1.jre11"
             def mysqlVersion = version "mysql", "9.2.0"
             def oracleVersion = version "oracle", "23.7.0.25.01"
+            def oracleJacksonOsonExtension = version "oracleJacksonOsonExtension", "1.0.3"
             def pgsqlVersion = version "pgsql", "42.7.4"
             def sybaseVersion = version "sybase", "1.3.1"
             def tidbVersion = version "tidb", mysqlVersion
@@ -247,6 +248,7 @@ dependencyResolutionManagement {
             library( "oracle", "com.oracle.database.jdbc", "ojdbc17" ).versionRef( oracleVersion )
             library( "oracleXml", "com.oracle.database.xml", "xdb" ).versionRef( oracleVersion )
             library( "oracleXmlParser", "com.oracle.database.xml", "xmlparserv2" ).versionRef( oracleVersion )
+            library( "oracleJdbcJacksonOsonExtension", "com.oracle.database.jdbc", "ojdbc-provider-jackson-oson" ).versionRef( oracleJacksonOsonExtension )
             library( "mssql", "com.microsoft.sqlserver", "mssql-jdbc" ).versionRef( mssqlVersion )
             library( "db2", "com.ibm.db2", "jcc" ).versionRef( db2Version )
             library( "hana", "com.sap.cloud.db.jdbc", "ngdbc" ).versionRef( hanaVersion )

From c1e27c559d528f19e91ec645c6081b07c3492860 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Mon, 7 Apr 2025 18:45:17 +0200
Subject: [PATCH 57/81] HHH-17404 : add schema compatibility test

---
 .../dialect/OracleOsonJacksonJdbcType.java    |  6 +-
 .../mapping/hhh17404/JsonCBLOBToOsonTest.java | 95 +++++++++++++++++++
 .../util/StringJsonDocumentReaderTest.java    | 35 +++++++
 settings.gradle                               |  2 +-
 4 files changed, 134 insertions(+), 4 deletions(-)
 create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/JsonCBLOBToOsonTest.java

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index bfe5b5dfb0e4..385eb40215dc 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -182,7 +182,7 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
 					return doExtraction(ojd,options);
 				} catch (SQLException exc) {
-					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// this may happen if we are fetching data from an existing schema
 						// that use CBLOB for JSON column In that case we assume byte are
 						// UTF-8 bytes (i.e not OSON)
@@ -203,7 +203,7 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 					OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
 					return doExtraction(ojd,options);
 				} catch (SQLException exc) {
-					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// this may happen if we are fetching data from an existing schema
 						// that use CBLOB for JSON column. In that case we assume byte are
 						// UTF-8 bytes (i.e not OSON)
@@ -225,7 +225,7 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o
 					OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
 					return doExtraction(ojd,options);
 				} catch (SQLException exc) {
-					if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// this may happen if we are fetching data from an existing schema
 						// that use CBLOB for JSON column In that case we assume byte are
 						//						// UTF-8 bytes (i.e not OSON)
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/JsonCBLOBToOsonTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/JsonCBLOBToOsonTest.java
new file mode 100644
index 000000000000..06a11ecca312
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/JsonCBLOBToOsonTest.java
@@ -0,0 +1,95 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.mapping.hhh17404;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.orm.test.mapping.basic.JsonMappingTests;
+import org.hibernate.testing.orm.junit.DomainModel;
+import org.hibernate.testing.orm.junit.RequiresDialect;
+import org.hibernate.testing.orm.junit.SessionFactory;
+import org.hibernate.testing.orm.junit.SessionFactoryScope;
+import org.hibernate.type.SqlTypes;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hibernate.testing.orm.junit.DialectContext.getDialect;
+
+/**
+ * This test class is about testing that legacy schema that use BLO for JSON column
+ * can be safely read even when Oracle Oson extention is in place.
+ * In Such a situation, the JSON type will expect JSON a JSON column and should
+ * silently fall back to String deserialization.
+ *
+ * @author Emmanuel Jannetti
+ */
+@DomainModel(annotatedClasses = JsonCBLOBToOsonTest.JsonEntity.class)
+@SessionFactory
+@RequiresDialect( value = OracleDialect.class, majorVersion = 23 )
+public class JsonCBLOBToOsonTest {
+
+	@Entity(name = "JsonEntity")
+	@Table(name = "TEST_OSON_COMPAT")
+	public static class JsonEntity {
+		@Id
+		private Integer id;
+		@JdbcTypeCode( SqlTypes.JSON )
+		private JsonMappingTests.StringNode jsonName;
+
+		public JsonEntity() {
+			super();
+		}
+		public JsonEntity(Integer id,  JsonMappingTests.StringNode node) {
+			this.id = id;
+			this.jsonName = node;
+		}
+	}
+
+	@BeforeEach
+	public void setup(SessionFactoryScope scope) {
+		scope.inTransaction(
+				(session) -> {
+					// force creation of a BLOB column type by creating the table ourselves
+					session.createNativeQuery( getDialect().getDropTableString( "TEST_OSON_COMPAT" ) )
+							.executeUpdate();
+					session.createNativeQuery( "CREATE TABLE TEST_OSON_COMPAT (id NUMBER, jsonName BLOB CHECK (jsonName is json)  ,primary key (id))" )
+							.executeUpdate();
+
+					String insert = "INSERT INTO TEST_OSON_COMPAT (id, jsonName) VALUES(:id,:json)";
+					String jsonstr = "{\"string\":\"john\"}";
+					session.createNativeQuery(insert).setParameter("id",1)
+							.setParameter( "json", jsonstr).executeUpdate();
+				}
+		);
+	}
+
+	@AfterEach
+	public void tearDown(SessionFactoryScope scope) {
+		scope.inTransaction(
+				(session) -> {
+					session.createNativeQuery( getDialect().getDropTableString( "TEST_OSON_COMPAT" ) ).executeUpdate();
+				}
+		);
+	}
+
+	@Test
+	public void verifyReadWorks(SessionFactoryScope scope) {
+		scope.inTransaction(
+				(session) -> {
+					JsonEntity entity = session.find( JsonCBLOBToOsonTest.JsonEntity.class, 1 );
+					assertThat( entity.jsonName.getString(), is( "john" ) );
+
+				}
+		);
+	}
+
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
index 2371a39ffab3..a2cd7b75202f 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentReaderTest.java
@@ -381,4 +381,39 @@ public void testNestedArrayDocument() {
 		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
 	}
 
+	@Test
+	public void testUnicode() {
+		final StringJsonDocumentReader reader = new StringJsonDocumentReader( """
+							{
+							"myUnicode1": "\\u0074\\u0068\\u0069\\u0073\\u005f\\u0069\\u0073\\u005f\\u0075\\u006e\\u0069\\u0063\\u006f\\u0064\\u0065",
+							"myUnicode2": "this_\\u0069\\u0073_unicode",
+							"myUnicode3": "this_is_unicode"
+							}
+
+
+						""");
+
+		assertTrue(reader.hasNext(), "should have more element");
+		assertEquals( JsonDocumentItemType.OBJECT_START, reader.next());
+
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals("myUnicode1", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItemType.VALUE, reader.next());
+		assertEquals("this_is_unicode", reader.getStringValue());
+
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals("myUnicode2", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItemType.VALUE, reader.next());
+		assertEquals("this_is_unicode", reader.getStringValue());
+
+		assertEquals( JsonDocumentItemType.VALUE_KEY,reader.next());
+		assertEquals("myUnicode3", reader.getObjectKeyName());
+		assertEquals( JsonDocumentItemType.VALUE, reader.next());
+		assertEquals("this_is_unicode", reader.getStringValue());
+
+		assertEquals( JsonDocumentItemType.OBJECT_END, reader.next());
+
+	}
+
+
 }
diff --git a/settings.gradle b/settings.gradle
index 7a6754bf3256..48e3a3b747f6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -229,7 +229,7 @@ dependencyResolutionManagement {
             def mssqlVersion = version "mssql", "12.8.1.jre11"
             def mysqlVersion = version "mysql", "9.2.0"
             def oracleVersion = version "oracle", "23.7.0.25.01"
-            def oracleJacksonOsonExtension = version "oracleJacksonOsonExtension", "1.0.3"
+            def oracleJacksonOsonExtension = version "oracleJacksonOsonExtension", "1.0.4"
             def pgsqlVersion = version "pgsql", "42.7.4"
             def sybaseVersion = version "sybase", "1.3.1"
             def tidbVersion = version "tidb", mysqlVersion

From 16e751a52c5f557bae2ddfed5cefdd6deb0b8c6d Mon Sep 17 00:00:00 2001
From: BidyadharM <bidyadhar.mohanty@oracle.com>
Date: Thu, 10 Apr 2025 01:32:55 +0530
Subject: [PATCH 58/81] HHH-17404 : Removed Duration mapping to intervalDS
 misssed during previous uploads.

---
 .../src/main/java/org/hibernate/dialect/OracleDialect.java     | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 0d1b586b042a..a1f5231a1cef 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -842,9 +842,6 @@ protected String columnType(int sqlTypeCode) {
 			case VARBINARY:
 				return "raw($l)";
 
-			case DURATION:
-				return "interval day to second";
-
 			default:
 				return super.columnType( sqlTypeCode );
 		}

From 8727f6e3be44f9cd1a958176aeeb279f6391b7e7 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Wed, 9 Apr 2025 22:17:02 +0200
Subject: [PATCH 59/81] HHH-17404 rename oson flag in oracle dialect

---
 .../org/hibernate/dialect/OracleDialect.java  |  8 +--
 .../OracleOsonJacksonArrayJdbcType.java       | 67 ++++++++++++++++---
 .../dialect/OracleOsonJacksonJdbcType.java    | 18 ++---
 .../type/descriptor/jdbc/JsonHelper.java      | 13 +++-
 4 files changed, 80 insertions(+), 26 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index a1f5231a1cef..c788b42fa9d9 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -199,8 +199,7 @@ public class OracleDialect extends Dialect {
 
 	private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 19 );
 
-	private static final String JACKSON_MAPPER_NAME = "jackson";
-	private static boolean OracleOsonExtensionUsed = false;
+	private boolean osonExtensionEnabled = false;
 
 	private final OracleUserDefinedTypeExporter userDefinedTypeExporter = new OracleUserDefinedTypeExporter( this );
 	private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this);
@@ -1046,8 +1045,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 				DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
 						"Oracle OSON Jackson extension used" );
-				// as we speak this is not supported by OSON extension
-				OracleOsonExtensionUsed = true;
+				osonExtensionEnabled = true;
 			}
 			else {
 				if (DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.isDebugEnabled()) {
@@ -1108,7 +1106,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this ,!OracleOsonExtensionUsed);
+		return OracleAggregateSupport.valueOf( this ,!osonExtensionEnabled );
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index d847e534d771..a1566c46f678 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -6,11 +6,14 @@
 
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
+import oracle.jdbc.driver.DatabaseError;
 import oracle.jdbc.provider.oson.OsonFactory;
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
 
+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;
@@ -27,6 +30,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -43,6 +47,7 @@
  */
 public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
 
+	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonArrayJdbcType.class );
 
 	private static final OsonFactory osonFactory = new OsonFactory();
 
@@ -121,6 +126,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getJsonFormatMapper();
 				JsonParser osonParser = osonFactory.createParser( osonBytes );
+
 				return mapper.readFromSource(  getJavaType(), osonParser, options);
 			}
 
@@ -139,25 +145,66 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-
-				OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-				return doExtraction( ojd, options);
+				try {
+					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+					return doExtraction( ojd, options);
+				} catch (SQLException exc) {
+					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+						// This may happen if we are fetching data from an existing schema
+						// that uses BLOB for JSON column In that case we assume bytes are
+						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+						return OracleOsonJacksonArrayJdbcType.this.fromString(
+								new String( rs.getBytes( paramIndex ), StandardCharsets.UTF_8 ),
+								getJavaType(),
+								options);
+					} else {
+						throw exc;
+					}
+				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-
-
-				OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
-				return doExtraction( ojd, options);
+				try {
+					OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
+					return doExtraction( ojd, options);
+				} catch (SQLException exc) {
+					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+						// This may happen if we are fetching data from an existing schema
+						// that uses BLOB for JSON column In that case we assume bytes are
+						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+						return OracleOsonJacksonArrayJdbcType.this.fromString(
+								new String( statement.getBytes( index ), StandardCharsets.UTF_8 ),
+								getJavaType(),
+								options);
+					} else {
+						throw exc;
+					}
+				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
-
-				OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
-				return doExtraction( ojd, options);
+				try {
+					OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
+					return doExtraction( ojd, options);
+				} catch (SQLException exc) {
+					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+						// This may happen if we are fetching data from an existing schema
+						// that uses BLOB for JSON column In that case we assume bytes are
+						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+						return OracleOsonJacksonArrayJdbcType.this.fromString(
+								new String( statement.getBytes( name ), StandardCharsets.UTF_8 ),
+								getJavaType(),
+								options);
+					} else {
+						throw exc;
+					}
+				}
 			}
 		};
 	}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index 385eb40215dc..fbff6af6a8ad 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -183,9 +183,9 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 					return doExtraction(ojd,options);
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-						// this may happen if we are fetching data from an existing schema
-						// that use CBLOB for JSON column In that case we assume byte are
-						// UTF-8 bytes (i.e not OSON)
+						// This may happen if we are fetching data from an existing schema
+						// that uses BLOB for JSON column In that case we assume bytes are
+						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
 						return OracleOsonJacksonJdbcType.this.fromString(
 								new String( rs.getBytes( paramIndex ), StandardCharsets.UTF_8 ),
@@ -204,9 +204,9 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 					return doExtraction(ojd,options);
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-						// this may happen if we are fetching data from an existing schema
-						// that use CBLOB for JSON column. In that case we assume byte are
-						// UTF-8 bytes (i.e not OSON)
+						// This may happen if we are fetching data from an existing schema
+						// that uses BLOB for JSON column In that case we assume bytes are
+						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
 						return OracleOsonJacksonJdbcType.this.fromString(
 								new String( statement.getBytes( index ), StandardCharsets.UTF_8 ),
@@ -226,9 +226,9 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o
 					return doExtraction(ojd,options);
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-						// this may happen if we are fetching data from an existing schema
-						// that use CBLOB for JSON column In that case we assume byte are
-						//						// UTF-8 bytes (i.e not OSON)
+						// This may happen if we are fetching data from an existing schema
+						// that uses BLOB for JSON column In that case we assume bytes are
+						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
 						return OracleOsonJacksonJdbcType.this.fromString(
 								new String( statement.getBytes( name ), StandardCharsets.UTF_8 ),
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 5859173f76df..18f4d96bcf98 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -408,16 +408,25 @@ public static <X> X deserialize(
 		return (X) values;
 	}
 
+
 	// This is also used by Hibernate Reactive
 	public static <X> X arrayFromString(
 			JavaType<X> javaType,
 			JdbcType elementJdbcType,
 			String string,
 			WrapperOptions options) throws SQLException {
-
 		if ( string == null ) {
 			return null;
 		}
+		return deserializeArray( javaType, elementJdbcType, new StringJsonDocumentReader( string ), options );
+	}
+
+	public static <X> X deserializeArray(
+			JavaType<X> javaType,
+			JdbcType elementJdbcType,
+			JsonDocumentReader reader,
+			WrapperOptions options) throws SQLException {
+
 
 		final CustomArrayList arrayList = new CustomArrayList();
 		final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) javaType).getElementJavaType();
@@ -429,7 +438,7 @@ public static <X> X arrayFromString(
 		else {
 			jdbcJavaType = options.getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor( preferredJavaTypeClass );
 		}
-		JsonDocumentReader reader = new StringJsonDocumentReader(string);
+
 		JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,false);
 
 		assert reader.hasNext():"Invalid array string";

From 564e13600ce41a1c8761718e2f88744b7fb61bb9 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Tue, 15 Apr 2025 20:06:37 +0200
Subject: [PATCH 60/81] HHH-17404 add array of embeddable support

---
 .../org/hibernate/dialect/OracleDialect.java  |   1 -
 .../OracleOsonJacksonArrayJdbcType.java       |  17 +-
 .../type/descriptor/jdbc/JsonHelper.java      |  20 +-
 .../embeddable/EmbeddableAggregate.java       |  15 +
 .../embeddable/JsonEmbeddableArrayTest.java   | 331 ++++++++++++++++++
 5 files changed, 380 insertions(+), 4 deletions(-)
 create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index c788b42fa9d9..6b0cd41fe328 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -145,7 +145,6 @@
 import static org.hibernate.type.SqlTypes.DATE;
 import static org.hibernate.type.SqlTypes.DECIMAL;
 import static org.hibernate.type.SqlTypes.DOUBLE;
-import static org.hibernate.type.SqlTypes.DURATION;
 import static org.hibernate.type.SqlTypes.FLOAT;
 import static org.hibernate.type.SqlTypes.GEOMETRY;
 import static org.hibernate.type.SqlTypes.INTEGER;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index a1566c46f678..be5d13b46df7 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -4,6 +4,7 @@
  */
 package org.hibernate.dialect;
 
+import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
 import oracle.jdbc.driver.DatabaseError;
@@ -12,6 +13,7 @@
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
 
+import oracle.sql.json.OracleJsonParser;
 import org.hibernate.internal.CoreLogging;
 import org.hibernate.internal.CoreMessageLogger;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@@ -26,6 +28,7 @@
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
 import org.hibernate.type.format.FormatMapper;
+import org.hibernate.type.format.OsonDocumentReader;
 import org.hibernate.type.format.OsonDocumentWriter;
 
 import java.io.ByteArrayOutputStream;
@@ -125,9 +128,19 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 				FormatMapper mapper = options.getJsonFormatMapper();
-				JsonParser osonParser = osonFactory.createParser( osonBytes );
+				OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( osonBytes );
+				final JdbcType elementJdbcType = getElementJdbcType();
+				if (elementJdbcType instanceof JsonJdbcType) {
+					if (((JsonJdbcType) elementJdbcType).getEmbeddableMappingType() != null) {
+						// embeddable array case.
+						return JsonHelper.deserializeArray( javaType,
+								elementJdbcType, new OsonDocumentReader( osonParser ), options );
+					}
+				}
+				try (JsonParser oParser = ((JsonFactory)osonFactory).createParser(  osonBytes )) {
+					return mapper.readFromSource(  getJavaType(), oParser, options);
+				}
 
-				return mapper.readFromSource(  getJavaType(), osonParser, options);
 			}
 
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 18f4d96bcf98..abbd2512b2e1 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -36,6 +36,7 @@
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
 import org.hibernate.type.format.JsonDocumentItemType;
 import org.hibernate.type.format.JsonDocumentReader;
 import org.hibernate.type.format.JsonDocumentWriter;
@@ -264,7 +265,15 @@ private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, Embedda
 		objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()+ ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
 		objectArrays.push( objectArrayResult );
 
-		while(reader.hasNext()) {
+		// We loop on two conditions:
+		//   - the parser still has tokens left
+		//   - the type stack is not empty
+		// Even if the reader has some tokens left, if the type stack is empty,
+		// that means that we have to stop parsing. That may be the case while parsing an object of object array,
+		// the array is not empty, but we ae done parsing that specific object.
+		// When we encounter OBJECT_END the current type is popped out of the stack. When parsing one object of an array we may end up
+		// having an empty stack. Next Objects are parsed in the next round.
+		while(reader.hasNext() && !embeddableMappingTypes.isEmpty()) {
 			JsonDocumentItemType type = reader.next();
 			switch (type) {
 				case VALUE_KEY:
@@ -462,6 +471,15 @@ public static <X> X deserializeArray(
 				case VALUE:
 					arrayList.add( adapter.fromValue(jdbcJavaType, elementJdbcType ,reader, options) );
 					break;
+				case OBJECT_START:
+					assert elementJdbcType instanceof JsonJdbcType;
+					Object o = deserialize(
+							((JsonJdbcType) elementJdbcType).getEmbeddableMappingType(),
+							reader,
+							true,
+							options);
+					arrayList.add(o);
+					break;
 				default:
 					throw new UnsupportedOperationException( "Unexpected JSON type " + type );
 			}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
index 069ed1ac1559..4edb530ade4e 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/EmbeddableAggregate.java
@@ -292,6 +292,14 @@ public void setMutableValue(MutableValue mutableValue) {
 		this.mutableValue = mutableValue;
 	}
 
+	static void assertArraysEquals(EmbeddableAggregate [] a1, EmbeddableAggregate []a2) {
+		Assertions.assertTrue( (a1 == null && a2 == null) || (a1 != null && a2 != null) );
+		Assertions.assertEquals( a1.length, a2.length );
+		for (int i = 0; i < a1.length; i++) {
+			assertEquals(a1[i], a2[i]);
+		}
+	}
+
 	static void assertEquals(EmbeddableAggregate a1, EmbeddableAggregate a2) {
 		Assertions.assertEquals( a1.theInt, a2.theInt );
 		Assertions.assertEquals( a1.theDouble, a2.theDouble );
@@ -338,6 +346,13 @@ static void assertEquals(EmbeddableAggregate a1, EmbeddableAggregate a2) {
 		}
 	}
 
+	public static EmbeddableAggregate[] createAggregateArray1() {
+		return new EmbeddableAggregate[] {createAggregate1(),createAggregate2()};
+	}
+	public static EmbeddableAggregate[] createAggregateArray2() {
+		return new EmbeddableAggregate[] {createAggregate3()};
+	}
+
 	public static EmbeddableAggregate createAggregate1() {
 		final EmbeddableAggregate aggregate = new EmbeddableAggregate();
 		aggregate.theBoolean = true;
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
new file mode 100644
index 000000000000..3c5b2ad1d786
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
@@ -0,0 +1,331 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.mapping.embeddable;
+
+import java.net.URL;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.type.SqlTypes;
+
+import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
+import org.hibernate.testing.orm.domain.gambit.MutableValue;
+import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest;
+import org.hibernate.testing.orm.junit.DialectFeatureChecks;
+import org.hibernate.testing.orm.junit.RequiresDialectFeature;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Tuple;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonAggregate.class)
+public class JsonEmbeddableArrayTest extends BaseSessionFactoryFunctionalTest {
+
+	@Override
+	protected Class<?>[] getAnnotatedClasses() {
+		return new Class<?>[] {
+				JsonEmbeddableArrayTest.JsonArrayHolder.class
+		};
+	}
+
+	@BeforeEach
+	public void setUp() {
+		inTransaction(
+				session -> {
+					session.persist( new JsonArrayHolder( 1L, EmbeddableAggregate.createAggregateArray1() ));
+					session.persist( new JsonArrayHolder( 2L, EmbeddableAggregate.createAggregateArray2() ));
+				}
+		);
+	}
+
+	@AfterEach
+	protected void cleanupTest() {
+		inTransaction(
+				session -> {
+					session.createMutationQuery( "delete from JsonArrayHolder h" ).executeUpdate();
+				}
+		);
+	}
+
+	@Test
+	public void testUpdate() {
+		sessionFactoryScope().inTransaction(
+				entityManager -> {
+					JsonArrayHolder jsonArrayHolder = entityManager.find( JsonArrayHolder.class, 1L );
+					jsonArrayHolder.setAggregateArray( EmbeddableAggregate.createAggregateArray2() );
+					entityManager.flush();
+					entityManager.clear();
+					EmbeddableAggregate.assertArraysEquals(
+							EmbeddableAggregate.createAggregateArray2(),
+							entityManager.find( JsonArrayHolder.class, 1L ).getAggregateArray() );
+				}
+		);
+	}
+
+	@Test
+	public void testFetch() {
+		sessionFactoryScope().inSession(
+				entityManager -> {
+					List<JsonArrayHolder> jsonArrayHolders = entityManager.createQuery( "from JsonArrayHolder b where b.id = 1", JsonArrayHolder.class ).getResultList();
+					assertEquals( 1, jsonArrayHolders.size() );
+					assertEquals( 1L, jsonArrayHolders.get( 0 ).getId() );
+					EmbeddableAggregate.assertArraysEquals( EmbeddableAggregate.createAggregateArray1(), jsonArrayHolders.get( 0 ).getAggregateArray() );
+				}
+		);
+	}
+
+	@Test
+	public void testFetchNull() {
+		sessionFactoryScope().inSession(
+				entityManager -> {
+					List<JsonArrayHolder> jsonArrayHolders = entityManager.createQuery( "from JsonArrayHolder b where b.id = 2", JsonArrayHolder.class ).getResultList();
+					assertEquals( 1, jsonArrayHolders.size() );
+					assertEquals( 2L, jsonArrayHolders.get( 0 ).getId() );
+					EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregateArray2()[0], jsonArrayHolders.get( 0 ).getAggregateArray()[0] );
+				}
+		);
+	}
+
+	@Test
+	public void testDomainResult() {
+		sessionFactoryScope().inSession(
+				entityManager -> {
+					List<EmbeddableAggregate[]> structs = entityManager.createQuery( "select b.aggregateArray from JsonArrayHolder b where b.id = 1", EmbeddableAggregate[].class ).getResultList();
+					assertEquals( 1, structs.size() );
+					EmbeddableAggregate.assertArraysEquals( EmbeddableAggregate.createAggregateArray1(), structs.get( 0 ) );
+				}
+		);
+	}
+
+	@Test
+	public void testSelectionItems() {
+		sessionFactoryScope().inSession(
+				entityManager -> {
+					List<Tuple> tuples = entityManager.createQuery(
+							"select " +
+									"b.aggregateArray[0].theInt," +
+									"b.aggregateArray[0].theDouble," +
+									"b.aggregateArray[0].theBoolean," +
+									"b.aggregateArray[0].theNumericBoolean," +
+									"b.aggregateArray[0].theStringBoolean," +
+									"b.aggregateArray[0].theString," +
+									"b.aggregateArray[0].theInteger," +
+									"b.aggregateArray[0].theUrl," +
+									"b.aggregateArray[0].theClob," +
+									"b.aggregateArray[0].theBinary," +
+									"b.aggregateArray[0].theDate," +
+									"b.aggregateArray[0].theTime," +
+									"b.aggregateArray[0].theTimestamp," +
+									"b.aggregateArray[0].theInstant," +
+									"b.aggregateArray[0].theUuid," +
+									"b.aggregateArray[0].gender," +
+									"b.aggregateArray[0].convertedGender," +
+									"b.aggregateArray[0].ordinalGender," +
+									"b.aggregateArray[0].theDuration," +
+									"b.aggregateArray[0].theLocalDateTime," +
+									"b.aggregateArray[0].theLocalDate," +
+									"b.aggregateArray[0].theLocalTime," +
+									"b.aggregateArray[0].theZonedDateTime," +
+									"b.aggregateArray[0].theOffsetDateTime," +
+									"b.aggregateArray[0].mutableValue " +
+									"from JsonArrayHolder b where b.id = 1",
+							Tuple.class
+					).getResultList();
+					assertEquals( 1, tuples.size() );
+					final Tuple tuple = tuples.get( 0 );
+					final EmbeddableAggregate struct = new EmbeddableAggregate();
+					struct.setTheInt( tuple.get( 0, int.class ) );
+					struct.setTheDouble( tuple.get( 1, Double.class ) );
+					struct.setTheBoolean( tuple.get( 2, Boolean.class ) );
+					struct.setTheNumericBoolean( tuple.get( 3, Boolean.class ) );
+					struct.setTheStringBoolean( tuple.get( 4, Boolean.class ) );
+					struct.setTheString( tuple.get( 5, String.class ) );
+					struct.setTheInteger( tuple.get( 6, Integer.class ) );
+					struct.setTheUrl( tuple.get( 7, URL.class ) );
+					struct.setTheClob( tuple.get( 8, String.class ) );
+					struct.setTheBinary( tuple.get( 9, byte[].class ) );
+					struct.setTheDate( tuple.get( 10, Date.class ) );
+					struct.setTheTime( tuple.get( 11, Time.class ) );
+					struct.setTheTimestamp( tuple.get( 12, Timestamp.class ) );
+					struct.setTheInstant( tuple.get( 13, Instant.class ) );
+					struct.setTheUuid( tuple.get( 14, UUID.class ) );
+					struct.setGender( tuple.get( 15, EntityOfBasics.Gender.class ) );
+					struct.setConvertedGender( tuple.get( 16, EntityOfBasics.Gender.class ) );
+					struct.setOrdinalGender( tuple.get( 17, EntityOfBasics.Gender.class ) );
+					struct.setTheDuration( tuple.get( 18, Duration.class ) );
+					struct.setTheLocalDateTime( tuple.get( 19, LocalDateTime.class ) );
+					struct.setTheLocalDate( tuple.get( 20, LocalDate.class ) );
+					struct.setTheLocalTime( tuple.get( 21, LocalTime.class ) );
+					struct.setTheZonedDateTime( tuple.get( 22, ZonedDateTime.class ) );
+					struct.setTheOffsetDateTime( tuple.get( 23, OffsetDateTime.class ) );
+					struct.setMutableValue( tuple.get( 24, MutableValue.class ) );
+					EmbeddableAggregate.assertEquals( EmbeddableAggregate.createAggregate1(), struct );
+				}
+		);
+	}
+
+	@Test
+	public void testDeleteWhere() {
+		sessionFactoryScope().inTransaction(
+				entityManager -> {
+					entityManager.createMutationQuery( "delete JsonArrayHolder b where b.aggregateArray is not null" ).executeUpdate();
+					assertNull( entityManager.find( JsonArrayHolder.class, 1L ) );
+
+				}
+		);
+	}
+
+	@Test
+	public void testUpdateAggregate() {
+		sessionFactoryScope().inTransaction(
+				entityManager -> {
+					entityManager.createMutationQuery( "update JsonArrayHolder b set b.aggregateArray = null" ).executeUpdate();
+					assertNull( entityManager.find( JsonArrayHolder.class, 1L ).getAggregateArray() );
+				}
+		);
+	}
+
+	@Test
+	public void testUpdateAggregateMember() {
+		sessionFactoryScope().inTransaction(
+				entityManager -> {
+					entityManager.createMutationQuery( "update JsonArrayHolder b set b.aggregateArray[0].theString = null where b.id = 1" ).executeUpdate();
+					EmbeddableAggregate[] struct = EmbeddableAggregate.createAggregateArray1();
+					struct[0].setTheString( null );
+					EmbeddableAggregate.assertArraysEquals( struct, entityManager.find( JsonArrayHolder.class, 1L ).getAggregateArray() );
+				}
+		);
+	}
+
+	@Test
+	public void testUpdateMultipleAggregateMembers() {
+		sessionFactoryScope().inTransaction(
+				entityManager -> {
+					entityManager.createMutationQuery( "update JsonArrayHolder b set b.aggregateArray.theString = null, b.aggregateArray[0].theUuid = null" ).executeUpdate();
+					EmbeddableAggregate[] struct = EmbeddableAggregate.createAggregateArray1();
+					struct[0].setTheString( null );
+					struct[0].setTheUuid( null );
+					EmbeddableAggregate.assertArraysEquals( struct, entityManager.find( JsonArrayHolder.class, 1L ).getAggregateArray() );
+				}
+		);
+	}
+
+	@Test
+	public void testUpdateAllAggregateMembers() {
+		sessionFactoryScope().inTransaction(
+				entityManager -> {
+					EmbeddableAggregate[] struct = EmbeddableAggregate.createAggregateArray1();
+					entityManager.createMutationQuery(
+									"update JsonArrayHolder b set " +
+											"b.aggregateArray[0].theInt = :theInt," +
+											"b.aggregateArray[0].theDouble = :theDouble," +
+											"b.aggregateArray[0].theBoolean = :theBoolean," +
+											"b.aggregateArray[0].theNumericBoolean = :theNumericBoolean," +
+											"b.aggregateArray[0].theStringBoolean = :theStringBoolean," +
+											"b.aggregateArray[0].theString = :theString," +
+											"b.aggregateArray[0].theInteger = :theInteger," +
+											"b.aggregateArray[0].theUrl = :theUrl," +
+											"b.aggregateArray[0].theClob = :theClob," +
+											"b.aggregateArray[0].theBinary = :theBinary," +
+											"b.aggregateArray[0].theDate = :theDate," +
+											"b.aggregateArray[0].theTime = :theTime," +
+											"b.aggregateArray[0].theTimestamp = :theTimestamp," +
+											"b.aggregateArray[0].theInstant = :theInstant," +
+											"b.aggregateArray[0].theUuid = :theUuid," +
+											"b.aggregateArray[0].gender = :gender," +
+											"b.aggregateArray[0].convertedGender = :convertedGender," +
+											"b.aggregateArray[0].ordinalGender = :ordinalGender," +
+											"b.aggregateArray[0].theDuration = :theDuration," +
+											"b.aggregateArray[0].theLocalDateTime = :theLocalDateTime," +
+											"b.aggregateArray[0].theLocalDate = :theLocalDate," +
+											"b.aggregateArray[0].theLocalTime = :theLocalTime," +
+											"b.aggregateArray[0].theZonedDateTime = :theZonedDateTime," +
+											"b.aggregateArray[0].theOffsetDateTime = :theOffsetDateTime," +
+											"b.aggregateArray[0].mutableValue = :mutableValue " +
+											"where b.id = 2"
+							)
+							.setParameter( "theInt", struct[0].getTheInt() )
+							.setParameter( "theDouble", struct[0].getTheDouble() )
+							.setParameter( "theBoolean", struct[0].isTheBoolean() )
+							.setParameter( "theNumericBoolean", struct[0].isTheNumericBoolean() )
+							.setParameter( "theStringBoolean", struct[0].isTheStringBoolean() )
+							.setParameter( "theString", struct[0].getTheString() )
+							.setParameter( "theInteger", struct[0].getTheInteger() )
+							.setParameter( "theUrl", struct[0].getTheUrl() )
+							.setParameter( "theClob", struct[0].getTheClob() )
+							.setParameter( "theBinary", struct[0].getTheBinary() )
+							.setParameter( "theDate", struct[0].getTheDate() )
+							.setParameter( "theTime", struct[0].getTheTime() )
+							.setParameter( "theTimestamp", struct[0].getTheTimestamp() )
+							.setParameter( "theInstant", struct[0].getTheInstant() )
+							.setParameter( "theUuid", struct[0].getTheUuid() )
+							.setParameter( "gender", struct[0].getGender() )
+							.setParameter( "convertedGender", struct[0].getConvertedGender() )
+							.setParameter( "ordinalGender", struct[0].getOrdinalGender() )
+							.setParameter( "theDuration", struct[0].getTheDuration() )
+							.setParameter( "theLocalDateTime", struct[0].getTheLocalDateTime() )
+							.setParameter( "theLocalDate", struct[0].getTheLocalDate() )
+							.setParameter( "theLocalTime", struct[0].getTheLocalTime() )
+							.setParameter( "theZonedDateTime", struct[0].getTheZonedDateTime() )
+							.setParameter( "theOffsetDateTime", struct[0].getTheOffsetDateTime() )
+							.setParameter( "mutableValue", struct[0].getMutableValue() )
+							.executeUpdate();
+					EmbeddableAggregate.assertArraysEquals( EmbeddableAggregate.createAggregateArray1(), entityManager.find( JsonArrayHolder.class, 2L ).getAggregateArray() );
+				}
+		);
+	}
+
+	@Entity(name = "JsonArrayHolder")
+	public static class JsonArrayHolder {
+
+		@Id
+		private Long id;
+		@JdbcTypeCode(SqlTypes.JSON_ARRAY)
+		private EmbeddableAggregate [] aggregateArray;
+
+		public JsonArrayHolder() {
+		}
+
+		public JsonArrayHolder(Long id, EmbeddableAggregate[] aggregateArray) {
+			this.id = id;
+			this.aggregateArray = aggregateArray;
+		}
+
+		public Long getId() {
+			return id;
+		}
+
+		public void setId(Long id) {
+			this.id = id;
+		}
+
+		public EmbeddableAggregate[] getAggregateArray() {
+			return aggregateArray;
+		}
+
+		public void setAggregateArray(EmbeddableAggregate[] aggregateArray) {
+			this.aggregateArray = aggregateArray;
+		}
+
+	}
+	
+}

From cabbed683f2511306773caa7a2e0bf245f26ef5a Mon Sep 17 00:00:00 2001
From: Christian Beikov <christian.beikov@gmail.com>
Date: Thu, 17 Apr 2025 11:42:59 +0200
Subject: [PATCH 61/81] HHH-17404 Fix compile errors

---
 .../type/format/OsonDocumentReader.java       | 36 +++++++++----------
 .../type/format/StringJsonDocumentReader.java | 18 +++++-----
 2 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
index bf01bfd1fd55..37a7c4c4e17a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentReader.java
@@ -50,52 +50,52 @@ public JsonDocumentItemType next() {
 		currentKeyName = null;
 		currentValue = null;
 		switch (evt) {
-			case OracleJsonParser.Event.START_OBJECT:
+			case START_OBJECT:
 				return JsonDocumentItemType.OBJECT_START;
-			case OracleJsonParser.Event.END_OBJECT:
+			case END_OBJECT:
 				return JsonDocumentItemType.OBJECT_END;
-			case OracleJsonParser.Event.START_ARRAY:
+			case START_ARRAY:
 				return JsonDocumentItemType.ARRAY_START;
-			case OracleJsonParser.Event.END_ARRAY:
+			case END_ARRAY:
 				return JsonDocumentItemType.ARRAY_END;
-			case OracleJsonParser.Event.KEY_NAME:
+			case KEY_NAME:
 				currentKeyName = this.parser.getString();
 				return JsonDocumentItemType.VALUE_KEY;
-			case OracleJsonParser.Event.VALUE_TIMESTAMPTZ:
+			case VALUE_TIMESTAMPTZ:
 				currentValue = this.parser.getOffsetDateTime();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_DATE:
-			case OracleJsonParser.Event.VALUE_TIMESTAMP:
+			case VALUE_DATE:
+			case VALUE_TIMESTAMP:
 				currentValue = this.parser.getLocalDateTime();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_INTERVALDS:
+			case VALUE_INTERVALDS:
 				currentValue = this.parser.getDuration();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_INTERVALYM:
+			case VALUE_INTERVALYM:
 				currentValue = this.parser.getPeriod();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_STRING:
+			case VALUE_STRING:
 				currentValue = this.parser.getString();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_TRUE:
+			case VALUE_TRUE:
 				currentValue = Boolean.TRUE;
 				return JsonDocumentItemType.BOOLEAN_VALUE;
-			case OracleJsonParser.Event.VALUE_FALSE:
+			case VALUE_FALSE:
 				currentValue = Boolean.FALSE;
 				return JsonDocumentItemType.BOOLEAN_VALUE;
-			case OracleJsonParser.Event.VALUE_NULL:
+			case VALUE_NULL:
 				currentValue = null;
 				return JsonDocumentItemType.NULL_VALUE;
-			case OracleJsonParser.Event.VALUE_DECIMAL:
+			case VALUE_DECIMAL:
 				currentValue = this.parser.getBigDecimal();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_DOUBLE:
+			case VALUE_DOUBLE:
 				currentValue = this.parser.getDouble();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_FLOAT:
+			case VALUE_FLOAT:
 				currentValue = this.parser.getFloat();
 				return JsonDocumentItemType.VALUE;
-			case OracleJsonParser.Event.VALUE_BINARY:
+			case VALUE_BINARY:
 				currentValue = this.parser.getBytes();
 				return JsonDocumentItemType.VALUE;
 			default :
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index d5336103f002..7b025facacb2 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -115,10 +115,10 @@ private void moveStateMachine(StringJsonDocumentMarker marker) {
 				break;
 			case QUOTE:
 				switch ( currentState ) {
-					case JsonProcessingState.STARTING_ARRAY:
+					case STARTING_ARRAY:
 						this.processingStates.push( JsonProcessingState.ARRAY );
 						break;
-					case JsonProcessingState.STARTING_OBJECT:
+					case STARTING_OBJECT:
 						this.processingStates.push( JsonProcessingState.OBJECT );
 						this.processingStates.push( JsonProcessingState.OBJECT_KEY_NAME );
 						break;
@@ -175,18 +175,18 @@ public JsonDocumentItemType next() {
 					//        - if we just hit ':' that's a quoted value
 					//        - if we just hit ',' that's a quoted key
 					switch ( this.processingStates.getCurrent() ) {
-						case JsonProcessingState.STARTING_ARRAY:
+						case STARTING_ARRAY:
 							//this.processingStates.push( JsonProcessingState.ARRAY );
 							return JsonDocumentItemType.VALUE;
-						case JsonProcessingState.ARRAY:
+						case ARRAY:
 							return JsonDocumentItemType.VALUE;
-						case JsonProcessingState.STARTING_OBJECT:
+						case STARTING_OBJECT:
 							//this.processingStates.push( JsonProcessingState.OBJECT );
 							//this.processingStates.push( JsonProcessingState.OBJECT_KEY_NAME );
 							return JsonDocumentItemType.VALUE_KEY;
-						case JsonProcessingState.OBJECT: // we are processing object attribute value elements
+						case OBJECT: // we are processing object attribute value elements
 							return JsonDocumentItemType.VALUE;
-						case JsonProcessingState.OBJECT_KEY_NAME: // we are processing object elements key
+						case OBJECT_KEY_NAME: // we are processing object elements key
 							return JsonDocumentItemType.VALUE_KEY;
 						default:
 							throw new IllegalStateException( "unexpected quote read in current processing state " +
@@ -211,8 +211,8 @@ public JsonDocumentItemType next() {
 								this.jsonString.charAt( this.position )));
 					}
 					switch ( this.processingStates.getCurrent() ) {
-						case JsonProcessingState.ARRAY:
-						case JsonProcessingState.OBJECT:
+						case ARRAY:
+						case OBJECT:
 							return getUnquotedValueType(this.jsonString.charAt( this.jsonValueStart));
 						default:
 							throw new IllegalStateException( "unexpected read ["+

From a95a6e1fbdeafd4e6ce2b32f7f82ee77e2ffa246 Mon Sep 17 00:00:00 2001
From: Christian Beikov <christian.beikov@gmail.com>
Date: Thu, 17 Apr 2025 11:45:51 +0200
Subject: [PATCH 62/81] HHH-17404 Address dialect and aggregate support
 comments

---
 .../dialect/OracleLegacyDialect.java          |  2 +-
 .../org/hibernate/dialect/OracleDialect.java  | 93 +++++--------------
 .../aggregate/OracleAggregateSupport.java     | 80 +++++++---------
 3 files changed, 56 insertions(+), 119 deletions(-)

diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java
index 2670fb217ba1..07e85788a6fa 100644
--- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java
+++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java
@@ -992,7 +992,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this ,true);
+		return OracleAggregateSupport.valueOf( this );
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 6b0cd41fe328..4104d39cc0b3 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -6,10 +6,8 @@
 
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.TemporalType;
-import jakarta.persistence.Timeout;
 import org.hibernate.Length;
 import org.hibernate.QueryTimeoutException;
-import org.hibernate.Timeouts;
 import org.hibernate.boot.model.FunctionContributions;
 import org.hibernate.boot.model.TypeContributions;
 import org.hibernate.cfg.MappingSettings;
@@ -124,13 +122,14 @@
 import static java.util.regex.Pattern.CASE_INSENSITIVE;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
-import static org.hibernate.dialect.DialectLogging.DIALECT_MESSAGE_LOGGER;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getArrayJdbcTypeConstructor;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getNestedTableJdbcTypeConstructor;
+import static org.hibernate.dialect.DialectLogging.DIALECT_LOGGER;
 import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
 import static org.hibernate.internal.util.JdbcExceptionHelper.extractErrorCode;
 import static org.hibernate.internal.util.StringHelper.isEmpty;
 import static org.hibernate.internal.util.StringHelper.isNotEmpty;
+import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
 import static org.hibernate.query.common.TemporalUnit.DAY;
 import static org.hibernate.query.common.TemporalUnit.HOUR;
 import static org.hibernate.query.common.TemporalUnit.MINUTE;
@@ -198,8 +197,6 @@ public class OracleDialect extends Dialect {
 
 	private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 19 );
 
-	private boolean osonExtensionEnabled = false;
-
 	private final OracleUserDefinedTypeExporter userDefinedTypeExporter = new OracleUserDefinedTypeExporter( this );
 	private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this);
 	private final SequenceSupport oracleSequenceSupport = OracleSequenceSupport.getInstance(this);
@@ -223,9 +220,6 @@ protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggr
 	// Is the database accessed using a database service protected by Application Continuity.
 	protected final boolean applicationContinuity;
 
-	// Is the database OSON format should be disabled.
-	protected final boolean isOracleOsonDisabled;
-
 	protected final int driverMajorVersion;
 	protected final int driverMinorVersion;
 	private boolean useBinaryFloat;
@@ -239,7 +233,6 @@ public OracleDialect(DatabaseVersion version) {
 		autonomous = false;
 		extended = false;
 		applicationContinuity = false;
-		isOracleOsonDisabled = false;
 		driverMajorVersion = 19;
 		driverMinorVersion = 0;
 	}
@@ -248,14 +241,13 @@ public OracleDialect(DialectResolutionInfo info) {
 		this( info, OracleServerConfiguration.fromDialectResolutionInfo( info ) );
 	}
 
-	public OracleDialect(DialectResolutionInfo info, OracleServerConfiguration configuration) {
+	public OracleDialect(DialectResolutionInfo info, OracleServerConfiguration serverConfiguration) {
 		super( info );
-		autonomous = configuration.isAutonomous();
-		extended = configuration.isExtended();
-		applicationContinuity = configuration.isApplicationContinuity();
-		isOracleOsonDisabled = configuration.isOSONEnabled();
-		driverMinorVersion = configuration.getDriverMinorVersion();
-		driverMajorVersion = configuration.getDriverMajorVersion();
+		autonomous = serverConfiguration.isAutonomous();
+		extended = serverConfiguration.isExtended();
+		applicationContinuity = serverConfiguration.isApplicationContinuity();
+		this.driverMinorVersion = serverConfiguration.getDriverMinorVersion();
+		this.driverMajorVersion = serverConfiguration.getDriverMajorVersion();
 	}
 
 	public boolean isAutonomous() {
@@ -269,7 +261,6 @@ public boolean isExtended() {
 	public boolean isApplicationContinuity() {
 		return applicationContinuity;
 	}
-	public boolean isOracleOsonDisabled() {return isOracleOsonDisabled;}
 
 	private static boolean isJacksonJsonFormatMapper(ConfigurationService configService) {
 		// Mirror the behavior of SessionFactoryOptionsBuilder#determineJsonFormatMapper
@@ -302,11 +293,10 @@ public void appendBooleanValueString(SqlAppender appender, boolean bool) {
 
 	@Override
 	public void initializeFunctionRegistry(FunctionContributions functionContributions) {
-		super.initializeFunctionRegistry( functionContributions );
+		super.initializeFunctionRegistry(functionContributions);
 		final TypeConfiguration typeConfiguration = functionContributions.getTypeConfiguration();
 
-		final var functionFactory = new CommonFunctionFactory( functionContributions );
-
+		CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
 		functionFactory.ascii();
 		functionFactory.char_chr();
 		functionFactory.cosh();
@@ -983,11 +973,6 @@ public boolean supportsBitType() {
 		return false;
 	}
 
-	@Override
-	public boolean supportsUserDefinedTypes() {
-		return true;
-	}
-
 	@Override
 	public String getArrayTypeName(String javaElementTypeName, String elementTypeName, Integer maxLength) {
 		return ( javaElementTypeName == null ? elementTypeName : javaElementTypeName ) + "Array";
@@ -1032,29 +1017,21 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 		}
 
 
-		final String mapperName = configurationService.getSetting( "hibernate.type.json_format_mapper",
-				StandardConverters.STRING,JACKSON_MAPPER_NAME);
-
 		if ( getVersion().isSameOrAfter( 21 ) ) {
-
-			if ( !isOracleOsonDisabled() && JacksonIntegration.isOracleOsonExtensionAvailable() && JACKSON_MAPPER_NAME.equalsIgnoreCase( mapperName )) {
+			final boolean osonDisabled = getBoolean( ORACLE_OSON_DISABLED , configurationService.getSettings() );
+			if ( !osonDisabled && JacksonIntegration.isOracleOsonExtensionAvailable() && isJacksonJsonFormatMapper( configurationService )) {
 				// We must check that that extension is available and actually used.
 				typeContributions.contributeJdbcType( OracleOsonJacksonJdbcType.INSTANCE );
 				typeContributions.contributeJdbcTypeConstructor( OracleOsonArrayJdbcTypeConstructor.INSTANCE );
 
-				DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
-						"Oracle OSON Jackson extension used" );
-				osonExtensionEnabled = true;
+				DIALECT_LOGGER.log( Logger.Level.DEBUG, "Oracle OSON Jackson extension used" );
 			}
 			else {
-				if (DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.isDebugEnabled()) {
-					DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
-							"Oracle OSON Jackson extension not used" );
-					DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
+				if (DIALECT_LOGGER.isDebugEnabled()) {
+					DIALECT_LOGGER.log( Logger.Level.DEBUG, "Oracle OSON Jackson extension not used" );
+					DIALECT_LOGGER.log( Logger.Level.DEBUG,
 							"JacksonIntegration.isOracleOsonExtensionAvailable(): " +
 									JacksonIntegration.isOracleOsonExtensionAvailable());
-					DIALECT_MESSAGE_LOGGER.DIALECT_LOGGER.log( Logger.Level.DEBUG,
-							"hibernate.type.json_format_mapper : " + mapperName);
 				}
 				typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
 				typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE );
@@ -1105,7 +1082,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 
 	@Override
 	public AggregateSupport getAggregateSupport() {
-		return OracleAggregateSupport.valueOf( this ,!osonExtensionEnabled );
+		return OracleAggregateSupport.valueOf( this );
 	}
 
 	@Override
@@ -1521,36 +1498,12 @@ public String getForUpdateSkipLockedString(String aliases) {
 		return " for update of " + aliases + " skip locked";
 	}
 
-	private String withTimeout(String lockString, Timeout timeout) {
-		return withTimeout( lockString, timeout.milliseconds() );
-	}
-
-	@Override
-	public String getWriteLockString(Timeout timeout) {
-		return withTimeout( getForUpdateString(), timeout );
-	}
-
-	@Override
-	public String getWriteLockString(String aliases, Timeout timeout) {
-		return withTimeout( getForUpdateString(aliases), timeout );
-	}
-
-	@Override
-	public String getReadLockString(Timeout timeout) {
-		return getWriteLockString( timeout );
-	}
-
-	@Override
-	public String getReadLockString(String aliases, Timeout timeout) {
-		return getWriteLockString( aliases, timeout );
-	}
-
 	private String withTimeout(String lockString, int timeout) {
 		return switch (timeout) {
-			case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString;
-			case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString;
-			case Timeouts.WAIT_FOREVER_MILLI -> lockString;
-			default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString;
+			case NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString;
+			case SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString;
+			case WAIT_FOREVER -> lockString;
+			default -> supportsWait() ? lockString + " wait " + getTimeoutInSeconds( timeout ) : lockString;
 		};
 	}
 
@@ -1726,10 +1679,10 @@ public String generatedAs(String generatedAs) {
 	}
 
 	@Override
-	public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData metadata)
+	public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
 			throws SQLException {
 		builder.setAutoQuoteInitialUnderscore( true );
-		return super.buildIdentifierHelper( builder, metadata );
+		return super.buildIdentifierHelper( builder, dbMetaData );
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
index d4c296ff0c13..55f290752e0b 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java
@@ -49,14 +49,13 @@
 
 public class OracleAggregateSupport extends AggregateSupportImpl {
 
-	protected static final AggregateSupport V23_INSTANCE = new OracleAggregateSupport( true, JsonSupport.OSON, true );
+	protected static final AggregateSupport V23_INSTANCE = new OracleAggregateSupport( true, JsonSupport.OSON );
 	// Special instance used when an Oracle OSON extension is available and used
-	protected static final AggregateSupport V23_OSON_EXT_INSTANCE = new OracleAggregateSupport( true, JsonSupport.OSON,false);
-	protected static final AggregateSupport V21_INSTANCE = new OracleAggregateSupport( false, JsonSupport.OSON, true );
-	protected static final AggregateSupport V19_INSTANCE = new OracleAggregateSupport( false, JsonSupport.MERGEPATCH , true);
-	protected static final AggregateSupport V18_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY_AND_PATH, true );
-	protected static final AggregateSupport V12_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY , true);
-	protected static final AggregateSupport LEGACY_INSTANCE = new OracleAggregateSupport( false, JsonSupport.NONE , true);
+	protected static final AggregateSupport V21_INSTANCE = new OracleAggregateSupport( false, JsonSupport.OSON );
+	protected static final AggregateSupport V19_INSTANCE = new OracleAggregateSupport( false, JsonSupport.MERGEPATCH );
+	protected static final AggregateSupport V18_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY_AND_PATH );
+	protected static final AggregateSupport V12_INSTANCE = new OracleAggregateSupport( false, JsonSupport.QUERY );
+	protected static final AggregateSupport LEGACY_INSTANCE = new OracleAggregateSupport( false, JsonSupport.NONE );
 
 	private static final String JSON_QUERY_START = "json_query(";
 	private static final String JSON_QUERY_JSON_END = "' returning json)";
@@ -70,17 +69,13 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
 
 	private final boolean checkConstraintSupport;
 	private final JsonSupport jsonSupport;
-	private final boolean dateTypesStoreAsString;
 
-	private OracleAggregateSupport(boolean checkConstraintSupport, JsonSupport jsonSupport, boolean dateTypesStoreAsString) {
+	private OracleAggregateSupport(boolean checkConstraintSupport, JsonSupport jsonSupport) {
 		this.checkConstraintSupport = checkConstraintSupport;
 		this.jsonSupport = jsonSupport;
-		// this flag tell us if data is serialized/de-serialized as String. As opposed to using OSON
-		// In other words, this flag tells us if the Oracle OSON JDBC extension is used or not.
-		this.dateTypesStoreAsString = dateTypesStoreAsString;
 	}
 
-	public static AggregateSupport valueOf(Dialect dialect, boolean useDateStoredAsString) {
+	public static AggregateSupport valueOf(Dialect dialect) {
 		final DatabaseVersion version = dialect.getVersion();
 		return switch ( version.getMajor() ) {
 			case 12, 13, 14, 15, 16, 17 -> V12_INSTANCE;
@@ -88,12 +83,16 @@ public static AggregateSupport valueOf(Dialect dialect, boolean useDateStoredAsS
 			case 19, 20 -> V19_INSTANCE;
 			case 21, 22 -> V21_INSTANCE;
 			default -> version.isSameOrAfter( 23 )
-				? useDateStoredAsString?OracleAggregateSupport.V23_INSTANCE:
-				OracleAggregateSupport.V23_OSON_EXT_INSTANCE
-								: OracleAggregateSupport.LEGACY_INSTANCE;
+					? OracleAggregateSupport.V23_INSTANCE
+					: OracleAggregateSupport.LEGACY_INSTANCE;
 		};
 	}
 
+	private boolean supportsOson() {
+		// OSON is supported when check constraints are supported
+		return checkConstraintSupport;
+	}
+
 	@Override
 	public String aggregateComponentCustomReadExpression(
 			String template,
@@ -147,17 +146,17 @@ public String aggregateComponentCustomReadExpression(
 								);
 
 							case DATE:
-								if (this.dateTypesStoreAsString) {
+								if (supportsOson()) {
+									// Oracle OSON extension is used, value is not stored as string
 									return template.replace(
 											placeholder,
-											"to_date(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD')"
+											"json_value(" + parentPartExpression + columnExpression + "' returning date)"
 									);
 								}
 								else {
-									// Oracle OSON extension is used, value is not stored as string
 									return template.replace(
 											placeholder,
-											"json_value(" + parentPartExpression + columnExpression + "' returning date)"
+											"to_date(substr(json_value(" + parentPartExpression + columnExpression + "'),1,10),'YYYY-MM-DD')"
 									);
 								}
 
@@ -167,45 +166,31 @@ public String aggregateComponentCustomReadExpression(
 										"to_timestamp(json_value(" + parentPartExpression + columnExpression + "'),'hh24:mi:ss')"
 								);
 							case TIMESTAMP:
-								if (this.dateTypesStoreAsString) {
-									return template.replace(
-											placeholder,
-											"to_timestamp(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9')"
-									);
-								}
-								else {
-
-									return template.replace(
-											placeholder,
-											"json_value(" + parentPartExpression + columnExpression + "' returning timestamp)"
-									);
-								}
-							case DURATION:
-								if (this.dateTypesStoreAsString) {
+								if (supportsOson()) {
 									return template.replace(
 											placeholder,
-											"cast(json_value(" + parentPartExpression + columnExpression + "') as " + column.getColumnDefinition() + ')'
+											"json_value(" + parentPartExpression + columnExpression + "' returning timestamp(9))"
 									);
 								}
 								else {
 									return template.replace(
 											placeholder,
-											"json_value(" + parentPartExpression + columnExpression + "' returning interval day to second)"
+											"to_timestamp(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9')"
 									);
 								}
 							case TIMESTAMP_WITH_TIMEZONE:
 							case TIMESTAMP_UTC:
-								if (this.dateTypesStoreAsString) {
+								if (supportsOson()) {
+									// Oracle OSON extension is used, value is not stored as string
 									return template.replace(
 											placeholder,
-											"to_timestamp_tz(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9TZH:TZM')"
+											"json_value(" + parentPartExpression + columnExpression + "' returning timestamp(9) with time zone)"
 									);
 								}
 								else {
-									// Oracle OSON extension is used, value is not stored as string
 									return template.replace(
 											placeholder,
-											"json_value(" + parentPartExpression + columnExpression + "')"
+											"to_timestamp_tz(json_value(" + parentPartExpression + columnExpression + "'),'YYYY-MM-DD\"T\"hh24:mi:ss.FF9TZH:TZM')"
 									);
 								}
 							case UUID:
@@ -236,13 +221,8 @@ public String aggregateComponentCustomReadExpression(
 								final BasicPluralType<?, ?> pluralType = (BasicPluralType<?, ?>) column.getJdbcMapping();
 								final OracleArrayJdbcType jdbcType = (OracleArrayJdbcType) pluralType.getJdbcType();
 								switch ( jdbcType.getElementJdbcType().getDefaultSqlTypeCode() ) {
-
-									case DATE:
-										return template.replace(
-												placeholder,
-												"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
-										);
 									case BOOLEAN:
+									case DATE:
 									case TIME:
 									case TIMESTAMP:
 									case TIMESTAMP_WITH_TIMEZONE:
@@ -251,11 +231,15 @@ public String aggregateComponentCustomReadExpression(
 									case VARBINARY:
 									case LONG32VARBINARY:
 									case UUID:
-									default:
 										return template.replace(
 												placeholder,
 												jdbcType.getSqlTypeName() + "_from_json(json_query(" + parentPartExpression + columnExpression + "' returning " + jsonTypeName + "))"
 										);
+									default:
+										return template.replace(
+												placeholder,
+												"json_value(" + parentPartExpression + columnExpression + "' returning " + column.getColumnDefinition() + ')'
+										);
 								}
 							case JSON:
 							case JSON_ARRAY:

From 44048c3cc97892b10d06ba29658379ac9c21c662 Mon Sep 17 00:00:00 2001
From: Christian Beikov <christian.beikov@gmail.com>
Date: Thu, 17 Apr 2025 11:52:59 +0200
Subject: [PATCH 63/81] HHH-17404 Address issues in StringJsonDocumentReader

---
 .../type/format/StringJsonDocumentReader.java | 32 ++++++-------------
 1 file changed, 10 insertions(+), 22 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index 7b025facacb2..fa034cc4c594 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -222,8 +222,10 @@ public JsonDocumentItemType next() {
 					}
 			}
 		}
-		// no way we get here.
-		return null;
+		throw new IllegalStateException( "unexpected end of JSON ["+
+										this.jsonString.substring( this.jsonValueStart,this.jsonValueEnd )+
+										"] in current processing state " +
+										this.processingStates.getCurrent() );
 	}
 
 	/**
@@ -295,31 +297,17 @@ private void moveTo(char character) throws IllegalStateException {
 		throw new IllegalStateException("character [" + character + "] is not the next non-blank character");
 	}
 
-	/**
-	 * Goes through the JSON string to locate a non-escaped instance of a given character.
-	 * Ex: on 'AB\"C"' this method returns 5 (not 3)
-	 *
-	 * @param character character to be found
-	 * @return the position of the character or -1 if not found.
-	 */
-	private int locateCharacter(char character) {
+	private int nextQuote() {
 		int pointer = this.position;
 
 		while ( pointer < this.limit) {
 			final char c = this.jsonString.charAt( pointer );
 			if (c == ESCAPE_CHAR) {
-				// We encountered an escape character.
-				// We should just skip the next one as it is either the expected character
-				// but as escaped one, we should ignore it, either this is something else
-				// and we should ignore it also
-				pointer += 2;
-				continue;
+				pointer++;
 			}
-			else {
-				if ( c == character ) {
-					// found
-					return pointer;
-				}
+			else if (c == '"') {
+				// found
+				return pointer;
 			}
 			pointer++;
 		}
@@ -366,7 +354,7 @@ private void consumeQuotedString() {
 		this.position++;
 
 		//locate ending quote
-		int endingQuote = locateCharacter( StringJsonDocumentMarker.QUOTE.getMarkerCharacter());
+		int endingQuote = nextQuote();
 		if (endingQuote == -1) {
 			throw new IllegalStateException("Can't find ending quote of key name");
 		}

From b6f711ac1a1c32faa591b1803b7442633d133d26 Mon Sep 17 00:00:00 2001
From: Christian Beikov <christian.beikov@gmail.com>
Date: Thu, 17 Apr 2025 14:12:33 +0200
Subject: [PATCH 64/81] HHH-17404 Cleanup JsonHelper and fix nested parsing

---
 .../OracleOsonJacksonArrayJdbcType.java       |  73 ++-
 .../dialect/OracleOsonJacksonJdbcType.java    |  61 +-
 .../descriptor/jdbc/JsonArrayJdbcType.java    |  34 +-
 .../type/descriptor/jdbc/JsonHelper.java      | 542 +++++++-----------
 .../type/descriptor/jdbc/JsonJdbcType.java    |  18 +-
 .../type/format/StringJsonDocumentWriter.java | 200 ++++++-
 .../embeddable/JsonEmbeddableArrayTest.java   |   2 +-
 .../util/StringJsonDocumentWriterTest.java    |  30 +-
 8 files changed, 506 insertions(+), 454 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
index be5d13b46df7..28ee7d868faf 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
@@ -4,16 +4,12 @@
  */
 package org.hibernate.dialect;
 
-import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
 import oracle.jdbc.driver.DatabaseError;
-import oracle.jdbc.provider.oson.OsonFactory;
 import oracle.sql.json.OracleJsonDatum;
-import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
 
-import oracle.sql.json.OracleJsonParser;
 import org.hibernate.internal.CoreLogging;
 import org.hibernate.internal.CoreMessageLogger;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@@ -22,12 +18,12 @@
 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.JsonJdbcType;
-import org.hibernate.type.format.FormatMapper;
 import org.hibernate.type.format.OsonDocumentReader;
 import org.hibernate.type.format.OsonDocumentWriter;
 
@@ -39,6 +35,9 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
+import static org.hibernate.dialect.OracleOsonJacksonJdbcType.OSON_JACKSON_FACTORY;
+import static org.hibernate.dialect.OracleOsonJacksonJdbcType.OSON_JSON_FACTORY;
+
 /**
  *
  * Type mapping of (JSON) array of JSON SQL data type for Oracle database.
@@ -52,8 +51,6 @@ public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
 
 	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonArrayJdbcType.class );
 
-	private static final OsonFactory osonFactory = new OsonFactory();
-
 	public OracleOsonJacksonArrayJdbcType(JdbcType elementJdbcType) {
 		super(elementJdbcType);
 	}
@@ -70,29 +67,31 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 		return new BasicBinder<>( javaType, this ) {
 
-			private <X> byte[] toOsonStream(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
+			private <T> byte[] toOsonStream(T value, JavaType<T> javaType, WrapperOptions options) throws Exception {
 				final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
-
-				ByteArrayOutputStream out = new ByteArrayOutputStream();
-				try (OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out )) {
-					OsonDocumentWriter writer = new OsonDocumentWriter( generator );
-
-					if ( getElementJdbcType() instanceof JsonJdbcType jsonElementJdbcType ) {
-						final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
-						JsonHelper.serializeArray( embeddableMappingType, domainObjects, options, writer );
+				final ByteArrayOutputStream out = new ByteArrayOutputStream();
+				try (OracleJsonGenerator generator = OSON_JSON_FACTORY.createJsonBinaryGenerator( out )) {
+					final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) javaType).getElementJavaType();
+					if ( elementJavaType instanceof UnknownBasicJavaType<?> ) {
+						options.getJsonFormatMapper().writeToTarget( value, javaType, generator, options);
 					}
 					else {
-						assert !( getElementJdbcType() instanceof AggregateJdbcType );
-						final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
-						JsonHelper.serializeArray(
-								elementJavaType,
-								getElementJdbcType(),
-								domainObjects,
-								options,
-								writer
-						);
+						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
+							);
+						}
 					}
-					generator.close();
 					return out.toByteArray();
 				}
 
@@ -127,20 +126,20 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
-				FormatMapper mapper = options.getJsonFormatMapper();
-				OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( osonBytes );
-				final JdbcType elementJdbcType = getElementJdbcType();
-				if (elementJdbcType instanceof JsonJdbcType) {
-					if (((JsonJdbcType) elementJdbcType).getEmbeddableMappingType() != null) {
-						// embeddable array case.
-						return JsonHelper.deserializeArray( javaType,
-								elementJdbcType, new OsonDocumentReader( osonParser ), options );
+				if ( ((BasicPluralJavaType<?>) getJavaType()).getElementJavaType() instanceof UnknownBasicJavaType<?> ) {
+					try (JsonParser oParser = OSON_JACKSON_FACTORY.createParser( osonBytes )) {
+						return options.getJsonFormatMapper().readFromSource( getJavaType(), oParser, options );
 					}
 				}
-				try (JsonParser oParser = ((JsonFactory)osonFactory).createParser(  osonBytes )) {
-					return mapper.readFromSource(  getJavaType(), oParser, options);
+				else {
+					// embeddable array case.
+					return JsonHelper.deserializeArray(
+							javaType,
+							getElementJdbcType(),
+							new OsonDocumentReader( OSON_JSON_FACTORY.createJsonBinaryParser( osonBytes ) ),
+							options
+					);
 				}
-
 			}
 
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
index fbff6af6a8ad..d2a1894a30c8 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
@@ -4,7 +4,6 @@
  */
 package org.hibernate.dialect;
 
-import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
@@ -13,7 +12,6 @@
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
-import oracle.sql.json.OracleJsonParser;
 import org.hibernate.internal.CoreLogging;
 import org.hibernate.internal.CoreMessageLogger;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@@ -25,7 +23,6 @@
 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.format.FormatMapper;
 import org.hibernate.type.format.OsonDocumentReader;
 import org.hibernate.type.format.OsonDocumentWriter;
 
@@ -50,7 +47,8 @@ public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
 
 	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonJdbcType.class );
 
-	private static final OsonFactory osonFactory = new OsonFactory();
+	static final OsonFactory OSON_JACKSON_FACTORY = new OsonFactory();
+	static final OracleJsonFactory OSON_JSON_FACTORY = new OracleJsonFactory();
 
 	private OracleOsonJacksonJdbcType(EmbeddableMappingType embeddableMappingType) {
 		super( embeddableMappingType );
@@ -78,23 +76,23 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 		return new BasicBinder<>( javaType, this ) {
 
-			private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options) throws Exception {
-
-				FormatMapper mapper = options.getJsonFormatMapper();
-
+			private <T> byte[] toOson(T value, JavaType<T> javaType, WrapperOptions options) throws Exception {
+				final ByteArrayOutputStream out = new ByteArrayOutputStream();
 				if (getEmbeddableMappingType() != null) {
-					ByteArrayOutputStream out = new ByteArrayOutputStream();
 					// OracleJsonFactory is used and not OracleOsonFactory as Jackson is not involved here
-					try (OracleJsonGenerator generator = new OracleJsonFactory().createJsonBinaryGenerator( out )) {
-						OsonDocumentWriter writer = new OsonDocumentWriter( generator);
-						JsonHelper.serialize( getEmbeddableMappingType(), value,options,writer);
+					try (OracleJsonGenerator generator = OSON_JSON_FACTORY.createJsonBinaryGenerator( out )) {
+						JsonHelper.serialize(
+								getEmbeddableMappingType(),
+								value,
+								options,
+								new OsonDocumentWriter( generator )
+						);
 					}
-					return out.toByteArray();
 				}
-
-				ByteArrayOutputStream out = new ByteArrayOutputStream();
-				try (JsonGenerator osonGen = osonFactory.createGenerator( out )) {
-					mapper.writeToTarget( value, javaType, osonGen, options );
+				else {
+					try (JsonGenerator osonGen = OSON_JACKSON_FACTORY.createGenerator( out )) {
+						options.getJsonFormatMapper().writeToTarget( value, javaType, osonGen, options );
+					}
 				}
 				return out.toByteArray();
 			}
@@ -133,33 +131,18 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 		return new BasicExtractor<>( javaType, this ) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
-
-				FormatMapper mapper = options.getJsonFormatMapper();
-
-				if (getEmbeddableMappingType() != null &&
-						getJavaType().getJavaTypeClass() == Object[].class) {
-					// We are dealing with embeddable (@Embeddable) and we request
-					// an array of objects. We use JsonParser to fetch values
-					// and build the array.(as opposed to let Jackson do it as we do not
-					// have a proper object definition at that stage).
-					OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( osonBytes );
-					Object[] objects =  JsonHelper.deserialize(
+				if ( getEmbeddableMappingType() != null ) {
+					return JsonHelper.deserialize(
 							getEmbeddableMappingType(),
-							new OsonDocumentReader(osonParser),
+							new OsonDocumentReader( OSON_JSON_FACTORY.createJsonBinaryParser( osonBytes ) ),
 							javaType.getJavaTypeClass() != Object[].class,
 							options
 					);
-					return (X) objects;
 				}
-
-				JavaType <X> type = getJavaType();
-				if (getEmbeddableMappingType() != null) {
-					// We are dealing with embeddable (@Embeddable)
-					type = (JavaType<X>) getEmbeddableMappingType().getJavaType();
-				}
-
-				try (JsonParser osonParser = ((JsonFactory)osonFactory).createParser(  osonBytes )) {
-					return mapper.readFromSource( type, osonParser, options );
+				else {
+					try (JsonParser osonParser = OSON_JACKSON_FACTORY.createParser( osonBytes )) {
+						return options.getJsonFormatMapper().readFromSource( getJavaType(), osonParser, options );
+					}
 				}
 			}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java
index 6e2b3e640dab..5784a5efd18d 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonArrayJdbcType.java
@@ -16,6 +16,7 @@
 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.format.StringJsonDocumentWriter;
 
 /**
@@ -59,24 +60,33 @@ protected <X> X fromString(String string, JavaType<X> javaType, WrapperOptions o
 		if ( string == null ) {
 			return null;
 		}
-		return JsonHelper.arrayFromString( javaType, this.getElementJdbcType(), string, options );
+		if ( ((BasicPluralJavaType<?>) javaType).getElementJavaType() instanceof UnknownBasicJavaType<?> ) {
+			return options.getJsonFormatMapper().fromString( string, javaType, options );
+		}
+		else {
+			return JsonHelper.arrayFromString( javaType, this.getElementJdbcType(), string, options );
+		}
 	}
 
 	protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) {
-		final JdbcType elementJdbcType = getElementJdbcType();
-		final Object[] domainObjects = javaType.unwrap( value, Object[].class, options );
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
-		if ( elementJdbcType instanceof JsonJdbcType jsonElementJdbcType ) {
-			final EmbeddableMappingType embeddableMappingType = jsonElementJdbcType.getEmbeddableMappingType();
-			JsonHelper.serializeArray( embeddableMappingType, domainObjects, options,  writer);
+		final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
+		if ( elementJavaType instanceof UnknownBasicJavaType<?> ) {
+			return options.getJsonFormatMapper().toString( value, javaType, options);
 		}
 		else {
-			assert !( elementJdbcType instanceof AggregateJdbcType );
-			final JavaType<?> elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
-			JsonHelper.serializeArray( elementJavaType, elementJdbcType, domainObjects, options, writer );
+			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 );
+			}
+			return writer.getJson();
 		}
-		return sb.toString();
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index abbd2512b2e1..52274ed6654b 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -6,7 +6,6 @@
 
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.lang.reflect.Array;
 import java.sql.SQLException;
 import java.util.AbstractCollection;
@@ -17,16 +16,18 @@
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
 import org.hibernate.Internal;
 import org.hibernate.internal.build.AllowReflection;
 import org.hibernate.internal.util.collections.ArrayHelper;
 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.sql.ast.spi.SqlAppender;
 import org.hibernate.type.BasicPluralType;
 import org.hibernate.type.BasicType;
 import org.hibernate.type.SqlTypes;
@@ -238,32 +239,80 @@ else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
 	 */
 	private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, EmbeddableMappingType embeddableMappingType, boolean returnEmbeddable, WrapperOptions options)
 			throws SQLException {
-		// final result of a mapped object array
-		Object [] objectArrayResult;
-		// current mapping to be used
-		SelectableMapping currentSelectableMapping = null;
-		String currentKeyName = null;
-		List<Object> subArrayObjectList = null;
-		BasicPluralType<?, ?> subArrayObjectTypes = null;
-
-		// mapping definitions are in a tree
-		// Each mapping definition may contain sub mappings (sub embeddable mapping)
-		// This stack is used to keep a pointer on the current mapping to be used to assign correct types.
-		// see onStartObject()/onEndObject() methods
-		StandardStack<EmbeddableMappingType> embeddableMappingTypes = new StandardStack<>();
-		// As for mapping definitions, when "sub embeddable" is encountered, the array
-		// that needs to be filled with Objects is the one we allocate in the final result array slot.
-		// We use a stack to keep track of array ref
-		StandardStack<Object[]> objectArrays = new StandardStack<>();
-
-		// index within objectArrayResult
-		int currentSelectableIndexInResultArray = -1;
-
-		JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,returnEmbeddable);
-
-		embeddableMappingTypes.push(embeddableMappingType);
-		objectArrayResult = new Object[embeddableMappingType.getJdbcValueCount()+ ( embeddableMappingType.isPolymorphic() ? 1 : 0 )];
-		objectArrays.push( objectArrayResult );
+		record SelectableData(String selectableName, int selectableIndex, SelectableMapping selectableMapping){}
+		record ParseLevel(
+				@Nullable SelectableData selectableData,
+				@Nullable EmbeddableMappingType embeddableMappingType,
+				@Nullable BasicPluralType<?, ?> arrayType,
+				@Nullable List<Object> subArrayObjectList,
+				@Nullable Object [] objectArray
+		) {
+			ParseLevel(EmbeddableMappingType embeddableMappingType) {
+				this(null, embeddableMappingType);
+			}
+			ParseLevel(@Nullable SelectableData selectableData, EmbeddableMappingType embeddableMappingType) {
+				this(
+						selectableData,
+						embeddableMappingType,
+						null,
+						null,
+						new Object[embeddableMappingType.getJdbcValueCount()+ ( embeddableMappingType.isPolymorphic() ? 1 : 0 )]
+				);
+			}
+			ParseLevel(@Nullable SelectableData selectableData, BasicPluralType<?, ?> arrayType) {
+				this( selectableData, null, arrayType, new ArrayList<>(), null );
+			}
+
+			public void addValue(@Nullable SelectableData selectableData, @Nullable Object value) {
+				if ( embeddableMappingType != null ) {
+					assert selectableData != null;
+					objectArray[selectableData.selectableIndex] = value;
+				}
+				else {
+					assert subArrayObjectList != null;
+					subArrayObjectList.add(value);
+				}
+			}
+
+			public JdbcMapping determineJdbcMapping(@Nullable SelectableData currentSelectableData) {
+				if ( currentSelectableData != null ) {
+					return currentSelectableData.selectableMapping.getJdbcMapping();
+				}
+				else if ( arrayType != null ) {
+					return arrayType.getElementType();
+				}
+				else {
+					assert selectableData != null;
+					return selectableData.selectableMapping.getJdbcMapping();
+				}
+			}
+
+			public static String determineSelectablePath(StandardStack<ParseLevel> parseLevel, @Nullable SelectableData currentSelectableData) {
+				if ( currentSelectableData != null ) {
+					return currentSelectableData.selectableName;
+				}
+				else {
+					return determineSelectablePath( parseLevel, 0 );
+				}
+			}
+
+			private static String determineSelectablePath(StandardStack<ParseLevel> stack, int level) {
+				final ParseLevel parseLevel = stack.peek( level );
+				assert parseLevel != null;
+				if ( parseLevel.selectableData != null ) {
+					return parseLevel.selectableData.selectableName;
+				}
+				else {
+					assert parseLevel.arrayType != null;
+					return determineSelectablePath( stack, level + 1 ) + ".{element}";
+				}
+			}
+		}
+		final StandardStack<ParseLevel> parseLevel = new StandardStack<>();
+		final JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,returnEmbeddable);
+
+		parseLevel.push(new ParseLevel( embeddableMappingType ));
+		SelectableData currentSelectableData = null;
 
 		// We loop on two conditions:
 		//   - the parser still has tokens left
@@ -273,117 +322,141 @@ private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, Embedda
 		// the array is not empty, but we ae done parsing that specific object.
 		// When we encounter OBJECT_END the current type is popped out of the stack. When parsing one object of an array we may end up
 		// having an empty stack. Next Objects are parsed in the next round.
-		while(reader.hasNext() && !embeddableMappingTypes.isEmpty()) {
-			JsonDocumentItemType type = reader.next();
-			switch (type) {
-				case VALUE_KEY:
-					currentKeyName = reader.getObjectKeyName();
-
-					currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
-					if ( currentSelectableIndexInResultArray >= 0 ) {
-						// we may not have a selectable mapping for that key
-						currentSelectableMapping = embeddableMappingTypes.getCurrent().getJdbcValueSelectable( currentSelectableIndexInResultArray );
-					}
-					else {
+		while(reader.hasNext() && !parseLevel.isEmpty()) {
+			final ParseLevel currentLevel = parseLevel.getCurrent();
+			assert currentLevel != null;
+			switch (reader.next()) {
+				case VALUE_KEY -> {
+					final EmbeddableMappingType currentEmbeddableMappingType = currentLevel.embeddableMappingType;
+					assert currentEmbeddableMappingType != null
+							: "Value keys are only valid for objects";
+
+					assert currentSelectableData == null;
+
+					final String selectableName = reader.getObjectKeyName();
+					final int selectableIndex = currentEmbeddableMappingType.getSelectableIndex( selectableName );
+					if ( selectableIndex < 0 ) {
 						throw new IllegalArgumentException(
 								String.format(
 										"Could not find selectable [%s] in embeddable type [%s] for JSON processing.",
-										currentKeyName,
-										embeddableMappingTypes.getCurrent().getMappedJavaType().getJavaTypeClass().getName()
+										selectableName,
+										currentEmbeddableMappingType.getMappedJavaType().getJavaTypeClass().getName()
 								)
 						);
 					}
-					break;
-				case ARRAY_START:
-					assert (subArrayObjectList == null && subArrayObjectTypes == null) : "ARRAY_START item received twice in a row";
-
-					// initialize an array to gather values
-					subArrayObjectList = new ArrayList<>();
-					assert (currentSelectableMapping.getJdbcMapping() instanceof BasicPluralType<?, ?>)
-							: "Array event received for non plural type";
-					// initialize array's element type
-					subArrayObjectTypes = (BasicPluralType<?, ?>) currentSelectableMapping.getJdbcMapping();
-					break;
-				case ARRAY_END:
-					assert (subArrayObjectList != null && subArrayObjectTypes != null) : "ARRAY_END item received twice in a row";
+					final SelectableMapping selectableMapping =
+							currentEmbeddableMappingType.getJdbcValueSelectable( selectableIndex );
+					currentSelectableData = new SelectableData( selectableName, selectableIndex, selectableMapping );
+				}
+				case ARRAY_START -> {
+					assert currentSelectableData != null;
+
+					if ( !(currentSelectableData.selectableMapping.getJdbcMapping() instanceof BasicPluralType<?, ?> pluralType) ) {
+						throw new IllegalArgumentException(
+								String.format(
+										"Can't parse JSON array for selectable [%s] which is not of type BasicPluralType.",
+										ParseLevel.determineSelectablePath( parseLevel, currentSelectableData )
+								)
+						);
+					}
+					parseLevel.push( new ParseLevel( currentSelectableData, pluralType ) );
+					currentSelectableData = null;
+				}
+				case ARRAY_END -> {
+					assert currentLevel.arrayType != null;
+					assert currentLevel.selectableData != null;
+
+					parseLevel.pop();
+					final ParseLevel parentLevel = parseLevel.getCurrent();
+
+					assert parentLevel.embeddableMappingType != null;
 					// flush array values
-					objectArrays.getCurrent()[currentSelectableIndexInResultArray] = subArrayObjectTypes.getJdbcJavaType().wrap( subArrayObjectList, options );
-					// reset until we encounter next array element
-					subArrayObjectList = null;
-					subArrayObjectTypes = null;
-					break;
-				case OBJECT_START:
-					if (currentKeyName != null) {
-						// We are dealing with a sub-object, allocate space for it then,
-						// otherwise, we have nothing to do.
-						// Push the new (sub)mapping definition.
-						assert embeddableMappingTypes.getCurrent() != null;
-						currentSelectableIndexInResultArray = embeddableMappingTypes.getCurrent().getSelectableIndex( currentKeyName );
-						assert currentSelectableIndexInResultArray != -1: "Cannot get index of " + currentKeyName;
-
-						final SelectableMapping selectable = embeddableMappingTypes.getCurrent().getJdbcValueSelectable(
-								currentSelectableIndexInResultArray );
-						final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) selectable.getJdbcMapping()
-								.getJdbcType();
-						final EmbeddableMappingType subMappingType = aggregateJdbcType.getEmbeddableMappingType();
-						assert objectArrays.getCurrent() != null;
-						objectArrays.getCurrent()[currentSelectableIndexInResultArray] =
-								new Object[subMappingType.getJdbcValueCount()];
-						embeddableMappingTypes.push( subMappingType );
-						objectArrays.push( (Object[]) objectArrays.getCurrent()[currentSelectableIndexInResultArray] );
+					parentLevel.addValue(
+							currentLevel.selectableData,
+							currentLevel.arrayType.getJdbcJavaType().wrap( currentLevel.subArrayObjectList, options )
+					);
+				}
+				case OBJECT_START -> {
+					final JdbcMapping jdbcMapping = currentLevel.determineJdbcMapping( currentSelectableData );
+
+					if ( !(jdbcMapping.getJdbcType() instanceof AggregateJdbcType aggregateJdbcType) ) {
+						throw new IllegalArgumentException(
+								String.format(
+										"Can't parse JSON object for selectable [%s] which is not of type AggregateJdbcType.",
+										ParseLevel.determineSelectablePath( parseLevel, currentSelectableData )
+								)
+						);
 					}
-					break;
-				case OBJECT_END:
+					parseLevel.push(
+							new ParseLevel( currentSelectableData, aggregateJdbcType.getEmbeddableMappingType() ) );
+					currentSelectableData = null;
+				}
+				case OBJECT_END -> {
+					final EmbeddableMappingType currentEmbeddableMappingType = currentLevel.embeddableMappingType;
+					assert currentEmbeddableMappingType != null;
+
 					// go back in the mapping definition tree
-					embeddableMappingTypes.pop();
-					objectArrays.pop();
-					break;
-				case NULL_VALUE:
-					if ( subArrayObjectList != null ) {
-						// dealing with arrays
-						subArrayObjectList.add( null );
-					}
-					else {
-						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = null;
-					}
-					break;
-				case NUMERIC_VALUE:
-					if ( subArrayObjectList != null ) {
-						// dealing with arrays
-						subArrayObjectList.add( adapter.fromNumericValue( subArrayObjectTypes.getElementType().getJdbcJavaType(),
-								subArrayObjectTypes.getElementType().getJdbcType(),reader,options));
-					}
-					else {
-						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = adapter.fromNumericValue( currentSelectableMapping.getJdbcMapping().getJdbcJavaType(),
-								currentSelectableMapping.getJdbcMapping().getJdbcType(),reader,options);
-					}
-					break;
-				case BOOLEAN_VALUE:
-					if ( subArrayObjectList != null ) {
-						// dealing with arrays
-						subArrayObjectList.add( reader.getBooleanValue()?Boolean.TRUE:Boolean.FALSE);
+					parseLevel.pop();
+					final Object objectValue;
+					if ( returnEmbeddable ) {
+						final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
+								embeddableMappingType,
+								currentLevel.objectArray,
+								options
+						);
+						objectValue = instantiate( embeddableMappingType, attributeValues );
 					}
 					else {
-						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = reader.getBooleanValue()?Boolean.TRUE:Boolean.FALSE;
+						objectValue = currentLevel.objectArray;
 					}
-					break;
-				case VALUE:
-					if ( subArrayObjectList != null ) {
-						// dealing with arrays
-						subArrayObjectList.add(adapter.fromValue( subArrayObjectTypes.getElementType().getJdbcJavaType(),
-								subArrayObjectTypes.getElementType().getJdbcType(),reader,options));
+					if ( parseLevel.isEmpty() ) {
+						//noinspection unchecked
+						return (X) objectValue;
 					}
 					else {
-						objectArrays.getCurrent()[currentSelectableIndexInResultArray] = adapter.fromValue( currentSelectableMapping.getJdbcMapping().getJdbcJavaType(),
-								currentSelectableMapping.getJdbcMapping().getJdbcType(),reader,options);
+						parseLevel.getCurrent().addValue( currentLevel.selectableData, objectValue );
 					}
-
-					break;
-				default:
-					assert false: "Unexpected type " + type;
+				}
+				case NULL_VALUE -> {
+					currentLevel.addValue( currentSelectableData, null );
+					currentSelectableData = null;
+				}
+				case NUMERIC_VALUE -> {
+					final JdbcMapping jdbcMapping = currentLevel.determineJdbcMapping( currentSelectableData );
+					currentLevel.addValue(
+							currentSelectableData,
+							adapter.fromNumericValue(
+									jdbcMapping.getJdbcJavaType(),
+									jdbcMapping.getJdbcType(),
+									reader,
+									options
+							)
+					);
+					currentSelectableData = null;
+				}
+				case BOOLEAN_VALUE -> {
+					currentLevel.addValue(
+							currentSelectableData,
+							reader.getBooleanValue() ? Boolean.TRUE : Boolean.FALSE
+					);
+					currentSelectableData = null;
+				}
+				case VALUE -> {
+					final JdbcMapping jdbcMapping = currentLevel.determineJdbcMapping( currentSelectableData );
+					currentLevel.addValue(
+							currentSelectableData,
+							adapter.fromValue(
+									jdbcMapping.getJdbcJavaType(),
+									jdbcMapping.getJdbcType(),
+									reader,
+									options
+							)
+					);
+					currentSelectableData = null;
+				}
 			}
 		}
-		return (X) objectArrayResult;
+		throw new IllegalArgumentException( "Expected JSON object end, but none found." );
 	}
 
 	/**
@@ -401,20 +474,16 @@ public static <X> X deserialize(
 			JsonDocumentReader reader,
 			boolean returnEmbeddable,
 			WrapperOptions options) throws SQLException {
-
-
-		final Object[] values = consumeJsonDocumentItems(reader, embeddableMappingType, returnEmbeddable, options);
-		if ( returnEmbeddable ) {
-			final StructAttributeValues attributeValues = StructHelper.getAttributeValues(
-					embeddableMappingType,
-					values,
-					options
-			);
-			//noinspection unchecked
-			return (X) instantiate( embeddableMappingType, attributeValues );
+		final JsonDocumentItemType event;
+		if ( !reader.hasNext() || ( event = reader.next() ) == JsonDocumentItemType.NULL_VALUE ) {
+			return null;
+		}
+		if ( event != JsonDocumentItemType.OBJECT_START ) {
+			throw new IllegalArgumentException("Malformed JSON. Expected object but got: " + event);
 		}
-		//noinspection unchecked
-		return (X) values;
+		final X result = consumeJsonDocumentItems( reader, embeddableMappingType, returnEmbeddable, options );
+		assert !reader.hasNext();
+		return result;
 	}
 
 
@@ -435,7 +504,13 @@ public static <X> X deserializeArray(
 			JdbcType elementJdbcType,
 			JsonDocumentReader reader,
 			WrapperOptions options) throws SQLException {
-
+		final JsonDocumentItemType event;
+		if ( !reader.hasNext() || ( event = reader.next() ) == JsonDocumentItemType.NULL_VALUE ) {
+			return null;
+		}
+		if ( event != JsonDocumentItemType.ARRAY_START ) {
+			throw new IllegalArgumentException("Malformed JSON. Expected array but got: " + event);
+		}
 
 		final CustomArrayList arrayList = new CustomArrayList();
 		final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) javaType).getElementJavaType();
@@ -448,17 +523,13 @@ public static <X> X deserializeArray(
 			jdbcJavaType = options.getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor( preferredJavaTypeClass );
 		}
 
-		JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,false);
-
-		assert reader.hasNext():"Invalid array string";
-		assert reader.next() == JsonDocumentItemType.ARRAY_START:"Invalid start of array";
-		boolean endArrayFound = false;
+		final JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,false);
 		while(reader.hasNext()) {
 			JsonDocumentItemType type = reader.next();
 			switch ( type ) {
 				case ARRAY_END:
-					endArrayFound=true;
-					break;
+					assert !reader.hasNext();
+					return javaType.wrap( arrayList, options );
 				case NULL_VALUE:
 					arrayList.add( null );
 					break;
@@ -473,197 +544,18 @@ public static <X> X deserializeArray(
 					break;
 				case OBJECT_START:
 					assert elementJdbcType instanceof JsonJdbcType;
-					Object o = deserialize(
-							((JsonJdbcType) elementJdbcType).getEmbeddableMappingType(),
-							reader,
-							true,
-							options);
-					arrayList.add(o);
+					final EmbeddableMappingType embeddableMappingType = ((JsonJdbcType) elementJdbcType).getEmbeddableMappingType();
+					arrayList.add( consumeJsonDocumentItems(reader, embeddableMappingType, true, options) );
 					break;
 				default:
 					throw new UnsupportedOperationException( "Unexpected JSON type " + type );
 			}
 		}
 
-
-		assert endArrayFound:"Invalid end of array";
-		return javaType.wrap( arrayList, options );
+		throw new IllegalArgumentException( "Expected JSON array end, but none found." );
 	}
 
 
-
-	public static class JsonAppender extends OutputStream implements SqlAppender {
-
-		private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
-		private final StringBuilder sb;
-		private boolean escape;
-
-		public JsonAppender(StringBuilder sb) {
-			this.sb = sb;
-		}
-
-		@Override
-		public void appendSql(String fragment) {
-			append( fragment );
-		}
-
-		@Override
-		public void appendSql(char fragment) {
-			append( fragment );
-		}
-
-		@Override
-		public void appendSql(int value) {
-			sb.append( value );
-		}
-
-		@Override
-		public void appendSql(long value) {
-			sb.append( value );
-		}
-
-		@Override
-		public void appendSql(boolean value) {
-			sb.append( value );
-		}
-
-		@Override
-		public String toString() {
-			return sb.toString();
-		}
-
-		public void startEscaping() {
-			assert !escape;
-			escape = true;
-		}
-
-		public void endEscaping() {
-			assert escape;
-			escape = false;
-		}
-
-		@Override
-		public JsonAppender append(char fragment) {
-			if ( escape ) {
-				appendEscaped( fragment );
-			}
-			else {
-				sb.append( fragment );
-			}
-			return this;
-		}
-
-		@Override
-		public JsonAppender append(CharSequence csq) {
-			return append( csq, 0, csq.length() );
-		}
-
-		@Override
-		public JsonAppender append(CharSequence csq, int start, int end) {
-			if ( escape ) {
-				int len = end - start;
-				sb.ensureCapacity( sb.length() + len );
-				for ( int i = start; i < end; i++ ) {
-					appendEscaped( csq.charAt( i ) );
-				}
-			}
-			else {
-				sb.append( csq, start, end );
-			}
-			return this;
-		}
-
-		@Override
-		public void write(int v) {
-			final String hex = Integer.toHexString( v );
-			sb.ensureCapacity( sb.length() + hex.length() + 1 );
-			if ( ( hex.length() & 1 ) == 1 ) {
-				sb.append( '0' );
-			}
-			sb.append( hex );
-		}
-
-		@Override
-		public void write(byte[] bytes) {
-			write(bytes, 0, bytes.length);
-		}
-
-		@Override
-		public void write(byte[] bytes, int off, int len) {
-			sb.ensureCapacity( sb.length() + ( len << 1 ) );
-			for ( int i = 0; i < len; i++ ) {
-				final int v = bytes[off + i] & 0xFF;
-				sb.append( HEX_ARRAY[v >>> 4] );
-				sb.append( HEX_ARRAY[v & 0x0F] );
-			}
-		}
-
-		private void appendEscaped(char fragment) {
-			switch ( fragment ) {
-				case 0:
-				case 1:
-				case 2:
-				case 3:
-				case 4:
-				case 5:
-				case 6:
-				case 7:
-				//   8 is '\b'
-				//   9 is '\t'
-				//   10 is '\n'
-				case 11:
-				//   12 is '\f'
-				//   13 is '\r'
-				case 14:
-				case 15:
-				case 16:
-				case 17:
-				case 18:
-				case 19:
-				case 20:
-				case 21:
-				case 22:
-				case 23:
-				case 24:
-				case 25:
-				case 26:
-				case 27:
-				case 28:
-				case 29:
-				case 30:
-				case 31:
-					sb.append( "\\u" ).append( Integer.toHexString( fragment ) );
-					break;
-				case '\b':
-					sb.append("\\b");
-					break;
-				case '\t':
-					sb.append("\\t");
-					break;
-				case '\n':
-					sb.append("\\n");
-					break;
-				case '\f':
-					sb.append("\\f");
-					break;
-				case '\r':
-					sb.append("\\r");
-					break;
-				case '"':
-					sb.append( "\\\"" );
-					break;
-				case '\\':
-					sb.append( "\\\\" );
-					break;
-				default:
-					sb.append( fragment );
-					break;
-			}
-		}
-
-	}
-
 	private static class CustomArrayList extends AbstractCollection<Object> implements Collection<Object> {
 		Object[] array = ArrayHelper.EMPTY_OBJECT_ARRAY;
 		int size;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
index c927c262f38a..59b5b70d5935 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonJdbcType.java
@@ -77,7 +77,7 @@ protected <X> X fromString(String string, JavaType<X> javaType, WrapperOptions o
 			return null;
 		}
 		if ( embeddableMappingType != null ) {
-			return (X) JsonHelper.deserialize(
+			return JsonHelper.deserialize(
 					embeddableMappingType,
 					new StringJsonDocumentReader(string),
 					javaType.getJavaTypeClass() != Object[].class,
@@ -90,11 +90,10 @@ protected <X> X fromString(String string, JavaType<X> javaType, WrapperOptions o
 	@Override
 	public Object createJdbcValue(Object domainValue, WrapperOptions options) throws SQLException {
 		assert embeddableMappingType != null;
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		final StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		try {
-			JsonHelper.serialize( embeddableMappingType ,domainValue,options, writer );
-			return writer.toString();
+			JsonHelper.serialize( embeddableMappingType, domainValue, options, writer );
+			return writer.getJson();
 		}
 		catch (IOException e) {
 			throw new SQLException( e );
@@ -104,16 +103,15 @@ public Object createJdbcValue(Object domainValue, WrapperOptions options) throws
 	@Override
 	public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) throws SQLException {
 		assert embeddableMappingType != null;
-		return JsonHelper.deserialize( embeddableMappingType, new StringJsonDocumentReader( (String)rawJdbcValue ), false, options );
+		return JsonHelper.deserialize( embeddableMappingType, new StringJsonDocumentReader( (String) rawJdbcValue ), false, options );
 	}
 
 	protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) {
 		if ( embeddableMappingType != null ) {
 			try {
-				StringBuilder sb = new StringBuilder();
-				StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
-				JsonHelper.serialize( embeddableMappingType, value, options, writer);
-				return writer.toString();
+				final StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
+				JsonHelper.serialize( embeddableMappingType, value, options, writer );
+				return writer.getJson();
 			}
 			catch (IOException e) {
 				throw new RuntimeException("Failed to serialize JSON mapping", e );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
index c4e0aae2c8b1..48e3dd3bb090 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -4,7 +4,7 @@
  */
 package org.hibernate.type.format;
 
-import org.hibernate.dialect.JsonHelper;
+import org.hibernate.sql.ast.spi.SqlAppender;
 import org.hibernate.type.SqlTypes;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.BooleanJavaType;
@@ -14,30 +14,34 @@
 import org.hibernate.type.descriptor.java.JdbcTimestampJavaType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
 
+import java.io.OutputStream;
 import java.time.OffsetDateTime;
 import java.time.format.DateTimeFormatter;
 
 
 /**
  * Implementation of <code>JsonDocumentWriter</code> for String-based OSON document.
- * This implementation will receive a {@link JsonHelper.JsonAppender } to a serialze JSON object to it
+ * This implementation will receive a {@link JsonAppender } to a serialze JSON object to it
  * @author Emmanuel Jannetti
  */
 public class StringJsonDocumentWriter extends StringJsonDocument implements JsonDocumentWriter {
 
+	private final JsonAppender appender;
 
-
-	private JsonHelper.JsonAppender appender;
-
-
+	/**
+	 * Creates a new StringJsonDocumentWriter.
+	 */
+	public StringJsonDocumentWriter() {
+		this(new StringBuilder());
+	}
 
 	/**
 	 * Creates a new StringJsonDocumentWriter.
-	 * @param appender the appender to receive the serialze JSON object
+	 * @param sb the StringBuilder to receive the serialized JSON
 	 */
-	public StringJsonDocumentWriter(JsonHelper.JsonAppender appender) {
+	public StringJsonDocumentWriter(StringBuilder sb) {
 		this.processingStates.push( JsonProcessingState.NONE );
-		this.appender = appender;
+		this.appender = new JsonAppender( sb );
 	}
 
 	/**
@@ -237,7 +241,7 @@ public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> java
 	private  void convertedBasicValueToString(
 			Object value,
 			WrapperOptions options,
-			JsonHelper.JsonAppender appender,
+			JsonAppender appender,
 			JavaType<Object> javaType,
 			JdbcType jdbcType) {
 		switch ( jdbcType.getDefaultSqlTypeCode() ) {
@@ -354,8 +358,184 @@ private  void convertedBasicValueToString(
 		}
 	}
 
+	public String getJson() {
+		return appender.toString();
+	}
+
 	@Override
 	public String toString() {
 		return appender.toString();
 	}
+
+	private static class JsonAppender extends OutputStream implements SqlAppender {
+
+		private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+		private final StringBuilder sb;
+		private boolean escape;
+
+		public JsonAppender(StringBuilder sb) {
+			this.sb = sb;
+		}
+
+		@Override
+		public void appendSql(String fragment) {
+			append( fragment );
+		}
+
+		@Override
+		public void appendSql(char fragment) {
+			append( fragment );
+		}
+
+		@Override
+		public void appendSql(int value) {
+			sb.append( value );
+		}
+
+		@Override
+		public void appendSql(long value) {
+			sb.append( value );
+		}
+
+		@Override
+		public void appendSql(boolean value) {
+			sb.append( value );
+		}
+
+		@Override
+		public String toString() {
+			return sb.toString();
+		}
+
+		public void startEscaping() {
+			assert !escape;
+			escape = true;
+		}
+
+		public void endEscaping() {
+			assert escape;
+			escape = false;
+		}
+
+		@Override
+		public JsonAppender append(char fragment) {
+			if ( escape ) {
+				appendEscaped( fragment );
+			}
+			else {
+				sb.append( fragment );
+			}
+			return this;
+		}
+
+		@Override
+		public JsonAppender append(CharSequence csq) {
+			return append( csq, 0, csq.length() );
+		}
+
+		@Override
+		public JsonAppender append(CharSequence csq, int start, int end) {
+			if ( escape ) {
+				int len = end - start;
+				sb.ensureCapacity( sb.length() + len );
+				for ( int i = start; i < end; i++ ) {
+					appendEscaped( csq.charAt( i ) );
+				}
+			}
+			else {
+				sb.append( csq, start, end );
+			}
+			return this;
+		}
+
+		@Override
+		public void write(int v) {
+			final String hex = Integer.toHexString( v );
+			sb.ensureCapacity( sb.length() + hex.length() + 1 );
+			if ( ( hex.length() & 1 ) == 1 ) {
+				sb.append( '0' );
+			}
+			sb.append( hex );
+		}
+
+		@Override
+		public void write(byte[] bytes) {
+			write(bytes, 0, bytes.length);
+		}
+
+		@Override
+		public void write(byte[] bytes, int off, int len) {
+			sb.ensureCapacity( sb.length() + ( len << 1 ) );
+			for ( int i = 0; i < len; i++ ) {
+				final int v = bytes[off + i] & 0xFF;
+				sb.append( HEX_ARRAY[v >>> 4] );
+				sb.append( HEX_ARRAY[v & 0x0F] );
+			}
+		}
+
+		private void appendEscaped(char fragment) {
+			switch ( fragment ) {
+				case 0:
+				case 1:
+				case 2:
+				case 3:
+				case 4:
+				case 5:
+				case 6:
+				case 7:
+				//   8 is '\b'
+				//   9 is '\t'
+				//   10 is '\n'
+				case 11:
+				//   12 is '\f'
+				//   13 is '\r'
+				case 14:
+				case 15:
+				case 16:
+				case 17:
+				case 18:
+				case 19:
+				case 20:
+				case 21:
+				case 22:
+				case 23:
+				case 24:
+				case 25:
+				case 26:
+				case 27:
+				case 28:
+				case 29:
+				case 30:
+				case 31:
+					sb.append( "\\u" ).append( Integer.toHexString( fragment ) );
+					break;
+				case '\b':
+					sb.append("\\b");
+					break;
+				case '\t':
+					sb.append("\\t");
+					break;
+				case '\n':
+					sb.append("\\n");
+					break;
+				case '\f':
+					sb.append("\\f");
+					break;
+				case '\r':
+					sb.append("\\r");
+					break;
+				case '"':
+					sb.append( "\\\"" );
+					break;
+				case '\\':
+					sb.append( "\\\\" );
+					break;
+				default:
+					sb.append( fragment );
+					break;
+			}
+		}
+
+	}
 }
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
index 3c5b2ad1d786..81c0f73d0ec4 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
@@ -327,5 +327,5 @@ public void setAggregateArray(EmbeddableAggregate[] aggregateArray) {
 		}
 
 	}
-	
+
 }
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
index d890c9215b15..0788f86d4045 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/util/StringJsonDocumentWriterTest.java
@@ -4,7 +4,6 @@
  */
 package org.hibernate.orm.test.util;
 
-import org.hibernate.dialect.JsonHelper;
 import org.hibernate.type.format.StringJsonDocumentWriter;
 import org.junit.jupiter.api.Test;
 
@@ -22,8 +21,7 @@ private static void assertEqualsIgnoreSpace(String expected, String actual) {
 
 	@Test
 	public void testEmptyDocument() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startObject();
 		writer.endObject();
 		assertEquals( "{}" , writer.toString());
@@ -31,16 +29,14 @@ public void testEmptyDocument() {
 
 	@Test
 	public void testEmptyArray() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startArray();
 		writer.endArray();
 		assertEquals( "[]" , writer.toString() );
 	}
 	@Test
 	public void testArray() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startArray();
 		writer.booleanValue( false );
 		writer.booleanValue( true );
@@ -51,8 +47,7 @@ public void testArray() {
 
 	@Test
 	public void testMixedArrayDocument() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startArray();
 		writer.nullValue();
 		writer.booleanValue( false );
@@ -64,8 +59,7 @@ public void testMixedArrayDocument() {
 	}
 	@Test
 	public void testSimpleDocument() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startObject();
 		writer.objectKey( "key1" );
 		writer.stringValue( "value1" );
@@ -82,8 +76,7 @@ public void testSimpleDocument() {
 
 	@Test
 	public void testNonStringValueDocument() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter(new JsonHelper.JsonAppender(sb) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startObject();
 		writer.objectKey( "aNull" );
 		writer.nullValue();
@@ -103,8 +96,7 @@ public void testNonStringValueDocument() {
 
 	@Test
 	public void testArrayValueDocument() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startObject();
 		writer.objectKey( "anEmptyArray" );
 		writer.startArray();
@@ -126,8 +118,7 @@ public void testArrayValueDocument() {
 	}
 	@Test
 	public void testObjectArrayMultipleValueDocument() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startObject();
 		writer.objectKey( "anArray" ).startArray().nullValue().stringValue( "2" ).startObject()
 		.objectKey( "foo" ).stringValue( "bar" ).endObject().endArray().endObject();
@@ -136,14 +127,13 @@ public void testObjectArrayMultipleValueDocument() {
 					{
 						"anArray" : [null, "2" , {\"foo\":\"bar\"}  ]
 					}
-					""" , sb.toString() );
+					""" , writer.toString() );
 
 	}
 
 	@Test
 	public void testNestedDocument() {
-		StringBuilder sb = new StringBuilder();
-		StringJsonDocumentWriter writer = new StringJsonDocumentWriter( new JsonHelper.JsonAppender( sb ) );
+		StringJsonDocumentWriter writer = new StringJsonDocumentWriter();
 		writer.startObject().objectKey( "nested" ).startObject()
 				.objectKey( "converted_gender" ).stringValue( "M" )
 				.endObject()

From c70daedccea4ff4c4e269ae81695a40e274072ea Mon Sep 17 00:00:00 2001
From: Christian Beikov <christian.beikov@gmail.com>
Date: Thu, 17 Apr 2025 16:18:20 +0200
Subject: [PATCH 65/81] HHH-17404 Cleanup JdbcTypes and Jackson/OSON
 availability detection

---
 .../SessionFactoryOptionsBuilder.java         |  11 +-
 .../internal/StrategySelectorBuilder.java     |  14 +--
 .../org/hibernate/dialect/OracleDialect.java  |  30 ++---
 ...Type.java => OracleOsonArrayJdbcType.java} | 112 +++++++++++++-----
 .../OracleOsonArrayJdbcTypeConstructor.java   |   4 +-
 .../dialect/OracleOsonJacksonHelper.java      |  59 +++++++++
 ...nJdbcType.java => OracleOsonJdbcType.java} | 111 +++++++++++------
 .../dialect/type/OracleJdbcHelper.java        |  10 ++
 .../type/format/OsonDocumentWriter.java       |  13 +-
 .../type/format/OsonValueJDBCTypeAdapter.java |   1 -
 .../format/jackson/JacksonIntegration.java    |  17 ++-
 .../jackson/JacksonJsonFormatMapper.java      |  33 +++++-
 .../jackson/JacksonOsonFormatMapper.java      |  42 +++++--
 .../embeddable/JsonEmbeddableArrayTest.java   |   5 +
 .../src/main/groovy/local.java-module.gradle  |   3 +
 15 files changed, 331 insertions(+), 134 deletions(-)
 rename hibernate-core/src/main/java/org/hibernate/dialect/{OracleOsonJacksonArrayJdbcType.java => OracleOsonArrayJdbcType.java} (68%)
 create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonHelper.java
 rename hibernate-core/src/main/java/org/hibernate/dialect/{OracleOsonJacksonJdbcType.java => OracleOsonJdbcType.java} (69%)

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
index 2684a90f168d..d0b19c8f6e5f 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
@@ -794,13 +794,10 @@ private static FormatMapper determineJsonFormatMapper(Object setting, StrategySe
 				FormatMapper.class,
 				setting,
 				(Callable<FormatMapper>) () -> {
-					FormatMapper jsonJacksonFormatMapper = null;
-					if (JacksonIntegration.isOracleOsonExtensionAvailable()) {
-						jsonJacksonFormatMapper = getOsonJacksonFormatMapperOrNull();
-					}
-					else {
-						jsonJacksonFormatMapper = getJsonJacksonFormatMapperOrNull();
-					}
+					// Prefer the OSON Jackson FormatMapper by default if available
+					final FormatMapper jsonJacksonFormatMapper = JacksonIntegration.isJacksonOsonExtensionAvailable()
+							? getOsonJacksonFormatMapperOrNull()
+							: getJsonJacksonFormatMapperOrNull();
 					return jsonJacksonFormatMapper != null ? jsonJacksonFormatMapper : getJakartaJsonBFormatMapperOrNull();
 				}
 		);
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
index bf64c2676f7b..af0eda071fda 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
@@ -308,20 +308,18 @@ private static void addJsonFormatMappers(StrategySelectorImpl strategySelector)
 				JsonBJsonFormatMapper.SHORT_NAME,
 				JsonBJsonFormatMapper.class
 		);
-		if ( JacksonIntegration.isOracleOsonExtensionAvailable() ) {
+		strategySelector.registerStrategyImplementor(
+				FormatMapper.class,
+				JacksonJsonFormatMapper.SHORT_NAME,
+				JacksonJsonFormatMapper.class
+		);
+		if ( JacksonIntegration.isJacksonOsonExtensionAvailable() ) {
 			strategySelector.registerStrategyImplementor(
 					FormatMapper.class,
 					JacksonOsonFormatMapper.SHORT_NAME,
 					JacksonOsonFormatMapper.class
 			);
 		}
-		else {
-			strategySelector.registerStrategyImplementor(
-					FormatMapper.class,
-					JacksonJsonFormatMapper.SHORT_NAME,
-					JacksonJsonFormatMapper.class
-			);
-		}
 	}
 
 	private static void addXmlFormatMappers(StrategySelectorImpl strategySelector) {
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 4104d39cc0b3..15acdafc090a 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -10,7 +10,6 @@
 import org.hibernate.QueryTimeoutException;
 import org.hibernate.boot.model.FunctionContributions;
 import org.hibernate.boot.model.TypeContributions;
-import org.hibernate.cfg.MappingSettings;
 import org.hibernate.dialect.aggregate.AggregateSupport;
 import org.hibernate.dialect.aggregate.OracleAggregateSupport;
 import org.hibernate.dialect.function.CommonFunctionFactory;
@@ -103,8 +102,6 @@
 import org.hibernate.type.descriptor.sql.internal.NamedNativeEnumDdlTypeImpl;
 import org.hibernate.type.descriptor.sql.internal.NamedNativeOrdinalEnumDdlTypeImpl;
 import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
-import org.hibernate.type.format.jackson.JacksonIntegration;
-import org.hibernate.type.format.jackson.JacksonJsonFormatMapper;
 import org.hibernate.type.spi.TypeConfiguration;
 
 import java.sql.CallableStatement;
@@ -262,14 +259,6 @@ public boolean isApplicationContinuity() {
 		return applicationContinuity;
 	}
 
-	private static boolean isJacksonJsonFormatMapper(ConfigurationService configService) {
-		// Mirror the behavior of SessionFactoryOptionsBuilder#determineJsonFormatMapper
-		final String mapperName = configService.getSetting( MappingSettings.JSON_FORMAT_MAPPER,
-				StandardConverters.STRING,JacksonJsonFormatMapper.SHORT_NAME);
-		return JacksonJsonFormatMapper.SHORT_NAME.equalsIgnoreCase( mapperName )
-			|| mapperName == null && JacksonIntegration.getJsonJacksonFormatMapperOrNull() != null;
-	}
-
 	@Override
 	protected DatabaseVersion getMinimumSupportedVersion() {
 		return MINIMUM_VERSION;
@@ -1016,22 +1005,21 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
 			);
 		}
 
-
 		if ( getVersion().isSameOrAfter( 21 ) ) {
-			final boolean osonDisabled = getBoolean( ORACLE_OSON_DISABLED , configurationService.getSettings() );
-			if ( !osonDisabled && JacksonIntegration.isOracleOsonExtensionAvailable() && isJacksonJsonFormatMapper( configurationService )) {
+			final boolean osonDisabled = getBoolean( ORACLE_OSON_DISABLED, configurationService.getSettings() );
+			if ( !osonDisabled && OracleJdbcHelper.isOsonAvailable( serviceRegistry ) ) {
 				// We must check that that extension is available and actually used.
-				typeContributions.contributeJdbcType( OracleOsonJacksonJdbcType.INSTANCE );
+				typeContributions.contributeJdbcType( OracleOsonJdbcType.INSTANCE );
 				typeContributions.contributeJdbcTypeConstructor( OracleOsonArrayJdbcTypeConstructor.INSTANCE );
 
-				DIALECT_LOGGER.log( Logger.Level.DEBUG, "Oracle OSON Jackson extension used" );
+				DIALECT_LOGGER.log( Logger.Level.DEBUG, "Oracle OSON extension enabled" );
 			}
 			else {
-				if (DIALECT_LOGGER.isDebugEnabled()) {
-					DIALECT_LOGGER.log( Logger.Level.DEBUG, "Oracle OSON Jackson extension not used" );
-					DIALECT_LOGGER.log( Logger.Level.DEBUG,
-							"JacksonIntegration.isOracleOsonExtensionAvailable(): " +
-									JacksonIntegration.isOracleOsonExtensionAvailable());
+				if ( osonDisabled ) {
+					DIALECT_LOGGER.log( Logger.Level.DEBUG, "Oracle OSON extension disabled" );
+				}
+				else {
+					DIALECT_LOGGER.log( Logger.Level.DEBUG, "Oracle OSON extension not available" );
 				}
 				typeContributions.contributeJdbcType( OracleJsonJdbcType.INSTANCE );
 				typeContributions.contributeJdbcTypeConstructor( OracleJsonArrayJdbcTypeConstructor.NATIVE_INSTANCE );
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
similarity index 68%
rename from hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
rename to hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
index 28ee7d868faf..c0d7321b20a2 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
@@ -4,7 +4,6 @@
  */
 package org.hibernate.dialect;
 
-import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
 import oracle.jdbc.driver.DatabaseError;
 import oracle.sql.json.OracleJsonDatum;
@@ -28,6 +27,7 @@
 import org.hibernate.type.format.OsonDocumentWriter;
 
 import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.sql.CallableStatement;
@@ -35,8 +35,7 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
-import static org.hibernate.dialect.OracleOsonJacksonJdbcType.OSON_JACKSON_FACTORY;
-import static org.hibernate.dialect.OracleOsonJacksonJdbcType.OSON_JSON_FACTORY;
+import static org.hibernate.dialect.OracleOsonJdbcType.OSON_JSON_FACTORY;
 
 /**
  *
@@ -47,21 +46,19 @@
  * @author Emmanuel Jannetti
  * @author Bidyadhar Mohanty
  */
-public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
+public class OracleOsonArrayJdbcType extends OracleJsonArrayJdbcType {
 
-	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonArrayJdbcType.class );
+	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonArrayJdbcType.class );
 
-	public OracleOsonJacksonArrayJdbcType(JdbcType elementJdbcType) {
+	public OracleOsonArrayJdbcType(JdbcType elementJdbcType) {
 		super(elementJdbcType);
 	}
 
-
 	@Override
 	public String toString() {
-		return "OracleOsonJacksonArrayJdbcType";
+		return "OracleOsonArrayJdbcType";
 	}
 
-
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
@@ -73,7 +70,9 @@ private <T> byte[] toOsonStream(T value, JavaType<T> javaType, WrapperOptions op
 				try (OracleJsonGenerator generator = OSON_JSON_FACTORY.createJsonBinaryGenerator( out )) {
 					final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) javaType).getElementJavaType();
 					if ( elementJavaType instanceof UnknownBasicJavaType<?> ) {
-						options.getJsonFormatMapper().writeToTarget( value, javaType, generator, options);
+						try (Closeable osonGen = OracleOsonJacksonHelper.createWriteTarget( out )) {
+							options.getJsonFormatMapper().writeToTarget( value, javaType, osonGen, options );
+						}
 					}
 					else {
 						final OsonDocumentWriter writer = new OsonDocumentWriter( generator );
@@ -92,15 +91,31 @@ private <T> byte[] toOsonStream(T value, JavaType<T> javaType, WrapperOptions op
 							);
 						}
 					}
-					return out.toByteArray();
 				}
+				return out.toByteArray();
+			}
 
+			private boolean useUtf8(WrapperOptions options) {
+				final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) getJavaType()).getElementJavaType();
+				return elementJavaType instanceof UnknownBasicJavaType<?>
+					&& !options.getJsonFormatMapper().supportsTargetType( OracleOsonJacksonHelper.WRITER_CLASS );
 			}
+
 			@Override
 			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setObject( index, toOsonStream( value, getJavaType(), options ), OracleType.JSON );
+					if ( useUtf8( options ) ) {
+						final String json = OracleOsonArrayJdbcType.this.toString(
+								value,
+								getJavaType(),
+								options
+						);
+						st.setBytes( index, json.getBytes( StandardCharsets.UTF_8 ) );
+					}
+					else {
+						st.setObject( index, toOsonStream( value, getJavaType(), options ), OracleType.JSON );
+					}
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -111,7 +126,17 @@ protected void doBind(PreparedStatement st, X value, int index, WrapperOptions o
 			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setObject( name, toOsonStream( value, getJavaType(), options ) , OracleType.JSON);
+					if ( useUtf8( options ) ) {
+						final String json = OracleOsonArrayJdbcType.this.toString(
+								value,
+								getJavaType(),
+								options
+						);
+						st.setBytes( name, json.getBytes( StandardCharsets.UTF_8 ) );
+					}
+					else {
+						st.setObject( name, toOsonStream( value, getJavaType(), options ), OracleType.JSON );
+					}
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -127,7 +152,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
 			private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
 				if ( ((BasicPluralJavaType<?>) getJavaType()).getElementJavaType() instanceof UnknownBasicJavaType<?> ) {
-					try (JsonParser oParser = OSON_JACKSON_FACTORY.createParser( osonBytes )) {
+					try (Closeable oParser = OracleOsonJacksonHelper.createReadSource( osonBytes )) {
 						return options.getJsonFormatMapper().readFromSource( getJavaType(), oParser, options );
 					}
 				}
@@ -155,21 +180,40 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 				}
 			}
 
+			private boolean useUtf8(WrapperOptions options) {
+				final JavaType<?> elementJavaType = ((BasicPluralJavaType<?>) getJavaType()).getElementJavaType();
+				return elementJavaType instanceof UnknownBasicJavaType<?>
+					&& !options.getJsonFormatMapper().supportsTargetType( OracleOsonJacksonHelper.READER_CLASS );
+			}
+
+			private X fromString(byte[] json, WrapperOptions options) throws SQLException {
+				if ( json == null ) {
+					return null;
+				}
+				return OracleOsonArrayJdbcType.this.fromString(
+						new String( json, StandardCharsets.UTF_8 ),
+						getJavaType(),
+						options
+				);
+			}
+
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
 				try {
-					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-					return doExtraction( ojd, options);
+					if ( useUtf8( options ) ) {
+						return fromString( rs.getBytes( paramIndex ), options );
+					}
+					else {
+						OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+						return doExtraction( ojd, options );
+					}
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return OracleOsonJacksonArrayJdbcType.this.fromString(
-								new String( rs.getBytes( paramIndex ), StandardCharsets.UTF_8 ),
-								getJavaType(),
-								options);
+						return fromString( rs.getBytes( paramIndex ), options );
 					} else {
 						throw exc;
 					}
@@ -179,18 +223,20 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
 				try {
-					OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
-					return doExtraction( ojd, options);
+					if ( useUtf8( options ) ) {
+						return fromString( statement.getBytes( index ), options );
+					}
+					else {
+						OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
+						return doExtraction( ojd, options );
+					}
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return OracleOsonJacksonArrayJdbcType.this.fromString(
-								new String( statement.getBytes( index ), StandardCharsets.UTF_8 ),
-								getJavaType(),
-								options);
+						return fromString( statement.getBytes( index ), options );
 					} else {
 						throw exc;
 					}
@@ -201,18 +247,20 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
 				try {
-					OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
-					return doExtraction( ojd, options);
+					if ( useUtf8( options ) ) {
+						return fromString( statement.getBytes( name ), options );
+					}
+					else {
+						OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
+						return doExtraction( ojd, options );
+					}
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return OracleOsonJacksonArrayJdbcType.this.fromString(
-								new String( statement.getBytes( name ), StandardCharsets.UTF_8 ),
-								getJavaType(),
-								options);
+						return fromString( statement.getBytes( name ), options );
 					} else {
 						throw exc;
 					}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
index 806d9f98d8a8..0aa867c820bc 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcTypeConstructor.java
@@ -12,7 +12,7 @@
 import org.hibernate.type.spi.TypeConfiguration;
 
 /**
- * Factory for {@link OracleOsonJacksonArrayJdbcType}.
+ * Factory for {@link OracleOsonArrayJdbcType}.
  * @author Emmanuel Jannetti
  */
 public class OracleOsonArrayJdbcTypeConstructor implements JdbcTypeConstructor {
@@ -33,7 +33,7 @@ public JdbcType resolveType(
 			Dialect dialect,
 			JdbcType elementType,
 			ColumnTypeInformation columnTypeInformation) {
-		return new OracleOsonJacksonArrayJdbcType( elementType );
+		return new OracleOsonArrayJdbcType( elementType );
 	}
 
 	@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonHelper.java
new file mode 100644
index 000000000000..72c03f7557eb
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonHelper.java
@@ -0,0 +1,59 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.dialect;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import oracle.jdbc.provider.oson.OsonFactory;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.hibernate.type.format.jackson.JacksonIntegration;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static org.hibernate.type.format.jackson.JacksonIntegration.isJacksonOsonExtensionAvailable;
+
+public class OracleOsonJacksonHelper {
+
+	public static final @Nullable Class<? extends Closeable> READER_CLASS = loadOrNull( "com.fasterxml.jackson.core.JsonParser" );
+	public static final @Nullable Class<? extends Closeable> WRITER_CLASS = loadOrNull( "com.fasterxml.jackson.core.JsonGenerator" );
+
+	private static @Nullable Class<? extends Closeable> loadOrNull(String name) {
+		try {
+			//N.B. intentionally not using the context classloader
+			// as we're storing these in static references;
+			// IMO it's reasonable to expect that such dependencies are made reachable from the ORM classloader.
+			// (we can change this if it's more problematic than expected).
+			//noinspection unchecked
+			return (Class<? extends Closeable>) JacksonIntegration.class.getClassLoader().loadClass( name );
+		}
+		catch (ClassNotFoundException | LinkageError e) {
+			return null;
+		}
+	}
+
+	private OracleOsonJacksonHelper() {
+	}
+
+	public static Closeable createWriteTarget(OutputStream out) throws IOException {
+		return FactoryHolder.JACKSON_FACTORY.createGenerator( out );
+	}
+
+
+	public static Closeable createReadSource(InputStream osonBytes) throws IOException {
+		return FactoryHolder.JACKSON_FACTORY.createParser( osonBytes );
+	}
+
+	private static final class FactoryHolder {
+		// Intentionally storing the jackson typed factory in a different class,
+		// to avoid linkage errors for the outer class if Jackson is not available
+		private static final JsonFactory JACKSON_FACTORY = isJacksonOsonExtensionAvailable()
+				? new OsonFactory()
+				: new JsonFactory();
+
+	}
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
similarity index 69%
rename from hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
rename to hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
index d2a1894a30c8..6a4ffc8840d7 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
@@ -4,11 +4,8 @@
  */
 package org.hibernate.dialect;
 
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
 import oracle.jdbc.OracleType;
 import oracle.jdbc.driver.DatabaseError;
-import oracle.jdbc.provider.oson.OsonFactory;
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
@@ -27,6 +24,7 @@
 import org.hibernate.type.format.OsonDocumentWriter;
 
 import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.sql.CallableStatement;
@@ -37,26 +35,24 @@
 /**
  *
  * Type mapping JSON SQL data type for Oracle database.
- * This implementation is used when Jackson mapper is used and that the JDBC OSON extension
- * is available.
+ * This implementation is used when the JDBC OSON extension is available.
  *
  * @author Emmanuel Jannetti
  */
-public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
-	public static final OracleOsonJacksonJdbcType INSTANCE = new OracleOsonJacksonJdbcType( null );
+public class OracleOsonJdbcType extends OracleJsonJdbcType {
+	public static final OracleOsonJdbcType INSTANCE = new OracleOsonJdbcType( null );
 
-	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonJdbcType.class );
+	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJdbcType.class );
 
-	static final OsonFactory OSON_JACKSON_FACTORY = new OsonFactory();
 	static final OracleJsonFactory OSON_JSON_FACTORY = new OracleJsonFactory();
 
-	private OracleOsonJacksonJdbcType(EmbeddableMappingType embeddableMappingType) {
+	private OracleOsonJdbcType(EmbeddableMappingType embeddableMappingType) {
 		super( embeddableMappingType );
 	}
 
 	@Override
 	public String toString() {
-		return "OracleOsonJacksonJdbcType";
+		return "OracleOsonJdbcType";
 	}
 
 	@Override
@@ -64,7 +60,7 @@ public AggregateJdbcType resolveAggregateJdbcType(
 			EmbeddableMappingType mappingType,
 			String sqlType,
 			RuntimeModelCreationContext creationContext) {
-		return new OracleOsonJacksonJdbcType( mappingType );
+		return new OracleOsonJdbcType( mappingType );
 	}
 
 	@Override
@@ -90,18 +86,33 @@ private <T> byte[] toOson(T value, JavaType<T> javaType, WrapperOptions options)
 					}
 				}
 				else {
-					try (JsonGenerator osonGen = OSON_JACKSON_FACTORY.createGenerator( out )) {
+					try (Closeable osonGen = OracleOsonJacksonHelper.createWriteTarget( out )) {
 						options.getJsonFormatMapper().writeToTarget( value, javaType, osonGen, options );
 					}
 				}
 				return out.toByteArray();
 			}
 
+			private boolean useUtf8(WrapperOptions options) {
+				return getEmbeddableMappingType() == null
+					&& !options.getJsonFormatMapper().supportsTargetType( OracleOsonJacksonHelper.WRITER_CLASS );
+			}
+
 			@Override
 			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setObject( index, toOson( value, getJavaType(), options ), OracleType.JSON);
+					if ( useUtf8( options ) ) {
+						final String json = OracleOsonJdbcType.this.toString(
+								value,
+								getJavaType(),
+								options
+						);
+						st.setBytes( index, json.getBytes( StandardCharsets.UTF_8 ) );
+					}
+					else {
+						st.setObject( index, toOson( value, getJavaType(), options ), OracleType.JSON );
+					}
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -112,7 +123,17 @@ protected void doBind(PreparedStatement st, X value, int index, WrapperOptions o
 			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
 					throws SQLException {
 				try {
-					st.setObject( name, toOson( value, getJavaType(), options ), OracleType.JSON);
+					if ( useUtf8( options ) ) {
+						final String json = OracleOsonJdbcType.this.toString(
+								value,
+								getJavaType(),
+								options
+						);
+						st.setBytes( name, json.getBytes( StandardCharsets.UTF_8 ) );
+					}
+					else {
+						st.setObject( name, toOson( value, getJavaType(), options ), OracleType.JSON );
+					}
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -140,12 +161,17 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
 					);
 				}
 				else {
-					try (JsonParser osonParser = OSON_JACKSON_FACTORY.createParser( osonBytes )) {
+					try (Closeable osonParser = OracleOsonJacksonHelper.createReadSource( osonBytes )) {
 						return options.getJsonFormatMapper().readFromSource( getJavaType(), osonParser, options );
 					}
 				}
 			}
 
+			private boolean useUtf8(WrapperOptions options) {
+				return getEmbeddableMappingType() == null
+					&& !options.getJsonFormatMapper().supportsTargetType( OracleOsonJacksonHelper.READER_CLASS );
+			}
+
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
 				if ( datum == null ) {
 					return null;
@@ -159,21 +185,34 @@ private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQ
 				}
 			}
 
+			private X fromString(byte[] json, WrapperOptions options) throws SQLException {
+				if ( json == null ) {
+					return null;
+				}
+				return OracleOsonJdbcType.this.fromString(
+						new String( json, StandardCharsets.UTF_8 ),
+						getJavaType(),
+						options
+				);
+			}
+
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
 				try {
-					OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
-					return doExtraction(ojd,options);
+					if ( useUtf8( options ) ) {
+						return fromString( rs.getBytes( paramIndex ), options );
+					}
+					else {
+						OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
+						return doExtraction( ojd, options );
+					}
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return OracleOsonJacksonJdbcType.this.fromString(
-								new String( rs.getBytes( paramIndex ), StandardCharsets.UTF_8 ),
-								getJavaType(),
-								options);
+						return fromString( rs.getBytes( paramIndex ), options );
 					} else {
 						throw exc;
 					}
@@ -183,18 +222,20 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
 				try {
-					OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
-					return doExtraction(ojd,options);
+					if ( useUtf8( options ) ) {
+						return fromString( statement.getBytes( index ), options );
+					}
+					else {
+						OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
+						return doExtraction( ojd, options );
+					}
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return OracleOsonJacksonJdbcType.this.fromString(
-								new String( statement.getBytes( index ), StandardCharsets.UTF_8 ),
-								getJavaType(),
-								options);
+						return fromString( statement.getBytes( index ), options);
 					} else {
 						throw exc;
 					}
@@ -205,18 +246,20 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
 				try {
-					OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
-					return doExtraction(ojd,options);
+					if ( useUtf8( options ) ) {
+						return fromString( statement.getBytes( name ), options );
+					}
+					else {
+						OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
+						return doExtraction( ojd, options );
+					}
 				} catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return OracleOsonJacksonJdbcType.this.fromString(
-								new String( statement.getBytes( name ), StandardCharsets.UTF_8 ),
-								getJavaType(),
-								options);
+						return fromString( statement.getBytes( name ), options);
 					} else {
 						throw exc;
 					}
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java
index c37ccbe2ad58..be26134db251 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java
@@ -31,6 +31,16 @@ public static boolean isUsable(ServiceRegistry serviceRegistry) {
 			return false;
 		}
 	}
+	public static boolean isOsonAvailable(ServiceRegistry serviceRegistry) {
+		final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
+		try {
+			classLoaderService.classForName( "oracle.sql.json.OracleJsonFactory" );
+			return true;
+		}
+		catch (ClassLoadingException ex) {
+			return false;
+		}
+	}
 
 	public static JdbcTypeConstructor getArrayJdbcTypeConstructor(ServiceRegistry serviceRegistry) {
 		return create( serviceRegistry, "org.hibernate.dialect.type.OracleArrayJdbcTypeConstructor" );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index fcc6cb09038f..6514537e0cc6 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -18,9 +18,7 @@
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.sql.Time;
 import java.sql.Timestamp;
-import java.time.Duration;
 import java.time.OffsetDateTime;
 
 /**
@@ -171,8 +169,7 @@ private void serializeValue(Object value,
 			case SqlTypes.MATERIALIZED_NCLOB:
 			case SqlTypes.ENUM:
 			case SqlTypes.NAMED_ENUM:
-				// correct?
-				generator.write( javaType.unwrap( value,String.class,options ) );
+				generator.write( javaType.toString( value ) );
 				break;
 			case SqlTypes.DATE:
 				DATE dd = new DATE(javaType.unwrap( value,java.sql.Date.class,options ));
@@ -182,8 +179,7 @@ private void serializeValue(Object value,
 			case SqlTypes.TIME:
 			case SqlTypes.TIME_WITH_TIMEZONE:
 			case SqlTypes.TIME_UTC:
-				Time time = javaType.unwrap( value, Time.class,options );
-				generator.write( time.toString() );
+				generator.write( javaType.toString( value ) );
 				break;
 			case SqlTypes.TIMESTAMP:
 				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( value, Timestamp.class, options ));
@@ -205,11 +201,8 @@ private void serializeValue(Object value,
 				break;
 
 			case SqlTypes.DURATION:
-				Duration duration = javaType.unwrap( value, Duration.class, options );
-				generator.write( duration );
-				break;
 			case SqlTypes.UUID:
-				generator.write( javaType.unwrap( value, String.class, options ) );
+				generator.write( javaType.toString( value ) );
 				break;
 			case SqlTypes.BINARY:
 			case SqlTypes.VARBINARY:
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
index d94af51b883d..46a4c283d0ef 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonValueJDBCTypeAdapter.java
@@ -31,7 +31,6 @@ public Object fromValue(JavaType<?> jdbcJavaType, JdbcType jdbcType, JsonDocumen
 			case SqlTypes.VARBINARY:
 			case SqlTypes.LONGVARBINARY:
 			case SqlTypes.LONG32VARBINARY:
-			case SqlTypes.UUID:
 				valueToBeWrapped = source.getValue( PrimitiveByteArrayJavaType.INSTANCE, options );
 				break;
 			case SqlTypes.DATE:
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
index d3efc479f628..4d8552d59402 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java
@@ -12,11 +12,11 @@ public final class JacksonIntegration {
 	// when GraalVM native image is initializing them.
 	private static final boolean JACKSON_XML_AVAILABLE = ableToLoadJacksonXMLMapper();
 	private static final boolean JACKSON_JSON_AVAILABLE = ableToLoadJacksonJSONMapper();
-	private static final boolean JACKSON_OSON_AVAILABLE = ableToLoadJacksonOSONGenerator();
-	private static final JacksonXmlFormatMapper XML_FORMAT_MAPPER = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper() : null;
-	private static final JacksonXmlFormatMapper XML_FORMAT_MAPPER_PORTABLE = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper( false ) : null;
-	private static final JacksonJsonFormatMapper JSON_FORMAT_MAPPER = JACKSON_JSON_AVAILABLE ? new JacksonJsonFormatMapper() : null;
-	private static final JacksonJsonFormatMapper OSON_FORMAT_MAPPER = JACKSON_OSON_AVAILABLE ? new JacksonOsonFormatMapper() : null;
+	private static final boolean JACKSON_OSON_AVAILABLE = ableToLoadJacksonOSONFactory();
+	private static final FormatMapper XML_FORMAT_MAPPER = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper() : null;
+	private static final FormatMapper XML_FORMAT_MAPPER_PORTABLE = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper( false ) : null;
+	private static final FormatMapper JSON_FORMAT_MAPPER = JACKSON_JSON_AVAILABLE ? new JacksonJsonFormatMapper() : null;
+	private static final FormatMapper OSON_FORMAT_MAPPER = JACKSON_OSON_AVAILABLE ? new JacksonOsonFormatMapper() : null;
 
 
 	private JacksonIntegration() {
@@ -36,9 +36,9 @@ private static boolean ableToLoadJacksonXMLMapper() {
 	 * in the classpath.
 	 * @return true if we can load the OSON support, false otherwise.
 	 */
-	private static boolean ableToLoadJacksonOSONGenerator() {
+	private static boolean ableToLoadJacksonOSONFactory() {
 		return ableToLoadJacksonJSONMapper() &&
-				canLoad( "oracle.jdbc.provider.oson.OsonGenerator" );
+				canLoad( "oracle.jdbc.provider.oson.OsonFactory" );
 	}
 
 	public static FormatMapper getXMLJacksonFormatMapperOrNull(boolean legacyFormat) {
@@ -57,11 +57,10 @@ public static FormatMapper getOsonJacksonFormatMapperOrNull() {
 	 *
 	 * @return true if we can load the OSON support, false otherwise.
 	 */
-	public static boolean isOracleOsonExtensionAvailable() {
+	public static boolean isJacksonOsonExtensionAvailable() {
 		return JACKSON_OSON_AVAILABLE;
 	}
 
-
 	private static boolean canLoad(String name) {
 		try {
 			//N.B. intentionally not using the context classloader
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
index b71107c0949e..cc1f0c0aac42 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java
@@ -4,31 +4,58 @@
  */
 package org.hibernate.type.format.jackson;
 
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+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.io.IOException;
 import java.lang.reflect.Type;
 
 /**
  * @author Christian Beikov
  * @author Yanming Zhou
  */
-public class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
+public final class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
 
 	public static final String SHORT_NAME = "jackson";
 
-	protected final ObjectMapper objectMapper;
+	private final ObjectMapper objectMapper;
 
 	public JacksonJsonFormatMapper() {
-		this(new ObjectMapper().findAndRegisterModules());
+		this( new ObjectMapper().findAndRegisterModules() );
 	}
 
 	public JacksonJsonFormatMapper(ObjectMapper objectMapper) {
 		this.objectMapper = objectMapper;
 	}
 
+	@Override
+	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
+			throws IOException {
+		objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) )
+				.writeValue( (JsonGenerator) target, value );
+	}
+
+	@Override
+	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
+		return objectMapper.readValue( (JsonParser) source, objectMapper.constructType( javaType.getJavaType() ) );
+	}
+
+	@Override
+	public boolean supportsSourceType(Class<?> sourceType) {
+		return JsonParser.class.isAssignableFrom( sourceType );
+	}
+
+	@Override
+	public boolean supportsTargetType(Class<?> targetType) {
+		return JsonGenerator.class.isAssignableFrom( targetType );
+	}
+
 	@Override
 	public <T> T fromString(CharSequence charSequence, Type type) {
 		try {
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
index 86498ae67554..e2c36ee25c84 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonOsonFormatMapper.java
@@ -6,12 +6,16 @@
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import oracle.jdbc.provider.oson.OsonModule;
 import org.hibernate.type.descriptor.WrapperOptions;
 import org.hibernate.type.descriptor.java.JavaType;
+import org.hibernate.type.format.AbstractJsonFormatMapper;
 
 import java.io.IOException;
+import java.lang.reflect.Type;
 
 
 /**
@@ -20,30 +24,35 @@
  * @author Emmanuel Jannetti
  * @author Bidyadhar Mohanty
  */
-public class JacksonOsonFormatMapper extends JacksonJsonFormatMapper {
+public final class JacksonOsonFormatMapper extends AbstractJsonFormatMapper {
 
-	public static final String SHORT_NAME = "jackson";
+	public static final String SHORT_NAME = "jackson-oson";
 
+	private final ObjectMapper objectMapper;
 
 	/**
 	 * Creates a new JacksonOsonFormatMapper
 	 */
 	public JacksonOsonFormatMapper() {
-		super();
+		this( new ObjectMapper().findAndRegisterModules() );
+	}
+
+	public JacksonOsonFormatMapper(ObjectMapper objectMapper) {
 		objectMapper.registerModule( new OsonModule() );
-		objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+		objectMapper.disable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS );
+		this.objectMapper = objectMapper;
 	}
 
 	@Override
 	public <T> void writeToTarget(T value, JavaType<T> javaType, Object target, WrapperOptions options)
 			throws IOException {
-		objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) ).writeValue( (JsonGenerator) target, value);
-
+		objectMapper.writerFor( objectMapper.constructType( javaType.getJavaType() ) )
+				.writeValue( (JsonGenerator) target, value );
 	}
 
 	@Override
 	public <T> T readFromSource(JavaType<T> javaType, Object source, WrapperOptions options) throws IOException {
-		return  objectMapper.readValue( (JsonParser)source, objectMapper.constructType( javaType.getJavaType()) );
+		return objectMapper.readValue( (JsonParser) source, objectMapper.constructType( javaType.getJavaType() ) );
 	}
 
 	@Override
@@ -56,5 +65,24 @@ public boolean supportsTargetType(Class<?> targetType) {
 		return JsonGenerator.class.isAssignableFrom( targetType );
 	}
 
+	@Override
+	public <T> T fromString(CharSequence charSequence, Type type) {
+		try {
+			return objectMapper.readValue( charSequence.toString(), objectMapper.constructType( type ) );
+		}
+		catch (JsonProcessingException e) {
+			throw new IllegalArgumentException( "Could not deserialize string to java type: " + type, e );
+		}
+	}
+
+	@Override
+	public <T> String toString(T value, Type type) {
+		try {
+			return objectMapper.writerFor( objectMapper.constructType( type ) ).writeValueAsString( value );
+		}
+		catch (JsonProcessingException e) {
+			throw new IllegalArgumentException( "Could not serialize object of java type: " + type, e );
+		}
+	}
 
 }
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
index 81c0f73d0ec4..7f0a874dcb2d 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/embeddable/JsonEmbeddableArrayTest.java
@@ -19,6 +19,7 @@
 import java.util.UUID;
 
 import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.testing.orm.junit.FailureExpected;
 import org.hibernate.type.SqlTypes;
 
 import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
@@ -117,6 +118,7 @@ public void testDomainResult() {
 	}
 
 	@Test
+	@FailureExpected(jiraKey = "HHH-18717", reason = "Requires array functions to work with JSON_ARRAY")
 	public void testSelectionItems() {
 		sessionFactoryScope().inSession(
 				entityManager -> {
@@ -205,6 +207,7 @@ public void testUpdateAggregate() {
 	}
 
 	@Test
+	@FailureExpected(jiraKey = "HHH-18717", reason = "Requires array functions to work with JSON_ARRAY")
 	public void testUpdateAggregateMember() {
 		sessionFactoryScope().inTransaction(
 				entityManager -> {
@@ -217,6 +220,7 @@ public void testUpdateAggregateMember() {
 	}
 
 	@Test
+	@FailureExpected(jiraKey = "HHH-18717", reason = "Requires array functions to work with JSON_ARRAY")
 	public void testUpdateMultipleAggregateMembers() {
 		sessionFactoryScope().inTransaction(
 				entityManager -> {
@@ -230,6 +234,7 @@ public void testUpdateMultipleAggregateMembers() {
 	}
 
 	@Test
+	@FailureExpected(jiraKey = "HHH-18717", reason = "Requires array functions to work with JSON_ARRAY")
 	public void testUpdateAllAggregateMembers() {
 		sessionFactoryScope().inTransaction(
 				entityManager -> {
diff --git a/local-build-plugins/src/main/groovy/local.java-module.gradle b/local-build-plugins/src/main/groovy/local.java-module.gradle
index 291d4a8a4f2c..c548f82ff3a4 100644
--- a/local-build-plugins/src/main/groovy/local.java-module.gradle
+++ b/local-build-plugins/src/main/groovy/local.java-module.gradle
@@ -101,6 +101,9 @@ dependencies {
         testRuntimeOnly jdbcLibs.oracle
         testRuntimeOnly jdbcLibs.oracleXml
         testRuntimeOnly jdbcLibs.oracleXmlParser
+        testRuntimeOnly (jdbcLibs.oracleJdbcJacksonOsonExtension) {
+            exclude group: 'com.oracle.database.jdbc', module: 'ojdbc8'
+        }
     }
     else if ( db.startsWith( 'altibase' ) ) {
         testRuntimeOnly jdbcLibs.altibase

From ebf2fd010f69730a602fbd5db479413676289641 Mon Sep 17 00:00:00 2001
From: Christian Beikov <christian.beikov@gmail.com>
Date: Thu, 17 Apr 2025 17:34:09 +0200
Subject: [PATCH 66/81] HHH-17404 Parameterize DDL type for tests

---
 ....java => OracleOsonCompatibilityTest.java} | 66 +++++++++++++------
 1 file changed, 45 insertions(+), 21 deletions(-)
 rename hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/{JsonCBLOBToOsonTest.java => OracleOsonCompatibilityTest.java} (66%)

diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/JsonCBLOBToOsonTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
similarity index 66%
rename from hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/JsonCBLOBToOsonTest.java
rename to hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
index 06a11ecca312..d9fac7534f25 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/JsonCBLOBToOsonTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
@@ -8,18 +8,20 @@
 import jakarta.persistence.Id;
 import jakarta.persistence.Table;
 import org.hibernate.annotations.JdbcTypeCode;
+import org.hibernate.cfg.DialectSpecificSettings;
 import org.hibernate.dialect.OracleDialect;
 import org.hibernate.orm.test.mapping.basic.JsonMappingTests;
 import org.hibernate.testing.orm.junit.DomainModel;
 import org.hibernate.testing.orm.junit.RequiresDialect;
+import org.hibernate.testing.orm.junit.ServiceRegistry;
 import org.hibernate.testing.orm.junit.SessionFactory;
 import org.hibernate.testing.orm.junit.SessionFactoryScope;
+import org.hibernate.testing.orm.junit.Setting;
 import org.hibernate.type.SqlTypes;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hibernate.testing.orm.junit.DialectContext.getDialect;
@@ -32,36 +34,43 @@
  *
  * @author Emmanuel Jannetti
  */
-@DomainModel(annotatedClasses = JsonCBLOBToOsonTest.JsonEntity.class)
-@SessionFactory
+@DomainModel(annotatedClasses = OracleOsonCompatibilityTest.JsonEntity.class)
+@SessionFactory(exportSchema = false)
 @RequiresDialect( value = OracleDialect.class, majorVersion = 23 )
-public class JsonCBLOBToOsonTest {
-
-	@Entity(name = "JsonEntity")
-	@Table(name = "TEST_OSON_COMPAT")
-	public static class JsonEntity {
-		@Id
-		private Integer id;
-		@JdbcTypeCode( SqlTypes.JSON )
-		private JsonMappingTests.StringNode jsonName;
+public abstract class OracleOsonCompatibilityTest {
 
-		public JsonEntity() {
-			super();
+	@ServiceRegistry(settings = @Setting(name = DialectSpecificSettings.ORACLE_OSON_DISABLED, value = "true"))
+	public static class OracleOsonAsUtf8CompatibilityTest extends OracleOsonCompatibilityTest {
+		public OracleOsonAsUtf8CompatibilityTest() {
+			super( "JSON" );
 		}
-		public JsonEntity(Integer id,  JsonMappingTests.StringNode node) {
-			this.id = id;
-			this.jsonName = node;
+	}
+	public static class OracleBlobAsOsonCompatibilityTest extends OracleOsonCompatibilityTest {
+		public OracleBlobAsOsonCompatibilityTest() {
+			super( "BLOB" );
 		}
 	}
+	public static class OracleClobAsOsonCompatibilityTest extends OracleOsonCompatibilityTest {
+		public OracleClobAsOsonCompatibilityTest() {
+			super( "CLOB" );
+		}
+	}
+
+
+	private final String jsonType;
+
+	public OracleOsonCompatibilityTest(String jsonType) {
+		this.jsonType = jsonType;
+	}
 
 	@BeforeEach
 	public void setup(SessionFactoryScope scope) {
 		scope.inTransaction(
 				(session) -> {
 					// force creation of a BLOB column type by creating the table ourselves
-					session.createNativeQuery( getDialect().getDropTableString( "TEST_OSON_COMPAT" ) )
+					session.createNativeQuery( session.getDialect().getDropTableString( "TEST_OSON_COMPAT" ) )
 							.executeUpdate();
-					session.createNativeQuery( "CREATE TABLE TEST_OSON_COMPAT (id NUMBER, jsonName BLOB CHECK (jsonName is json)  ,primary key (id))" )
+					session.createNativeQuery( "CREATE TABLE TEST_OSON_COMPAT (id NUMBER, jsonName " + jsonType + " CHECK (jsonName is json)  ,primary key (id))" )
 							.executeUpdate();
 
 					String insert = "INSERT INTO TEST_OSON_COMPAT (id, jsonName) VALUES(:id,:json)";
@@ -85,11 +94,26 @@ public void tearDown(SessionFactoryScope scope) {
 	public void verifyReadWorks(SessionFactoryScope scope) {
 		scope.inTransaction(
 				(session) -> {
-					JsonEntity entity = session.find( JsonCBLOBToOsonTest.JsonEntity.class, 1 );
+					JsonEntity entity = session.find( OracleOsonCompatibilityTest.JsonEntity.class, 1 );
 					assertThat( entity.jsonName.getString(), is( "john" ) );
-
 				}
 		);
 	}
 
+	@Entity(name = "JsonEntity")
+	@Table(name = "TEST_OSON_COMPAT")
+	public static class JsonEntity {
+		@Id
+		private Integer id;
+		@JdbcTypeCode( SqlTypes.JSON )
+		private JsonMappingTests.StringNode jsonName;
+
+		public JsonEntity() {
+			super();
+		}
+		public JsonEntity(Integer id,  JsonMappingTests.StringNode node) {
+			this.id = id;
+			this.jsonName = node;
+		}
+	}
 }

From 33c699c00f2a34f7d157113e37803e35bf392477 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Wed, 30 Apr 2025 10:34:55 +0200
Subject: [PATCH 67/81] HHH-17404 add compatibility test

---
 .../SessionFactoryOptionsBuilder.java         |   7 +-
 .../internal/StrategySelectorBuilder.java     |   2 +-
 .../hibernate/dialect/OracleOsonJdbcType.java | 113 +++++++++++-------
 .../dialect/type/OracleJdbcHelper.java        |   2 +-
 .../hhh17404/OracleOsonCompatibilityTest.java |  92 ++++++++++++--
 5 files changed, 161 insertions(+), 55 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
index d0b19c8f6e5f..c405f088996d 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
@@ -94,6 +94,7 @@
 import static org.hibernate.cfg.CacheSettings.JPA_SHARED_CACHE_RETRIEVE_MODE;
 import static org.hibernate.cfg.CacheSettings.JPA_SHARED_CACHE_STORE_MODE;
 import static org.hibernate.cfg.CacheSettings.QUERY_CACHE_LAYOUT;
+import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.cfg.PersistenceSettings.UNOWNED_ASSOCIATION_TRANSIENT_CHECK;
 import static org.hibernate.cfg.QuerySettings.DEFAULT_NULL_ORDERING;
 import static org.hibernate.cfg.QuerySettings.JSON_FUNCTIONS_ENABLED;
@@ -132,6 +133,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
 
 	private final String uuid = LocalObjectUuidHelper.generateLocalObjectUuid();
 	private final StandardServiceRegistry serviceRegistry;
+	private static boolean osonExtensionEnabled;
 
 	// integration
 	private Object beanManagerReference;
@@ -305,10 +307,13 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
 				settings.get( AvailableSettings.JAKARTA_VALIDATION_FACTORY )
 		);
 
+		osonExtensionEnabled = !getBoolean( ORACLE_OSON_DISABLED ,settings);
+
 		jsonFormatMapper = determineJsonFormatMapper(
 				settings.get( AvailableSettings.JSON_FORMAT_MAPPER ),
 				strategySelector
 		);
+
 		xmlFormatMapper = determineXmlFormatMapper(
 				settings.get( AvailableSettings.XML_FORMAT_MAPPER ),
 				strategySelector,
@@ -795,7 +800,7 @@ private static FormatMapper determineJsonFormatMapper(Object setting, StrategySe
 				setting,
 				(Callable<FormatMapper>) () -> {
 					// Prefer the OSON Jackson FormatMapper by default if available
-					final FormatMapper jsonJacksonFormatMapper = JacksonIntegration.isJacksonOsonExtensionAvailable()
+					final FormatMapper jsonJacksonFormatMapper = (osonExtensionEnabled && JacksonIntegration.isJacksonOsonExtensionAvailable())
 							? getOsonJacksonFormatMapperOrNull()
 							: getJsonJacksonFormatMapperOrNull();
 					return jsonJacksonFormatMapper != null ? jsonJacksonFormatMapper : getJakartaJsonBFormatMapperOrNull();
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
index af0eda071fda..ffbb80518df0 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
@@ -313,7 +313,7 @@ private static void addJsonFormatMappers(StrategySelectorImpl strategySelector)
 				JacksonJsonFormatMapper.SHORT_NAME,
 				JacksonJsonFormatMapper.class
 		);
-		if ( JacksonIntegration.isJacksonOsonExtensionAvailable() ) {
+		if (JacksonIntegration.isJacksonOsonExtensionAvailable() ) {
 			strategySelector.registerStrategyImplementor(
 					FormatMapper.class,
 					JacksonOsonFormatMapper.SHORT_NAME,
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
index 6a4ffc8840d7..4906d5f35477 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
@@ -31,6 +31,7 @@
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
 
 /**
  *
@@ -169,7 +170,7 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
 
 			private boolean useUtf8(WrapperOptions options) {
 				return getEmbeddableMappingType() == null
-					&& !options.getJsonFormatMapper().supportsTargetType( OracleOsonJacksonHelper.READER_CLASS );
+					&& !options.getJsonFormatMapper().supportsSourceType(OracleOsonJacksonHelper.READER_CLASS );
 			}
 
 			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
@@ -196,74 +197,106 @@ private X fromString(byte[] json, WrapperOptions options) throws SQLException {
 				);
 			}
 
+			private byte[] getBytesFromResultSetByIndex(ResultSet rs, int index) throws SQLException {
+				// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
+				// and getString is not supported on BLOB. W have to try both
+				try {
+					return rs.getBytes( index);
+				} catch (SQLFeatureNotSupportedException nse) {
+					return rs.getString( index).getBytes();
+				}
+			}
+
+			private byte[] getBytesFromStatementByIndex(CallableStatement st, int index) throws SQLException {
+				// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
+				// and getString is not supported on BLOB. W have to try both
+				try {
+					return st.getBytes( index);
+				} catch (SQLFeatureNotSupportedException nse) {
+
+								return st.getString( index).getBytes();
+				}
+			}
+			private byte[] getBytesFromStatementByName(CallableStatement st, String columnName) throws SQLException {
+				// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
+				// and getString is not supported on BLOB. W have to try both
+				try {
+					return st.getBytes( columnName);
+				} catch (SQLFeatureNotSupportedException nse) {
+					return st.getString( columnName).getBytes();
+				}
+			}
+
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-				try {
 					if ( useUtf8( options ) ) {
-						return fromString( rs.getBytes( paramIndex ), options );
+						return fromString(getBytesFromResultSetByIndex(rs, paramIndex), options );
 					}
 					else {
+						try {
 						OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
+						} catch (SQLException exc) {
+							if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+								// This may happen if we are fetching data from an existing schema
+								// that uses BLOB for JSON column In that case we assume bytes are
+								// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+								LOG.invalidJSONColumnType( OracleType.BLOB.getName(), OracleType.JSON.getName() );
+								return fromString(getBytesFromResultSetByIndex(rs, paramIndex), options );
+							} else {
+								throw exc;
+							}
+						}
 					}
-				} catch (SQLException exc) {
-					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-						// This may happen if we are fetching data from an existing schema
-						// that uses BLOB for JSON column In that case we assume bytes are
-						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
-						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return fromString( rs.getBytes( paramIndex ), options );
-					} else {
-						throw exc;
-					}
-				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-				try {
 					if ( useUtf8( options ) ) {
-						return fromString( statement.getBytes( index ), options );
+						return fromString(getBytesFromStatementByIndex(statement, index), options);
 					}
 					else {
+						try {
 						OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
+						} catch (SQLException exc) {
+							if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+								// This may happen if we are fetching data from an existing schema
+								// that uses BLOB for JSON column In that case we assume bytes are
+								// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+								LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+								return fromString(getBytesFromStatementByIndex(statement, index), options);
+							} else {
+								throw exc;
+							}
+						}
 					}
-				} catch (SQLException exc) {
-					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-						// This may happen if we are fetching data from an existing schema
-						// that uses BLOB for JSON column In that case we assume bytes are
-						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
-						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return fromString( statement.getBytes( index ), options);
-					} else {
-						throw exc;
-					}
-				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
-				try {
+
 					if ( useUtf8( options ) ) {
-						return fromString( statement.getBytes( name ), options );
+						return fromString(getBytesFromStatementByName(statement, name), options);
 					}
 					else {
+						try {
 						OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
+						} catch (SQLException exc) {
+							if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
+								// This may happen if we are fetching data from an existing schema
+								// that uses BLOB for JSON column In that case we assume bytes are
+								// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+								LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+								return fromString(getBytesFromStatementByName(statement, name), options);
+							} else {
+								throw exc;
+							}
+						}
 					}
-				} catch (SQLException exc) {
-					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-						// This may happen if we are fetching data from an existing schema
-						// that uses BLOB for JSON column In that case we assume bytes are
-						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
-						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-						return fromString( statement.getBytes( name ), options);
-					} else {
-						throw exc;
-					}
-				}
+
 
 			}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java
index be26134db251..e800b215dfbe 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java
@@ -34,7 +34,7 @@ public static boolean isUsable(ServiceRegistry serviceRegistry) {
 	public static boolean isOsonAvailable(ServiceRegistry serviceRegistry) {
 		final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
 		try {
-			classLoaderService.classForName( "oracle.sql.json.OracleJsonFactory" );
+			classLoaderService.classForName( "oracle.jdbc.provider.oson.OsonFactory" );
 			return true;
 		}
 		catch (ClassLoadingException ex) {
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
index d9fac7534f25..d2a752cfa906 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
@@ -4,6 +4,9 @@
  */
 package org.hibernate.orm.test.mapping.hhh17404;
 
+import jakarta.persistence.Access;
+import jakarta.persistence.AccessType;
+import jakarta.persistence.Embeddable;
 import jakarta.persistence.Entity;
 import jakarta.persistence.Id;
 import jakarta.persistence.Table;
@@ -22,6 +25,11 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.UUID;
+
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hibernate.testing.orm.junit.DialectContext.getDialect;
@@ -67,16 +75,37 @@ public OracleOsonCompatibilityTest(String jsonType) {
 	public void setup(SessionFactoryScope scope) {
 		scope.inTransaction(
 				(session) -> {
-					// force creation of a BLOB column type by creating the table ourselves
+					// force creation of a column type by creating the table ourselves
 					session.createNativeQuery( session.getDialect().getDropTableString( "TEST_OSON_COMPAT" ) )
 							.executeUpdate();
-					session.createNativeQuery( "CREATE TABLE TEST_OSON_COMPAT (id NUMBER, jsonName " + jsonType + " CHECK (jsonName is json)  ,primary key (id))" )
-							.executeUpdate();
+					StringBuilder create = new StringBuilder();
+					create.append("CREATE TABLE TEST_OSON_COMPAT (");
+					create.append( "id NUMBER").append(',');
+					create.append( "payload ").append(jsonType).append(" CHECK (payload is null or json)").append(',');
+					create.append( "primary key (id))");
+					session.createNativeQuery(create.toString()).executeUpdate();
+
+					String insert = "INSERT INTO TEST_OSON_COMPAT (id, payload) VALUES(:id,:json)";
 
-					String insert = "INSERT INTO TEST_OSON_COMPAT (id, jsonName) VALUES(:id,:json)";
-					String jsonstr = "{\"string\":\"john\"}";
-					session.createNativeQuery(insert).setParameter("id",1)
-							.setParameter( "json", jsonstr).executeUpdate();
+					LocalDateTime theLocalDateTime = LocalDateTime.of( 2000, 1, 1, 0, 0, 0 );
+					LocalDate theLocalDate = LocalDate.of( 2000, 1, 1 );
+					LocalTime theLocalTime = LocalTime.of( 1, 0, 0 );
+					UUID uuid = UUID.fromString("53886a8a-7082-4879-b430-25cb94415be8");
+					String theString = "john";
+
+					StringBuilder j = new StringBuilder();
+					j.append( "{" );
+					j.append( "\"jsonString\":\"").append(theString).append("\"," );
+					j.append( "\"theUuid\":\"").append(uuid).append("\"," );
+					j.append( "\"theLocalDateTime\":\"").append(theLocalDateTime).append("\"," );
+					j.append( "\"theLocalDate\":\"").append(theLocalDate).append("\"," );
+					j.append( "\"theLocalTime\":\"").append(theLocalTime).append("\"" );
+					j.append( "}" );
+
+					session.createNativeQuery(insert)
+							.setParameter("id",1)
+							.setParameter( "json", j.toString())
+							.executeUpdate();
 				}
 		);
 	}
@@ -95,7 +124,8 @@ public void verifyReadWorks(SessionFactoryScope scope) {
 		scope.inTransaction(
 				(session) -> {
 					JsonEntity entity = session.find( OracleOsonCompatibilityTest.JsonEntity.class, 1 );
-					assertThat( entity.jsonName.getString(), is( "john" ) );
+					assertThat( entity.payload.jsonString.getString(), is( "john" ) );
+					assertThat( entity.payload.theUuid, is( "53886a8a-7082-4879-b430-25cb94415be8" ) );
 				}
 		);
 	}
@@ -105,15 +135,53 @@ public void verifyReadWorks(SessionFactoryScope scope) {
 	public static class JsonEntity {
 		@Id
 		private Integer id;
+
 		@JdbcTypeCode( SqlTypes.JSON )
-		private JsonMappingTests.StringNode jsonName;
+		private JsonEntityPayload payload;
 
 		public JsonEntity() {
-			super();
 		}
-		public JsonEntity(Integer id,  JsonMappingTests.StringNode node) {
+
+		public JsonEntity(Integer id, JsonEntityPayload payload) {
 			this.id = id;
-			this.jsonName = node;
+			this.payload = payload;
+		}
+
+		public Integer getId() {
+			return id;
+		}
+
+		public void setId(Integer id) {
+			this.id = id;
+		}
+
+		public JsonEntityPayload getPayload() {
+			return payload;
+		}
+
+		public void setPayload(JsonEntityPayload payload) {
+			this.payload = payload;
+		}
+
+	}
+	@Embeddable
+	@Access( AccessType.PROPERTY )
+	public static class JsonEntityPayload {
+		private JsonMappingTests.StringNode jsonString;
+		private UUID theUuid;
+		private LocalDateTime theLocalDateTime;
+		private LocalDate theLocalDate;
+		private LocalTime theLocalTime;
+
+		public JsonEntityPayload() {
+
+		}
+		public JsonEntityPayload(JsonMappingTests.StringNode jsonString, UUID theUuid, LocalDateTime theLocalDateTime, LocalDate theLocalDate, LocalTime theLocalTime) {
+			this.jsonString = jsonString;
+			this.theUuid = theUuid;
+			this.theLocalDateTime = theLocalDateTime;
+			this.theLocalDate = theLocalDate;
+			this.theLocalTime = theLocalTime;
 		}
 	}
 }

From 96cd77d105a978bdbb0b09160871aa198c324101 Mon Sep 17 00:00:00 2001
From: Marco Belladelli <marcobladel@gmail.com>
Date: Wed, 30 Apr 2025 11:43:43 +0200
Subject: [PATCH 68/81] HHH-17404 Oson compatibility test fixes

---
 .../hhh17404/OracleOsonCompatibilityTest.java | 61 +++++--------------
 1 file changed, 15 insertions(+), 46 deletions(-)

diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
index d2a752cfa906..d9d71b87eb3e 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
@@ -4,8 +4,6 @@
  */
 package org.hibernate.orm.test.mapping.hhh17404;
 
-import jakarta.persistence.Access;
-import jakarta.persistence.AccessType;
 import jakarta.persistence.Embeddable;
 import jakarta.persistence.Entity;
 import jakarta.persistence.Id;
@@ -13,7 +11,6 @@
 import org.hibernate.annotations.JdbcTypeCode;
 import org.hibernate.cfg.DialectSpecificSettings;
 import org.hibernate.dialect.OracleDialect;
-import org.hibernate.orm.test.mapping.basic.JsonMappingTests;
 import org.hibernate.testing.orm.junit.DomainModel;
 import org.hibernate.testing.orm.junit.RequiresDialect;
 import org.hibernate.testing.orm.junit.ServiceRegistry;
@@ -21,7 +18,6 @@
 import org.hibernate.testing.orm.junit.SessionFactoryScope;
 import org.hibernate.testing.orm.junit.Setting;
 import org.hibernate.type.SqlTypes;
-import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -32,11 +28,10 @@
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.hibernate.testing.orm.junit.DialectContext.getDialect;
 
 /**
- * This test class is about testing that legacy schema that use BLO for JSON column
- * can be safely read even when Oracle Oson extention is in place.
+ * This test class is about testing that legacy schema that use BLOB or CLOB for JSON columns
+ * can be safely read even when Oracle Oson extension is in place.
  * In Such a situation, the JSON type will expect JSON a JSON column and should
  * silently fall back to String deserialization.
  *
@@ -53,11 +48,13 @@ public OracleOsonAsUtf8CompatibilityTest() {
 			super( "JSON" );
 		}
 	}
+
 	public static class OracleBlobAsOsonCompatibilityTest extends OracleOsonCompatibilityTest {
 		public OracleBlobAsOsonCompatibilityTest() {
 			super( "BLOB" );
 		}
 	}
+
 	public static class OracleClobAsOsonCompatibilityTest extends OracleOsonCompatibilityTest {
 		public OracleClobAsOsonCompatibilityTest() {
 			super( "CLOB" );
@@ -76,14 +73,14 @@ public void setup(SessionFactoryScope scope) {
 		scope.inTransaction(
 				(session) -> {
 					// force creation of a column type by creating the table ourselves
-					session.createNativeQuery( session.getDialect().getDropTableString( "TEST_OSON_COMPAT" ) )
+					session.createNativeMutationQuery( session.getDialect().getDropTableString( "TEST_OSON_COMPAT" ) )
 							.executeUpdate();
 					StringBuilder create = new StringBuilder();
 					create.append("CREATE TABLE TEST_OSON_COMPAT (");
 					create.append( "id NUMBER").append(',');
-					create.append( "payload ").append(jsonType).append(" CHECK (payload is null or json)").append(',');
+					create.append( "payload ").append(jsonType).append(',');
 					create.append( "primary key (id))");
-					session.createNativeQuery(create.toString()).executeUpdate();
+					session.createNativeMutationQuery(create.toString()).executeUpdate();
 
 					String insert = "INSERT INTO TEST_OSON_COMPAT (id, payload) VALUES(:id,:json)";
 
@@ -102,30 +99,22 @@ public void setup(SessionFactoryScope scope) {
 					j.append( "\"theLocalTime\":\"").append(theLocalTime).append("\"" );
 					j.append( "}" );
 
-					session.createNativeQuery(insert)
-							.setParameter("id",1)
-							.setParameter( "json", j.toString())
+					final Object json = jsonType.equals( "BLOB" ) ? j.toString().getBytes() : j.toString();
+					session.createNativeMutationQuery( insert )
+							.setParameter( "id", 1 )
+							.setParameter( "json", json )
 							.executeUpdate();
 				}
 		);
 	}
 
-	@AfterEach
-	public void tearDown(SessionFactoryScope scope) {
-		scope.inTransaction(
-				(session) -> {
-					session.createNativeQuery( getDialect().getDropTableString( "TEST_OSON_COMPAT" ) ).executeUpdate();
-				}
-		);
-	}
-
 	@Test
 	public void verifyReadWorks(SessionFactoryScope scope) {
 		scope.inTransaction(
 				(session) -> {
 					JsonEntity entity = session.find( OracleOsonCompatibilityTest.JsonEntity.class, 1 );
-					assertThat( entity.payload.jsonString.getString(), is( "john" ) );
-					assertThat( entity.payload.theUuid, is( "53886a8a-7082-4879-b430-25cb94415be8" ) );
+					assertThat( entity.payload.jsonString, is( "john" ) );
+					assertThat( entity.payload.theUuid.toString(), is( "53886a8a-7082-4879-b430-25cb94415be8" ) );
 				}
 		);
 	}
@@ -139,14 +128,6 @@ public static class JsonEntity {
 		@JdbcTypeCode( SqlTypes.JSON )
 		private JsonEntityPayload payload;
 
-		public JsonEntity() {
-		}
-
-		public JsonEntity(Integer id, JsonEntityPayload payload) {
-			this.id = id;
-			this.payload = payload;
-		}
-
 		public Integer getId() {
 			return id;
 		}
@@ -162,26 +143,14 @@ public JsonEntityPayload getPayload() {
 		public void setPayload(JsonEntityPayload payload) {
 			this.payload = payload;
 		}
-
 	}
+
 	@Embeddable
-	@Access( AccessType.PROPERTY )
 	public static class JsonEntityPayload {
-		private JsonMappingTests.StringNode jsonString;
+		private String jsonString;
 		private UUID theUuid;
 		private LocalDateTime theLocalDateTime;
 		private LocalDate theLocalDate;
 		private LocalTime theLocalTime;
-
-		public JsonEntityPayload() {
-
-		}
-		public JsonEntityPayload(JsonMappingTests.StringNode jsonString, UUID theUuid, LocalDateTime theLocalDateTime, LocalDate theLocalDate, LocalTime theLocalTime) {
-			this.jsonString = jsonString;
-			this.theUuid = theUuid;
-			this.theLocalDateTime = theLocalDateTime;
-			this.theLocalDate = theLocalDate;
-			this.theLocalTime = theLocalTime;
-		}
 	}
 }

From e3132d0a97a16e1d06152c6350586f8d41f146b2 Mon Sep 17 00:00:00 2001
From: Marco Belladelli <marcobladel@gmail.com>
Date: Wed, 30 Apr 2025 11:44:13 +0200
Subject: [PATCH 69/81] HHH-17404 Apply code-style to OracleOsonJdbcType and
 misc cleanups

---
 .../SessionFactoryOptionsBuilder.java         |  13 +-
 .../internal/StrategySelectorBuilder.java     |   2 +-
 .../hibernate/dialect/OracleOsonJdbcType.java | 137 +++++++++---------
 3 files changed, 77 insertions(+), 75 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
index c405f088996d..642449a502fa 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java
@@ -133,7 +133,6 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
 
 	private final String uuid = LocalObjectUuidHelper.generateLocalObjectUuid();
 	private final StandardServiceRegistry serviceRegistry;
-	private static boolean osonExtensionEnabled;
 
 	// integration
 	private Object beanManagerReference;
@@ -307,10 +306,9 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
 				settings.get( AvailableSettings.JAKARTA_VALIDATION_FACTORY )
 		);
 
-		osonExtensionEnabled = !getBoolean( ORACLE_OSON_DISABLED ,settings);
-
 		jsonFormatMapper = determineJsonFormatMapper(
 				settings.get( AvailableSettings.JSON_FORMAT_MAPPER ),
+				!getBoolean( ORACLE_OSON_DISABLED ,settings),
 				strategySelector
 		);
 
@@ -794,15 +792,16 @@ private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
 						.getDefaultConnectionHandlingMode();
 	}
 
-	private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector) {
+	private static FormatMapper determineJsonFormatMapper(Object setting, boolean osonExtensionEnabled, StrategySelector strategySelector) {
 		return strategySelector.resolveDefaultableStrategy(
 				FormatMapper.class,
 				setting,
 				(Callable<FormatMapper>) () -> {
 					// Prefer the OSON Jackson FormatMapper by default if available
-					final FormatMapper jsonJacksonFormatMapper = (osonExtensionEnabled && JacksonIntegration.isJacksonOsonExtensionAvailable())
-							? getOsonJacksonFormatMapperOrNull()
-							: getJsonJacksonFormatMapperOrNull();
+					final FormatMapper jsonJacksonFormatMapper =
+							(osonExtensionEnabled && JacksonIntegration.isJacksonOsonExtensionAvailable())
+									? getOsonJacksonFormatMapperOrNull()
+									: getJsonJacksonFormatMapperOrNull();
 					return jsonJacksonFormatMapper != null ? jsonJacksonFormatMapper : getJakartaJsonBFormatMapperOrNull();
 				}
 		);
diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
index ffbb80518df0..af0eda071fda 100644
--- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
+++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java
@@ -313,7 +313,7 @@ private static void addJsonFormatMappers(StrategySelectorImpl strategySelector)
 				JacksonJsonFormatMapper.SHORT_NAME,
 				JacksonJsonFormatMapper.class
 		);
-		if (JacksonIntegration.isJacksonOsonExtensionAvailable() ) {
+		if ( JacksonIntegration.isJacksonOsonExtensionAvailable() ) {
 			strategySelector.registerStrategyImplementor(
 					FormatMapper.class,
 					JacksonOsonFormatMapper.SHORT_NAME,
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
index 4906d5f35477..558cbfc4b547 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
@@ -34,7 +34,6 @@
 import java.sql.SQLFeatureNotSupportedException;
 
 /**
- *
  * Type mapping JSON SQL data type for Oracle database.
  * This implementation is used when the JDBC OSON extension is available.
  *
@@ -67,7 +66,7 @@ public AggregateJdbcType resolveAggregateJdbcType(
 	@Override
 	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
-		if(javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class) {
+		if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
 			return super.getBinder( javaType );
 		}
 
@@ -75,7 +74,7 @@ public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
 
 			private <T> byte[] toOson(T value, JavaType<T> javaType, WrapperOptions options) throws Exception {
 				final ByteArrayOutputStream out = new ByteArrayOutputStream();
-				if (getEmbeddableMappingType() != null) {
+				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(
@@ -146,7 +145,7 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions
 	@Override
 	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
 
-		if(javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class) {
+		if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
 			return super.getExtractor( javaType );
 		}
 
@@ -170,16 +169,16 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
 
 			private boolean useUtf8(WrapperOptions options) {
 				return getEmbeddableMappingType() == null
-					&& !options.getJsonFormatMapper().supportsSourceType(OracleOsonJacksonHelper.READER_CLASS );
+					&& !options.getJsonFormatMapper().supportsSourceType( OracleOsonJacksonHelper.READER_CLASS );
 			}
 
-			private X doExtraction(OracleJsonDatum datum,  WrapperOptions options) throws SQLException {
+			private X doExtraction(OracleJsonDatum datum, WrapperOptions options) throws SQLException {
 				if ( datum == null ) {
 					return null;
 				}
 				InputStream osonBytes = datum.getStream();
 				try {
-					return fromOson( osonBytes ,options);
+					return fromOson( osonBytes, options );
 				}
 				catch (Exception e) {
 					throw new SQLException( e );
@@ -201,9 +200,10 @@ private byte[] getBytesFromResultSetByIndex(ResultSet rs, int index) throws SQLE
 				// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
 				// and getString is not supported on BLOB. W have to try both
 				try {
-					return rs.getBytes( index);
-				} catch (SQLFeatureNotSupportedException nse) {
-					return rs.getString( index).getBytes();
+					return rs.getBytes( index );
+				}
+				catch (SQLFeatureNotSupportedException nse) {
+					return rs.getString( index ).getBytes();
 				}
 			}
 
@@ -211,97 +211,100 @@ private byte[] getBytesFromStatementByIndex(CallableStatement st, int index) thr
 				// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
 				// and getString is not supported on BLOB. W have to try both
 				try {
-					return st.getBytes( index);
-				} catch (SQLFeatureNotSupportedException nse) {
+					return st.getBytes( index );
+				}
+				catch (SQLFeatureNotSupportedException nse) {
 
-								return st.getString( index).getBytes();
+					return st.getString( index ).getBytes();
 				}
 			}
+
 			private byte[] getBytesFromStatementByName(CallableStatement st, String columnName) throws SQLException {
 				// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
 				// and getString is not supported on BLOB. W have to try both
 				try {
-					return st.getBytes( columnName);
-				} catch (SQLFeatureNotSupportedException nse) {
-					return st.getString( columnName).getBytes();
+					return st.getBytes( columnName );
+				}
+				catch (SQLFeatureNotSupportedException nse) {
+					return st.getString( columnName ).getBytes();
 				}
 			}
 
 			@Override
 			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
-					if ( useUtf8( options ) ) {
-						return fromString(getBytesFromResultSetByIndex(rs, paramIndex), options );
-					}
-					else {
-						try {
+				if ( useUtf8( options ) ) {
+					return fromString( getBytesFromResultSetByIndex( rs, paramIndex ), options );
+				}
+				else {
+					try {
 						OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
-						} catch (SQLException exc) {
-							if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-								// This may happen if we are fetching data from an existing schema
-								// that uses BLOB for JSON column In that case we assume bytes are
-								// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
-								LOG.invalidJSONColumnType( OracleType.BLOB.getName(), OracleType.JSON.getName() );
-								return fromString(getBytesFromResultSetByIndex(rs, paramIndex), options );
-							} else {
-								throw exc;
-							}
+					}
+					catch (SQLException exc) {
+						if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE ) {
+							// This may happen if we are fetching data from an existing schema
+							// that uses BLOB for JSON column. In that case we assume bytes are
+							// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+							LOG.invalidJSONColumnType( OracleType.BLOB.getName(), OracleType.JSON.getName() );
+							return fromString( getBytesFromResultSetByIndex( rs, paramIndex ), options );
+						}
+						else {
+							throw exc;
 						}
 					}
+				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
-					if ( useUtf8( options ) ) {
-						return fromString(getBytesFromStatementByIndex(statement, index), options);
-					}
-					else {
-						try {
+				if ( useUtf8( options ) ) {
+					return fromString( getBytesFromStatementByIndex( statement, index ), options );
+				}
+				else {
+					try {
 						OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
-						} catch (SQLException exc) {
-							if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-								// This may happen if we are fetching data from an existing schema
-								// that uses BLOB for JSON column In that case we assume bytes are
-								// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
-								LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-								return fromString(getBytesFromStatementByIndex(statement, index), options);
-							} else {
-								throw exc;
-							}
+					}
+					catch (SQLException exc) {
+						if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE ) {
+							// This may happen if we are fetching data from an existing schema
+							// that uses BLOB for JSON column In that case we assume bytes are
+							// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+							LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+							return fromString( getBytesFromStatementByIndex( statement, index ), options );
+						}
+						else {
+							throw exc;
 						}
 					}
+				}
 			}
 
 			@Override
 			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
 					throws SQLException {
-
-					if ( useUtf8( options ) ) {
-						return fromString(getBytesFromStatementByName(statement, name), options);
-					}
-					else {
-						try {
+				if ( useUtf8( options ) ) {
+					return fromString( getBytesFromStatementByName( statement, name ), options );
+				}
+				else {
+					try {
 						OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
-						} catch (SQLException exc) {
-							if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
-								// This may happen if we are fetching data from an existing schema
-								// that uses BLOB for JSON column In that case we assume bytes are
-								// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
-								LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
-								return fromString(getBytesFromStatementByName(statement, name), options);
-							} else {
-								throw exc;
-							}
+					}
+					catch (SQLException exc) {
+						if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE ) {
+							// This may happen if we are fetching data from an existing schema
+							// that uses BLOB for JSON column In that case we assume bytes are
+							// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
+							LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
+							return fromString( getBytesFromStatementByName( statement, name ), options );
+						}
+						else {
+							throw exc;
 						}
 					}
-
-
+				}
 			}
-
 		};
 	}
-
-
 }

From 38da5faeb4b01d2b1f39539ac998e97f250bc9b8 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Wed, 30 Apr 2025 16:00:38 +0200
Subject: [PATCH 70/81] HHH-17404 rebase with upstream

---
 .../java/org/hibernate/dialect/OracleOsonArrayJdbcType.java | 2 ++
 .../main/java/org/hibernate/dialect/OracleOsonJdbcType.java | 2 ++
 .../java/org/hibernate/dialect/type/OracleJsonJdbcType.java | 2 +-
 .../java/org/hibernate/type/descriptor/jdbc/JsonHelper.java | 4 ----
 .../org/hibernate/type/format/StringJsonDocumentReader.java | 2 ++
 .../type/format/StringJsonValueJDBCTypeAdapter.java         | 6 +++---
 6 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
index c0d7321b20a2..46f28693af49 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
@@ -9,6 +9,7 @@
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonGenerator;
 
+import org.hibernate.dialect.type.OracleJsonArrayJdbcType;
 import org.hibernate.internal.CoreLogging;
 import org.hibernate.internal.CoreMessageLogger;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@@ -22,6 +23,7 @@
 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.format.OsonDocumentReader;
 import org.hibernate.type.format.OsonDocumentWriter;
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
index 558cbfc4b547..93e2ebe9aa4a 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJdbcType.java
@@ -9,6 +9,7 @@
 import oracle.sql.json.OracleJsonDatum;
 import oracle.sql.json.OracleJsonFactory;
 import oracle.sql.json.OracleJsonGenerator;
+import org.hibernate.dialect.type.OracleJsonJdbcType;
 import org.hibernate.internal.CoreLogging;
 import org.hibernate.internal.CoreMessageLogger;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
@@ -20,6 +21,7 @@
 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.JsonHelper;
 import org.hibernate.type.format.OsonDocumentReader;
 import org.hibernate.type.format.OsonDocumentWriter;
 
diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java
index 36bc1ed331a0..943ccb9bbd8d 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJsonJdbcType.java
@@ -24,7 +24,7 @@ public class OracleJsonJdbcType extends OracleJsonBlobJdbcType {
 	 */
 	public static final OracleJsonJdbcType INSTANCE = new OracleJsonJdbcType( null );
 
-	OracleJsonJdbcType(EmbeddableMappingType embeddableMappingType) {
+	protected OracleJsonJdbcType(EmbeddableMappingType embeddableMappingType) {
 		super( embeddableMappingType );
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 52274ed6654b..7caa8711f845 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -34,10 +34,6 @@
 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.jdbc.AggregateJdbcType;
-import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
-import org.hibernate.type.descriptor.jdbc.JdbcType;
-import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
 import org.hibernate.type.format.JsonDocumentItemType;
 import org.hibernate.type.format.JsonDocumentReader;
 import org.hibernate.type.format.JsonDocumentWriter;
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index fa034cc4c594..380a719cc827 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -129,6 +129,8 @@ private void moveStateMachine(StringJsonDocumentMarker marker) {
 					this.processingStates.push( JsonProcessingState.ARRAY );
 				}
 				break;
+			default:
+				throw new IllegalStateException( "Unexpected JsonProcessingState " + marker );
 		}
 	}
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
index 90c41e3d58eb..41eba74f2911 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonValueJDBCTypeAdapter.java
@@ -4,8 +4,6 @@
  */
 package org.hibernate.type.format;
 
-import org.hibernate.dialect.StructAttributeValues;
-import org.hibernate.dialect.StructHelper;
 import org.hibernate.internal.util.CharSequenceHelper;
 import org.hibernate.metamodel.mapping.EmbeddableMappingType;
 import org.hibernate.type.SqlTypes;
@@ -19,10 +17,12 @@
 import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
 import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
 import org.hibernate.type.descriptor.jdbc.JdbcType;
+import org.hibernate.type.descriptor.jdbc.StructAttributeValues;
+import org.hibernate.type.descriptor.jdbc.StructHelper;
 
 import java.sql.SQLException;
 
-import static org.hibernate.dialect.StructHelper.instantiate;
+import static org.hibernate.type.descriptor.jdbc.StructHelper.instantiate;
 
 /**
  * JDBC type adapter for String-based JSON document reader.

From 6ec53e2ffa5ecb0b3b3c331063415019c7d181c3 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Wed, 30 Apr 2025 17:20:22 +0200
Subject: [PATCH 71/81] HHH-14707 fix code style

---
 .../dialect/OracleOsonArrayJdbcType.java       | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
index 46f28693af49..ee241225562d 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonArrayJdbcType.java
@@ -209,14 +209,16 @@ protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) thro
 						OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
 					}
-				} catch (SQLException exc) {
+				}
+				catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
 						return fromString( rs.getBytes( paramIndex ), options );
-					} else {
+					}
+					else {
 						throw exc;
 					}
 				}
@@ -232,14 +234,16 @@ protected X doExtract(CallableStatement statement, int index, WrapperOptions opt
 						OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
 					}
-				} catch (SQLException exc) {
+				}
+				catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
 						return fromString( statement.getBytes( index ), options );
-					} else {
+					}
+					else {
 						throw exc;
 					}
 				}
@@ -256,14 +260,16 @@ protected X doExtract(CallableStatement statement, String name, WrapperOptions o
 						OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
 						return doExtraction( ojd, options );
 					}
-				} catch (SQLException exc) {
+				}
+				catch (SQLException exc) {
 					if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
 						// This may happen if we are fetching data from an existing schema
 						// that uses BLOB for JSON column In that case we assume bytes are
 						// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
 						LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
 						return fromString( statement.getBytes( name ), options );
-					} else {
+					}
+					else {
 						throw exc;
 					}
 				}

From e9c0d2d2941abb3e6d9cf41fb4e9a380c642930e Mon Sep 17 00:00:00 2001
From: Marco Belladelli <marcobladel@gmail.com>
Date: Wed, 30 Apr 2025 17:54:12 +0200
Subject: [PATCH 72/81] HHH-14707 Fix xml embeddable aggregate array support

---
 .../java/org/hibernate/type/descriptor/jdbc/XmlHelper.java    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlHelper.java
index dda672dfc016..36e494fb83b2 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/XmlHelper.java
@@ -335,7 +335,7 @@ else if ( !string.startsWith( COLLECTION_START_TAG ) || !string.endsWith( COLLEC
 		final ArrayList<Object> arrayList = new ArrayList<>();
 		final int end = fromArrayString(
 				string,
-				false,
+				true,
 				options,
 				COLLECTION_START_TAG.length(),
 				arrayList,
@@ -646,7 +646,7 @@ private static int fromArrayString(
 								else {
 									arrayList.add( array );
 								}
-								i = end + 1;
+								i = end;
 							}
 							else {
 								throw new IllegalArgumentException( "XML not properly formed: " + string.substring( start ) );

From d8dfee5b372fc4ae94693396d92abd46e093ec18 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Wed, 30 Apr 2025 18:54:25 +0200
Subject: [PATCH 73/81] HHH-17404 code style

---
 .../java/org/hibernate/type/descriptor/jdbc/JsonHelper.java    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index 7caa8711f845..d1c4857b8e66 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -185,7 +185,8 @@ private static void serializeMapping(EmbeddableMappingType embeddableMappingType
 					writer.startObject();
 					serializeMapping(  (EmbeddableMappingType)attributeMapping.getMappedType(), values[i], options,writer);
 					writer.endObject();
-				} else {
+				}
+				else {
 					serialize(attributeMapping.getMappedType(), values[i], options, writer);
 				}
 

From fcfee69c37c87055ce5646061d6204b439822a3c Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Fri, 2 May 2025 10:46:25 +0200
Subject: [PATCH 74/81] HHH-17404 fix code scna warning about switch default
 case

---
 .../org/hibernate/type/format/StringJsonDocumentReader.java  | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index 380a719cc827..b1e5da28d09c 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -222,6 +222,11 @@ public JsonDocumentItemType next() {
 															"] in current processing state " +
 															this.processingStates.getCurrent() );
 					}
+				default: {
+					throw new IllegalStateException( "unexpected marker ["+
+													marker +
+													"] at position " + this.position );
+				}
 			}
 		}
 		throw new IllegalStateException( "unexpected end of JSON ["+

From 40ebb50db71eda5f28a2b9bcf5d7f0dec8491c88 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Fri, 2 May 2025 20:26:19 +0200
Subject: [PATCH 75/81] HHH-17404 enhance compatibility test on type chakcing

---
 .../hhh17404/OracleOsonCompatibilityTest.java     | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
index d9d71b87eb3e..dcf727b26ccc 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
@@ -64,6 +64,12 @@ public OracleClobAsOsonCompatibilityTest() {
 
 	private final String jsonType;
 
+	private final LocalDateTime theLocalDateTime = LocalDateTime.of( 2000, 1, 1, 0, 0, 0 );
+	private final LocalDate theLocalDate = LocalDate.of( 2000, 1, 1 );
+	private final LocalTime theLocalTime = LocalTime.of( 1, 0, 0 );
+	private final UUID uuid = UUID.fromString("53886a8a-7082-4879-b430-25cb94415be8");
+	private final String theString = "john";
+
 	public OracleOsonCompatibilityTest(String jsonType) {
 		this.jsonType = jsonType;
 	}
@@ -84,11 +90,7 @@ public void setup(SessionFactoryScope scope) {
 
 					String insert = "INSERT INTO TEST_OSON_COMPAT (id, payload) VALUES(:id,:json)";
 
-					LocalDateTime theLocalDateTime = LocalDateTime.of( 2000, 1, 1, 0, 0, 0 );
-					LocalDate theLocalDate = LocalDate.of( 2000, 1, 1 );
-					LocalTime theLocalTime = LocalTime.of( 1, 0, 0 );
-					UUID uuid = UUID.fromString("53886a8a-7082-4879-b430-25cb94415be8");
-					String theString = "john";
+
 
 					StringBuilder j = new StringBuilder();
 					j.append( "{" );
@@ -115,6 +117,9 @@ public void verifyReadWorks(SessionFactoryScope scope) {
 					JsonEntity entity = session.find( OracleOsonCompatibilityTest.JsonEntity.class, 1 );
 					assertThat( entity.payload.jsonString, is( "john" ) );
 					assertThat( entity.payload.theUuid.toString(), is( "53886a8a-7082-4879-b430-25cb94415be8" ) );
+					assertThat( entity.payload.theLocalDateTime, is( theLocalDateTime ) );
+					assertThat( entity.payload.theLocalTime, is( theLocalTime ) );
+					assertThat( entity.payload.theLocalDate, is( theLocalDate ) );
 				}
 		);
 	}

From 4b0b76ef8d54e7c5b83230e744ff46d6fff275a9 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Tue, 6 May 2025 14:57:27 +0200
Subject: [PATCH 76/81] HHH-17404 add comment in the what's new doc

---
 whats-new.adoc | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/whats-new.adoc b/whats-new.adoc
index b01ad852eb73..9b1f2b30f64f 100644
--- a/whats-new.adoc
+++ b/whats-new.adoc
@@ -374,3 +374,19 @@ Support for `hbm.xml` mappings will be removed in 8.0.
 
 We offer a transformation of `hbm.xml` files into `mapping.xml` files, which is available both at build-time (Gradle plugin) and at run-time (using `hibernate.transform_hbm_xml.enabled=true`).
 
+
+[[OSON-support]]
+== OSON support
+
+
+Starting in 21c, link:https://docs.oracle.com/en/database/oracle/oracle-database/23/adjsn/json-oracle-database.html[Oracle JSON binary format] (also known as OSON) can now be used in Hibernate to store `JSON` data. It brings a performance boost by replacing the actual to/from String conversion.
+To enable the OSON support, the link:https://github.com/oracle/ojdbc-extensions/blob/main/ojdbc-provider-jackson-oson/README.md[Oracle JDBC extension] must be added to the application classpath.
+Here is an example using Gradle build system
+```
+runtimeOnly ('com.oracle.database.jdbc:ojdbc-provider-jackson-oson:1.0.4')
+{
+  exclude group: 'com.oracle.database.jdbc', module: 'ojdbc8'
+}
+```
+
+The use of OSON can be disabled by setting the following hibernate property `hibernate.dialect.oracle.oson_format_disabled=true`

From 9f4a5adacf5e445589a34264c1d67ee8fd396cb4 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Fri, 16 May 2025 10:32:13 +0200
Subject: [PATCH 77/81] HHH-17404 add test claeanup phase

---
 .../type/format/StringJsonDocumentReader.java      | 14 ++++++--------
 .../hhh17404/OracleOsonCompatibilityTest.java      | 10 ++++++++++
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
index b1e5da28d09c..6d02753a05a6 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentReader.java
@@ -114,14 +114,12 @@ private void moveStateMachine(StringJsonDocumentMarker marker) {
 				assert this.processingStates.getCurrent() == JsonProcessingState.OBJECT;
 				break;
 			case QUOTE:
-				switch ( currentState ) {
-					case STARTING_ARRAY:
-						this.processingStates.push( JsonProcessingState.ARRAY );
-						break;
-					case STARTING_OBJECT:
-						this.processingStates.push( JsonProcessingState.OBJECT );
-						this.processingStates.push( JsonProcessingState.OBJECT_KEY_NAME );
-						break;
+				if (currentState == JsonProcessingState.STARTING_ARRAY) {
+					this.processingStates.push( JsonProcessingState.ARRAY );
+				}
+				if (currentState == JsonProcessingState.STARTING_OBJECT) {
+					this.processingStates.push( JsonProcessingState.OBJECT );
+					this.processingStates.push( JsonProcessingState.OBJECT_KEY_NAME );
 				}
 				break;
 			case OTHER:
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
index dcf727b26ccc..c5f6e0db56dd 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java
@@ -18,6 +18,7 @@
 import org.hibernate.testing.orm.junit.SessionFactoryScope;
 import org.hibernate.testing.orm.junit.Setting;
 import org.hibernate.type.SqlTypes;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -110,6 +111,15 @@ public void setup(SessionFactoryScope scope) {
 		);
 	}
 
+	@AfterEach
+	public void tearDown(SessionFactoryScope scope) {
+		scope.inTransaction(
+				(session) -> {
+					session.createNativeQuery( session.getDialect().getDropTableString( "TEST_OSON_COMPAT" ) ).executeUpdate();
+				}
+		);
+	}
+
 	@Test
 	public void verifyReadWorks(SessionFactoryScope scope) {
 		scope.inTransaction(

From 520cf75a633833fdb8ac68becfb7191d4fe69ccc Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Fri, 6 Jun 2025 17:45:32 +0200
Subject: [PATCH 78/81] HHH-17404 rebase with upstream

---
 .../src/main/java/org/hibernate/dialect/OracleDialect.java     | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 15acdafc090a..f6f686726c35 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -117,6 +117,9 @@
 import org.jboss.logging.Logger;
 
 import static java.util.regex.Pattern.CASE_INSENSITIVE;
+import static org.hibernate.LockOptions.NO_WAIT;
+import static org.hibernate.LockOptions.SKIP_LOCKED;
+import static org.hibernate.LockOptions.WAIT_FOREVER;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getArrayJdbcTypeConstructor;

From 8d56bfe77e8d16a97c410a446370fa48ce1729b6 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Fri, 6 Jun 2025 18:56:52 +0200
Subject: [PATCH 79/81] HHH-17404 add new contributor to AUTHORS list         
 removed some unchecked cast warning

---
 AUTHORS.txt                                   |  1 +
 .../type/descriptor/jdbc/JsonHelper.java      |  8 +---
 .../type/format/JsonDocumentWriter.java       |  4 +-
 .../type/format/OsonDocumentWriter.java       | 36 +++++++++---------
 .../type/format/StringJsonDocumentWriter.java | 38 +++++++++++++------
 5 files changed, 49 insertions(+), 38 deletions(-)

diff --git a/AUTHORS.txt b/AUTHORS.txt
index c6e19d08e3b3..234330ef0bf7 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -5,6 +5,7 @@
 # Corporate contributors
 
 Red Hat, Inc.
+Oracle, Corporation.
 
 # Individual contributors
 
diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
index d1c4857b8e66..5021a6e6c1f5 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/JsonHelper.java
@@ -95,7 +95,7 @@ public static void serializeArray(JavaType<?> elementJavaType, JdbcType elementJ
 				writer.nullValue();
 			}
 			else {
-				writer.serializeJsonValue( value ,(JavaType<Object>) elementJavaType,elementJdbcType,options);
+				writer.serializeJsonValue( value ,(JavaType<?>) elementJavaType,elementJdbcType,options);
 			}
 		}
 		writer.endArray();
@@ -135,15 +135,11 @@ private static void serialize(MappingType mappedType, Object value, WrapperOptio
 		else if ( mappedType instanceof EmbeddableMappingType ) {
 			serialize( (EmbeddableMappingType) mappedType, value, options, writer );
 		}
-		else if ( mappedType instanceof BasicType<?> ) {
-			//noinspection unchecked
-			final BasicType<Object> basicType = (BasicType<Object>) mappedType;
-
+		else if ( mappedType instanceof BasicType<?> basicType) {
 			if ( isArrayType(basicType.getJdbcType())) {
 				final int length = Array.getLength( value );
 				writer.startArray();
 				if ( length != 0 ) {
-					//noinspection unchecked
 					final JavaType<Object> elementJavaType = ( (BasicPluralJavaType<Object>) basicType.getJdbcJavaType() ).getElementJavaType();
 					final JdbcType elementJdbcType = ( (ArrayJdbcType) basicType.getJdbcType() ).getElementJdbcType();
 					final Object domainArray = basicType.convertToRelationalValue( value );
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
index 93cef54dac51..03bac2085eb7 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/JsonDocumentWriter.java
@@ -83,8 +83,8 @@ public interface JsonDocumentWriter {
 	 * @param options the wrapping options
 	 * @return this instance
 	 */
-	JsonDocumentWriter serializeJsonValue(Object value,
-							JavaType<Object> javaType,
+	<T> JsonDocumentWriter serializeJsonValue(Object value,
+							JavaType<T> javaType,
 							JdbcType jdbcType,
 							WrapperOptions options);
 }
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
index 6514537e0cc6..a88eca7a908a 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/OsonDocumentWriter.java
@@ -100,7 +100,7 @@ public JsonDocumentWriter stringValue(String value) {
 	}
 
 	@Override
-	public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
+	public <T> JsonDocumentWriter serializeJsonValue(Object value, JavaType<T> javaType, JdbcType jdbcType, WrapperOptions options) {
 		serializeValue(value, javaType, jdbcType, options);
 		return this;
 	}
@@ -114,8 +114,8 @@ public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> java
 	 * @param jdbcType the JDBC SQL type of the value
 	 * @param options the wapping options.
 	 */
-	private void serializeValue(Object value,
-								JavaType<Object> javaType,
+	private <T> void serializeValue(Object value,
+								JavaType<T> javaType,
 								JdbcType jdbcType,
 								WrapperOptions options) {
 		switch ( jdbcType.getDefaultSqlTypeCode() ) {
@@ -132,23 +132,23 @@ private void serializeValue(Object value,
 					generator.write( ((Enum<?>) value ).ordinal() );
 					break;
 				}
-				generator.write( javaType.unwrap( value,Integer.class,options ) );
+				generator.write( javaType.unwrap( (T)value,Integer.class,options ) );
 				break;
 			case SqlTypes.BOOLEAN:
-				generator.write( javaType.unwrap( value,Boolean.class,options ) );
+				generator.write( javaType.unwrap( (T)value,Boolean.class,options ) );
 				break;
 			case SqlTypes.BIT:
-				generator.write( javaType.unwrap( value,Integer.class,options ) );
+				generator.write( javaType.unwrap( (T)value,Integer.class,options ) );
 				break;
 			case SqlTypes.BIGINT:
-				generator.write( javaType.unwrap( value, BigInteger.class,options ) );
+				generator.write( javaType.unwrap( (T)value, BigInteger.class,options ) );
 				break;
 			case SqlTypes.FLOAT:
-				generator.write( javaType.unwrap( value,Float.class,options ) );
+				generator.write( javaType.unwrap( (T)value,Float.class,options ) );
 				break;
 			case SqlTypes.REAL:
 			case SqlTypes.DOUBLE:
-				generator.write( javaType.unwrap( value,Double.class,options ) );
+				generator.write( javaType.unwrap( (T)value,Double.class,options ) );
 				break;
 			case SqlTypes.CHAR:
 			case SqlTypes.NCHAR:
@@ -169,40 +169,40 @@ private void serializeValue(Object value,
 			case SqlTypes.MATERIALIZED_NCLOB:
 			case SqlTypes.ENUM:
 			case SqlTypes.NAMED_ENUM:
-				generator.write( javaType.toString( value ) );
+				generator.write( javaType.toString( (T)value ) );
 				break;
 			case SqlTypes.DATE:
-				DATE dd = new DATE(javaType.unwrap( value,java.sql.Date.class,options ));
+				DATE dd = new DATE(javaType.unwrap( (T)value,java.sql.Date.class,options ));
 				OracleJsonDate jsonDate = new OracleJsonDateImpl(dd.shareBytes());
 				generator.write(jsonDate);
 				break;
 			case SqlTypes.TIME:
 			case SqlTypes.TIME_WITH_TIMEZONE:
 			case SqlTypes.TIME_UTC:
-				generator.write( javaType.toString( value ) );
+				generator.write( javaType.toString( (T)value ) );
 				break;
 			case SqlTypes.TIMESTAMP:
-				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( value, Timestamp.class, options ));
+				TIMESTAMP TS = new TIMESTAMP(javaType.unwrap( (T)value, Timestamp.class, options ));
 				OracleJsonTimestamp writeTimeStamp = new OracleJsonTimestampImpl(TS.shareBytes());
 				generator.write(writeTimeStamp);
 				break;
 			case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
-				OffsetDateTime dateTime = javaType.unwrap( value, OffsetDateTime.class, options );
+				OffsetDateTime dateTime = javaType.unwrap( (T)value, OffsetDateTime.class, options );
 				generator.write( dateTime );
 				break;
 			case SqlTypes.TIMESTAMP_UTC:
-				OffsetDateTime odt = javaType.unwrap( value, OffsetDateTime.class, options );
+				OffsetDateTime odt = javaType.unwrap( (T)value, OffsetDateTime.class, options );
 				generator.write( odt );
 				break;
 			case SqlTypes.NUMERIC:
 			case SqlTypes.DECIMAL:
-				BigDecimal bd = javaType.unwrap( value, BigDecimal.class, options );
+				BigDecimal bd = javaType.unwrap( (T)value, BigDecimal.class, options );
 				generator.write( bd );
 				break;
 
 			case SqlTypes.DURATION:
 			case SqlTypes.UUID:
-				generator.write( javaType.toString( value ) );
+				generator.write( javaType.toString( (T)value ) );
 				break;
 			case SqlTypes.BINARY:
 			case SqlTypes.VARBINARY:
@@ -211,7 +211,7 @@ private void serializeValue(Object value,
 			case SqlTypes.BLOB:
 			case SqlTypes.MATERIALIZED_BLOB:
 				// how to handle
-				byte[] bytes = javaType.unwrap( value, byte[].class, options );
+				byte[] bytes = javaType.unwrap( (T)value, byte[].class, options );
 				generator.write( bytes );
 				break;
 			case SqlTypes.ARRAY:
diff --git a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
index 48e3dd3bb090..c08aa01485fc 100644
--- a/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
+++ b/hibernate-core/src/main/java/org/hibernate/type/format/StringJsonDocumentWriter.java
@@ -108,7 +108,7 @@ public JsonDocumentWriter objectKey(String key) {
 			throw new IllegalArgumentException( "key cannot be null or empty" );
 		}
 
-		if (this.processingStates.getCurrent().equals( JsonProcessingState.OBJECT )) {
+		if (JsonProcessingState.OBJECT.equals(this.processingStates.getCurrent())) {
 			// we have started an object, and we are adding an item key: we do add a separator.
 			this.appender.append( StringJsonDocumentMarker.SEPARATOR.getMarkerCharacter() );
 		}
@@ -222,13 +222,24 @@ public JsonDocumentWriter stringValue(String value) {
 	}
 
 	@Override
-	public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> javaType, JdbcType jdbcType, WrapperOptions options) {
+	public <T> JsonDocumentWriter serializeJsonValue(Object value, JavaType<T> javaType, JdbcType jdbcType, WrapperOptions options) {
 		addItemsSeparator();
 		convertedBasicValueToString(value, options,this.appender,javaType,jdbcType);
 		moveProcessingStateMachine();
 		return this;
 	}
 
+	private <T> void convertedCastBasicValueToString(Object value,
+															WrapperOptions options,
+															JsonAppender appender,
+															JavaType<T> javaType,
+															JdbcType jdbcType) {
+		assert javaType.isInstance( value );
+		//noinspection unchecked
+		convertedBasicValueToString( (T) value, options, appender, javaType, jdbcType );
+	}
+
+
 	/**
 	 * Converts a value to String according to its mapping type.
 	 * This method serializes the value and writes it into the underlying appender
@@ -238,12 +249,15 @@ public JsonDocumentWriter serializeJsonValue(Object value, JavaType<Object> java
 	 * @param jdbcType the JDBC SQL type of the value
 	 * @param options the wapping options.
 	 */
-	private  void convertedBasicValueToString(
+	private <T> void convertedBasicValueToString(
 			Object value,
 			WrapperOptions options,
 			JsonAppender appender,
-			JavaType<Object> javaType,
+			JavaType<T> javaType,
 			JdbcType jdbcType) {
+
+		assert javaType.isInstance( value );
+
 		switch ( jdbcType.getDefaultSqlTypeCode() ) {
 			case SqlTypes.TINYINT:
 			case SqlTypes.SMALLINT:
@@ -264,7 +278,7 @@ private  void convertedBasicValueToString(
 			case SqlTypes.REAL:
 			case SqlTypes.DOUBLE:
 				// These types fit into the native representation of JSON, so let's use that
-				javaType.appendEncodedString( appender, value );
+				javaType.appendEncodedString( appender, (T)value );
 				break;
 			case SqlTypes.CHAR:
 			case SqlTypes.NCHAR:
@@ -290,7 +304,7 @@ private  void convertedBasicValueToString(
 				// These literals can contain the '"' character, so we need to escape it
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				appender.startEscaping();
-				javaType.appendEncodedString( appender, value );
+				javaType.appendEncodedString( appender, (T)value );
 				appender.endEscaping();
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
@@ -298,7 +312,7 @@ private  void convertedBasicValueToString(
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				JdbcDateJavaType.INSTANCE.appendEncodedString(
 						appender,
-						javaType.unwrap( value, java.sql.Date.class, options )
+						javaType.unwrap( (T)value, java.sql.Date.class, options )
 				);
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
@@ -308,7 +322,7 @@ private  void convertedBasicValueToString(
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				JdbcTimeJavaType.INSTANCE.appendEncodedString(
 						appender,
-						javaType.unwrap( value, java.sql.Time.class, options )
+						javaType.unwrap( (T)value, java.sql.Time.class, options )
 				);
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
@@ -316,7 +330,7 @@ private  void convertedBasicValueToString(
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				JdbcTimestampJavaType.INSTANCE.appendEncodedString(
 						appender,
-						javaType.unwrap( value, java.sql.Timestamp.class, options )
+						javaType.unwrap( (T)value, java.sql.Timestamp.class, options )
 				);
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
@@ -324,7 +338,7 @@ private  void convertedBasicValueToString(
 			case SqlTypes.TIMESTAMP_UTC:
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				DateTimeFormatter.ISO_OFFSET_DATE_TIME.formatTo(
-						javaType.unwrap( value, OffsetDateTime.class, options ),
+						javaType.unwrap( (T)value, OffsetDateTime.class, options ),
 						appender
 				);
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
@@ -335,7 +349,7 @@ private  void convertedBasicValueToString(
 			case SqlTypes.UUID:
 				// These types need to be serialized as JSON string, but don't have a need for escaping
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
-				javaType.appendEncodedString( appender, value );
+				javaType.appendEncodedString( appender, (T)value );
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.BINARY:
@@ -346,7 +360,7 @@ private  void convertedBasicValueToString(
 			case SqlTypes.MATERIALIZED_BLOB:
 				// These types need to be serialized as JSON string, and for efficiency uses appendString directly
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
-				appender.write( javaType.unwrap( value, byte[].class, options ) );
+				appender.write( javaType.unwrap( (T)value, byte[].class, options ) );
 				appender.append( StringJsonDocumentMarker.QUOTE.getMarkerCharacter() );
 				break;
 			case SqlTypes.ARRAY:

From 288683e832d29246ac81b805a5b8f971983f0bc6 Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Sat, 14 Jun 2025 00:00:18 +0200
Subject: [PATCH 80/81] HHH-17404 fix merge regresssion about struct support

---
 .../src/main/java/org/hibernate/dialect/OracleDialect.java   | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index f6f686726c35..0ed224331224 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -965,6 +965,11 @@ public boolean supportsBitType() {
 		return false;
 	}
 
+	@Override
+	public boolean supportsUserDefinedTypes() {
+		return true;
+	}
+
 	@Override
 	public String getArrayTypeName(String javaElementTypeName, String elementTypeName, Integer maxLength) {
 		return ( javaElementTypeName == null ? elementTypeName : javaElementTypeName ) + "Array";

From b2e77304fcb9d5fffeba60818eec4bfd332d049f Mon Sep 17 00:00:00 2001
From: Emmanuel JANNETTI <emmanuel.jannetti@gmail.com>
Date: Mon, 16 Jun 2025 14:09:35 +0200
Subject: [PATCH 81/81] HHH-17404 fix regression on OracleDialect on timeouts

---
 .../org/hibernate/dialect/OracleDialect.java  | 40 +++++++++++++++----
 .../locktimeout/OracleLockTimeoutTest.java    |  3 +-
 2 files changed, 34 insertions(+), 9 deletions(-)

diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
index 0ed224331224..5afbee744c52 100644
--- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
+++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java
@@ -6,8 +6,11 @@
 
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.TemporalType;
+import jakarta.persistence.Timeout;
 import org.hibernate.Length;
 import org.hibernate.QueryTimeoutException;
+import org.hibernate.Timeouts;
+import org.hibernate.QueryTimeoutException;
 import org.hibernate.boot.model.FunctionContributions;
 import org.hibernate.boot.model.TypeContributions;
 import org.hibernate.dialect.aggregate.AggregateSupport;
@@ -117,9 +120,6 @@
 import org.jboss.logging.Logger;
 
 import static java.util.regex.Pattern.CASE_INSENSITIVE;
-import static org.hibernate.LockOptions.NO_WAIT;
-import static org.hibernate.LockOptions.SKIP_LOCKED;
-import static org.hibernate.LockOptions.WAIT_FOREVER;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_USE_BINARY_FLOATS;
 import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
 import static org.hibernate.dialect.type.OracleJdbcHelper.getArrayJdbcTypeConstructor;
@@ -1470,7 +1470,7 @@ public RowLockStrategy getWriteRowLockStrategy() {
 	}
 
 	@Override
-	public String getForUpdateNowaitString() {
+	public String getForUpdateNowaitString(){
 		return " for update nowait";
 	}
 
@@ -1494,12 +1494,36 @@ public String getForUpdateSkipLockedString(String aliases) {
 		return " for update of " + aliases + " skip locked";
 	}
 
+	private String withTimeout(String lockString, Timeout timeout) {
+		return withTimeout( lockString, timeout.milliseconds() );
+	}
+
+	@Override
+	public String getWriteLockString(Timeout timeout) {
+		return withTimeout( getForUpdateString(), timeout );
+	}
+
+	@Override
+	public String getWriteLockString(String aliases, Timeout timeout) {
+		return withTimeout( getForUpdateString(aliases), timeout );
+	}
+
+	@Override
+	public String getReadLockString(Timeout timeout) {
+		return getWriteLockString( timeout );
+	}
+
+	@Override
+	public String getReadLockString(String aliases, Timeout timeout) {
+		return getWriteLockString( aliases, timeout );
+	}
+
 	private String withTimeout(String lockString, int timeout) {
 		return switch (timeout) {
-			case NO_WAIT -> supportsNoWait() ? lockString + " nowait" : lockString;
-			case SKIP_LOCKED -> supportsSkipLocked() ? lockString + " skip locked" : lockString;
-			case WAIT_FOREVER -> lockString;
-			default -> supportsWait() ? lockString + " wait " + getTimeoutInSeconds( timeout ) : lockString;
+			case Timeouts.NO_WAIT_MILLI -> supportsNoWait() ? lockString + " nowait" : lockString;
+			case Timeouts.SKIP_LOCKED_MILLI -> supportsSkipLocked() ? lockString + " skip locked" : lockString;
+			case Timeouts.WAIT_FOREVER_MILLI -> lockString;
+			default -> supportsWait() ? lockString + " wait " + Timeouts.getTimeoutInSeconds( timeout ) : lockString;
 		};
 	}
 
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java
index 9b6bbc2e7646..10db83e4c285 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/locktimeout/OracleLockTimeoutTest.java
@@ -9,7 +9,7 @@
 import org.hibernate.dialect.DatabaseVersion;
 import org.hibernate.dialect.Dialect;
 import org.hibernate.dialect.OracleDialect;
-
+import org.hibernate.testing.RequiresDialect;
 import org.hibernate.testing.junit4.BaseUnitTestCase;
 import org.junit.Test;
 
@@ -18,6 +18,7 @@
 /**
  * @author Vlad Mihalcea
  */
+@RequiresDialect(OracleDialect.class)
 public class OracleLockTimeoutTest extends BaseUnitTestCase {
 
 	private final Dialect dialect = new OracleDialect( DatabaseVersion.make( 12 ) );