Skip to content

Commit 1975563

Browse files
committed
HHH-17404 : refactor StringJsonDocumentReader to remove usage of CharBuffer
1 parent 22c6d1e commit 1975563

20 files changed

+330
-304
lines changed

hibernate-core/src/main/java/org/hibernate/cfg/DialectSpecificSettings.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ public interface DialectSpecificSettings {
4343
*/
4444
String ORACLE_APPLICATION_CONTINUITY = "hibernate.dialect.oracle.application_continuity";
4545

46+
/**
47+
* Specifies whether usage of the Oracle JSON binary format (aka OSON) should be disabled.
48+
* <p>
49+
* Starting to 21c, if the ojdbc-provider-jackson-oson extension is available. JSON data in an oracle
50+
* database are stored using the OSON binary format. This setting can be used to fallback to the old implementation
51+
* based on String serialization.
52+
*
53+
* @settingDefault {@code false}
54+
*
55+
* @see <a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/adjsn/json-oracle-database.html">Orace OSON format</a>
56+
* @see <a href="https://github.com/oracle/ojdbc-extensions/blob/main/ojdbc-provider-jackson-oson/README.md">Jackson OSON provider</a>
57+
*/
58+
String ORACLE_OSON_DISABLED = "hibernate.dialect.oracle.oson_format_disabled";
59+
4660
/**
4761
* Specifies whether the {@code ansinull} setting is enabled on Sybase.
4862
* <p>

hibernate-core/src/main/java/org/hibernate/dialect/JsonHelper.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838
import org.hibernate.type.descriptor.jdbc.JdbcType;
3939
import org.hibernate.type.format.JsonDocumentItemType;
4040
import org.hibernate.type.format.JsonDocumentReader;
41-
import org.hibernate.type.format.JsonDocumentReaderFactory;
4241
import org.hibernate.type.format.JsonDocumentWriter;
4342
import org.hibernate.type.format.JsonValueJDBCTypeAdapter;
4443
import org.hibernate.type.format.JsonValueJDBCTypeAdapterFactory;
44+
import org.hibernate.type.format.StringJsonDocumentReader;
4545

4646
import static org.hibernate.dialect.StructHelper.getEmbeddedPart;
4747
import static org.hibernate.dialect.StructHelper.instantiate;
@@ -396,7 +396,7 @@ private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, Embedda
396396
/**
397397
* Deserialize a JSON value to Java Object
398398
* @param embeddableMappingType the mapping type
399-
* @param source the JSON value
399+
* @param reader the JSON reader
400400
* @param returnEmbeddable do we return an Embeddable object or array of Objects
401401
* @param options wrappping options
402402
* @return the deserialized value
@@ -405,14 +405,10 @@ private static <X> X consumeJsonDocumentItems(JsonDocumentReader reader, Embedda
405405
*/
406406
public static <X> X deserialize(
407407
EmbeddableMappingType embeddableMappingType,
408-
Object source,
408+
JsonDocumentReader reader,
409409
boolean returnEmbeddable,
410410
WrapperOptions options) throws SQLException {
411411

412-
if ( source == null ) {
413-
return null;
414-
}
415-
JsonDocumentReader reader = JsonDocumentReaderFactory.getJsonDocumentReader(source);
416412

417413
final Object[] values = consumeJsonDocumentItems(reader, embeddableMappingType, returnEmbeddable, options);
418414
if ( returnEmbeddable ) {
@@ -449,7 +445,7 @@ public static <X> X arrayFromString(
449445
else {
450446
jdbcJavaType = options.getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor( preferredJavaTypeClass );
451447
}
452-
JsonDocumentReader reader = JsonDocumentReaderFactory.getJsonDocumentReader(string);
448+
JsonDocumentReader reader = new StringJsonDocumentReader(string);
453449
JsonValueJDBCTypeAdapter adapter = JsonValueJDBCTypeAdapterFactory.getAdapter(reader,false);
454450

455451
assert reader.hasNext():"Invalid array string";
@@ -474,7 +470,7 @@ public static <X> X arrayFromString(
474470
arrayList.add( adapter.fromValue(jdbcJavaType, elementJdbcType ,reader, options) );
475471
break;
476472
default:
477-
assert false : "Unexpected type " + type;
473+
throw new UnsupportedOperationException( "Unexpected JSON type " + type );
478474
}
479475
}
480476

hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ protected void applyAggregateColumnCheck(StringBuilder buf, AggregateColumn aggr
209209
// Is the database accessed using a database service protected by Application Continuity.
210210
protected final boolean applicationContinuity;
211211

212+
// Is the database OSON format should be disabled.
213+
protected final boolean isOracleOsonDisabled;
214+
212215
protected final int driverMajorVersion;
213216

214217
protected final int driverMinorVersion;
@@ -223,6 +226,7 @@ public OracleDialect(DatabaseVersion version) {
223226
autonomous = false;
224227
extended = false;
225228
applicationContinuity = false;
229+
isOracleOsonDisabled = false;
226230
driverMajorVersion = 19;
227231
driverMinorVersion = 0;
228232
}
@@ -236,6 +240,7 @@ public OracleDialect(DialectResolutionInfo info, OracleServerConfiguration serve
236240
autonomous = serverConfiguration.isAutonomous();
237241
extended = serverConfiguration.isExtended();
238242
applicationContinuity = serverConfiguration.isApplicationContinuity();
243+
isOracleOsonDisabled = serverConfiguration.isOSONEnabled();
239244
this.driverMinorVersion = serverConfiguration.getDriverMinorVersion();
240245
this.driverMajorVersion = serverConfiguration.getDriverMajorVersion();
241246
}
@@ -252,6 +257,8 @@ public boolean isApplicationContinuity() {
252257
return applicationContinuity;
253258
}
254259

260+
public boolean isOracleOsonDisabled() {return isOracleOsonDisabled;}
261+
255262
@Override
256263
protected DatabaseVersion getMinimumSupportedVersion() {
257264
return MINIMUM_VERSION;
@@ -993,7 +1000,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
9931000

9941001
if ( getVersion().isSameOrAfter( 21 ) ) {
9951002

996-
if ( JacksonIntegration.isOracleOsonExtensionAvailable() && JACKSON_MAPPER_NAME.equalsIgnoreCase( mapperName )) {
1003+
if ( !isOracleOsonDisabled() && JacksonIntegration.isOracleOsonExtensionAvailable() && JACKSON_MAPPER_NAME.equalsIgnoreCase( mapperName )) {
9971004
// We must check that that extension is available and actually used.
9981005
typeContributions.contributeJdbcType( OracleOsonJacksonJdbcType.INSTANCE );
9991006
typeContributions.contributeJdbcTypeConstructor( OracleOsonArrayJdbcTypeConstructor.INSTANCE );

hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonArrayJdbcType.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@
4545
public class OracleOsonJacksonArrayJdbcType extends OracleJsonArrayJdbcType {
4646

4747

48-
private static final Class osonFactoryKlass;
49-
48+
private static final Object osonFactory;
5049
static {
5150
try {
52-
osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
51+
Class osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
52+
osonFactory = osonFactoryKlass.getDeclaredConstructor().newInstance();
5353
}
54-
catch (ClassNotFoundException | LinkageError e) {
55-
// should not happen as OracleOsonJacksonArrayJdbcType is loaded
56-
// only when an Oracle OSON JDBC extension is present
54+
catch (Exception | LinkageError e) {
55+
// should not happen as OracleOsonJacksonJdbcType is loaded
56+
// only when Oracle OSON JDBC extension is present
5757
// see OracleDialect class.
58-
throw new ExceptionInInitializerError( "OracleOsonJacksonArrayJdbcType class loaded without OSON extension: " + e.getClass()+ " " + e.getMessage());
58+
throw new ExceptionInInitializerError( "OracleOsonJacksonJdbcType class loaded without OSON extension: " + e.getClass()+" "+ e.getMessage());
5959
}
6060
}
6161

@@ -134,8 +134,7 @@ public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
134134

135135
private X fromOson(InputStream osonBytes, WrapperOptions options) throws Exception {
136136
FormatMapper mapper = options.getJsonFormatMapper();
137-
JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
138-
JsonParser osonParser = osonFactory.createParser( osonBytes );
137+
JsonParser osonParser = ((JsonFactory)osonFactory).createParser( osonBytes );
139138
return mapper.readFromSource( getJavaType(), osonParser, options);
140139
}
141140

hibernate-core/src/main/java/org/hibernate/dialect/OracleOsonJacksonJdbcType.java

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
import com.fasterxml.jackson.core.JsonGenerator;
99
import com.fasterxml.jackson.core.JsonParser;
1010
import oracle.jdbc.OracleType;
11+
import oracle.jdbc.driver.DatabaseError;
1112
import oracle.sql.json.OracleJsonDatum;
1213
import oracle.sql.json.OracleJsonFactory;
1314
import oracle.sql.json.OracleJsonGenerator;
1415
import oracle.sql.json.OracleJsonParser;
16+
import org.hibernate.internal.CoreLogging;
17+
import org.hibernate.internal.CoreMessageLogger;
1518
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
1619
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
1720
import org.hibernate.type.descriptor.ValueBinder;
@@ -22,9 +25,11 @@
2225
import org.hibernate.type.descriptor.jdbc.BasicBinder;
2326
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
2427
import org.hibernate.type.format.FormatMapper;
28+
import org.hibernate.type.format.OsonDocumentReader;
2529
import org.hibernate.type.format.OsonDocumentWriter;
2630
import org.hibernate.type.format.jackson.JacksonOsonFormatMapper;
2731

32+
import java.io.ByteArrayInputStream;
2833
import java.io.ByteArrayOutputStream;
2934
import java.io.InputStream;
3035
import java.sql.CallableStatement;
@@ -43,12 +48,15 @@
4348
public class OracleOsonJacksonJdbcType extends OracleJsonJdbcType {
4449
public static final OracleOsonJacksonJdbcType INSTANCE = new OracleOsonJacksonJdbcType( null );
4550

46-
private static final Class osonFactoryKlass;
51+
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( OracleOsonJacksonJdbcType.class );
52+
53+
private static final Object osonFactory;
4754
static {
4855
try {
49-
osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
56+
Class osonFactoryKlass = JacksonOsonFormatMapper.class.getClassLoader().loadClass( "oracle.jdbc.provider.oson.OsonFactory" );
57+
osonFactory = osonFactoryKlass.getDeclaredConstructor().newInstance();
5058
}
51-
catch (ClassNotFoundException | LinkageError e) {
59+
catch (Exception | LinkageError e) {
5260
// should not happen as OracleOsonJacksonJdbcType is loaded
5361
// only when Oracle OSON JDBC extension is present
5462
// see OracleDialect class.
@@ -98,8 +106,7 @@ private <X> byte[] toOson(X value, JavaType<X> javaType, WrapperOptions options)
98106
}
99107

100108
ByteArrayOutputStream out = new ByteArrayOutputStream();
101-
JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
102-
try (JsonGenerator osonGen = osonFactory.createGenerator( out )) {
109+
try (JsonGenerator osonGen = ((JsonFactory)osonFactory).createGenerator( out )) {
103110
mapper.writeToTarget( value, javaType, osonGen, options );
104111
}
105112
return out.toByteArray();
@@ -151,7 +158,7 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
151158
OracleJsonParser osonParser = new OracleJsonFactory().createJsonBinaryParser( osonBytes );
152159
Object[] objects = JsonHelper.deserialize(
153160
getEmbeddableMappingType(),
154-
osonParser,
161+
new OsonDocumentReader(osonParser),
155162
javaType.getJavaTypeClass() != Object[].class,
156163
options
157164
);
@@ -164,12 +171,23 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
164171
type = (JavaType<X>) getEmbeddableMappingType().getJavaType();
165172
}
166173

167-
JsonFactory osonFactory = (JsonFactory) osonFactoryKlass.getDeclaredConstructor().newInstance();
168-
try (JsonParser osonParser = osonFactory.createParser( osonBytes )) {
174+
try (JsonParser osonParser = ((JsonFactory)osonFactory).createParser( osonBytes )) {
169175
return mapper.readFromSource( type, osonParser, options );
170176
}
171177
}
172178

179+
private X doExtraction(byte[] bytes, WrapperOptions options) throws SQLException {
180+
if ( bytes == null ) {
181+
return null;
182+
}
183+
184+
try {
185+
return fromOson( new ByteArrayInputStream(bytes) ,options);
186+
}
187+
catch (Exception e) {
188+
throw new SQLException( e );
189+
}
190+
}
173191
private X doExtraction(OracleJsonDatum datum, WrapperOptions options) throws SQLException {
174192
if ( datum == null ) {
175193
return null;
@@ -185,22 +203,55 @@ private X doExtraction(OracleJsonDatum datum, WrapperOptions options) throws SQ
185203

186204
@Override
187205
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
188-
OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
189-
return doExtraction(ojd,options);
190-
206+
try {
207+
OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
208+
return doExtraction(ojd,options);
209+
} catch (SQLException exc) {
210+
if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
211+
// this may happen if we are fetching data from an existing schema
212+
// that use CBLOB for JSON column
213+
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
214+
return doExtraction(rs.getBytes( paramIndex ), options);
215+
} else {
216+
throw exc;
217+
}
218+
}
191219
}
192220

193221
@Override
194222
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
195-
OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
196-
return doExtraction(ojd,options);
223+
try {
224+
OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
225+
return doExtraction(ojd,options);
226+
} catch (SQLException exc) {
227+
if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
228+
// this may happen if we are fetching data from an existing schema
229+
// that use CBLOB for JSON column
230+
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
231+
return doExtraction(statement.getBytes( index ), options);
232+
} else {
233+
throw exc;
234+
}
235+
}
197236
}
198237

199238
@Override
200239
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
201240
throws SQLException {
202-
OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
203-
return doExtraction(ojd,options);
241+
try {
242+
OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
243+
return doExtraction(ojd,options);
244+
} catch (SQLException exc) {
245+
if ( exc.getErrorCode() == DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
246+
// this may happen if we are fetching data from an existing schema
247+
// that use CBLOB for JSON column
248+
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
249+
return doExtraction(statement.getBytes( name ), options);
250+
} else {
251+
throw exc;
252+
}
253+
}
254+
204255
}
205256

206257
};

hibernate-core/src/main/java/org/hibernate/dialect/OracleServerConfiguration.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_APPLICATION_CONTINUITY;
2020
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_AUTONOMOUS_DATABASE;
2121
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_EXTENDED_STRING_SIZE;
22+
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
2223
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
2324

2425
/**
@@ -31,6 +32,7 @@ public class OracleServerConfiguration {
3132
private final boolean autonomous;
3233
private final boolean extended;
3334
private final boolean applicationContinuity;
35+
private final boolean osonDisabled;
3436
private final int driverMajorVersion;
3537
private final int driverMinorVersion;
3638

@@ -46,6 +48,10 @@ public boolean isApplicationContinuity() {
4648
return applicationContinuity;
4749
}
4850

51+
public boolean isOSONEnabled() {
52+
return osonDisabled;
53+
}
54+
4955
public int getDriverMajorVersion() {
5056
return driverMajorVersion;
5157
}
@@ -55,26 +61,28 @@ public int getDriverMinorVersion() {
5561
}
5662

5763
public OracleServerConfiguration(boolean autonomous, boolean extended) {
58-
this( autonomous, extended, false, 19, 0 );
64+
this( autonomous, extended, false, false, 19, 0 );
5965
}
6066

6167
public OracleServerConfiguration(
6268
boolean autonomous,
6369
boolean extended,
6470
int driverMajorVersion,
6571
int driverMinorVersion) {
66-
this( autonomous, extended, false, driverMajorVersion, driverMinorVersion );
72+
this( autonomous, extended, false, false, driverMajorVersion, driverMinorVersion );
6773
}
6874

6975
public OracleServerConfiguration(
7076
boolean autonomous,
7177
boolean extended,
7278
boolean applicationContinuity,
79+
boolean osonDisabled,
7380
int driverMajorVersion,
7481
int driverMinorVersion) {
7582
this.autonomous = autonomous;
7683
this.extended = extended;
7784
this.applicationContinuity = applicationContinuity;
85+
this.osonDisabled = osonDisabled;
7886
this.driverMajorVersion = driverMajorVersion;
7987
this.driverMinorVersion = driverMinorVersion;
8088
}
@@ -86,10 +94,13 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut
8694
final boolean defaultExtended = getBoolean( ORACLE_EXTENDED_STRING_SIZE, configuration, false );
8795
final boolean defaultAutonomous = getBoolean( ORACLE_AUTONOMOUS_DATABASE, configuration, false );
8896
final boolean defaultContinuity = getBoolean( ORACLE_APPLICATION_CONTINUITY, configuration, false );
97+
final boolean defaultOsonDisabled = getBoolean( ORACLE_OSON_DISABLED , configuration, false );
8998

9099
boolean extended;
91100
boolean autonomous;
92101
boolean applicationContinuity;
102+
boolean osonDisabled = defaultOsonDisabled;
103+
93104
int majorVersion;
94105
int minorVersion;
95106
final DatabaseMetaData databaseMetaData = info.getDatabaseMetadata();
@@ -125,7 +136,7 @@ public static OracleServerConfiguration fromDialectResolutionInfo(DialectResolut
125136
}
126137
}
127138

128-
return new OracleServerConfiguration( autonomous, extended, applicationContinuity, majorVersion, minorVersion );
139+
return new OracleServerConfiguration( autonomous, extended, applicationContinuity, osonDisabled,majorVersion, minorVersion );
129140
}
130141

131142
private static boolean isExtended(Statement statement) {

0 commit comments

Comments
 (0)