Skip to content

Commit f18fa56

Browse files
ejannettbeikov
authored andcommitted
HHH-17404 add compatibility test
1 parent 7acb2d2 commit f18fa56

File tree

5 files changed

+161
-55
lines changed

5 files changed

+161
-55
lines changed

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
import static org.hibernate.cfg.CacheSettings.JPA_SHARED_CACHE_RETRIEVE_MODE;
9595
import static org.hibernate.cfg.CacheSettings.JPA_SHARED_CACHE_STORE_MODE;
9696
import static org.hibernate.cfg.CacheSettings.QUERY_CACHE_LAYOUT;
97+
import static org.hibernate.cfg.DialectSpecificSettings.ORACLE_OSON_DISABLED;
9798
import static org.hibernate.cfg.PersistenceSettings.UNOWNED_ASSOCIATION_TRANSIENT_CHECK;
9899
import static org.hibernate.cfg.QuerySettings.DEFAULT_NULL_ORDERING;
99100
import static org.hibernate.cfg.QuerySettings.JSON_FUNCTIONS_ENABLED;
@@ -132,6 +133,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
132133

133134
private final String uuid = LocalObjectUuidHelper.generateLocalObjectUuid();
134135
private final StandardServiceRegistry serviceRegistry;
136+
private static boolean osonExtensionEnabled;
135137

136138
// integration
137139
private Object beanManagerReference;
@@ -305,10 +307,13 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
305307
settings.get( AvailableSettings.JAKARTA_VALIDATION_FACTORY )
306308
);
307309

310+
osonExtensionEnabled = !getBoolean( ORACLE_OSON_DISABLED ,settings);
311+
308312
jsonFormatMapper = determineJsonFormatMapper(
309313
settings.get( AvailableSettings.JSON_FORMAT_MAPPER ),
310314
strategySelector
311315
);
316+
312317
xmlFormatMapper = determineXmlFormatMapper(
313318
settings.get( AvailableSettings.XML_FORMAT_MAPPER ),
314319
strategySelector,
@@ -795,7 +800,7 @@ private static FormatMapper determineJsonFormatMapper(Object setting, StrategySe
795800
setting,
796801
(Callable<FormatMapper>) () -> {
797802
// Prefer the OSON Jackson FormatMapper by default if available
798-
final FormatMapper jsonJacksonFormatMapper = JacksonIntegration.isJacksonOsonExtensionAvailable()
803+
final FormatMapper jsonJacksonFormatMapper = (osonExtensionEnabled && JacksonIntegration.isJacksonOsonExtensionAvailable())
799804
? getOsonJacksonFormatMapperOrNull()
800805
: getJsonJacksonFormatMapperOrNull();
801806
return jsonJacksonFormatMapper != null ? jsonJacksonFormatMapper : getJakartaJsonBFormatMapperOrNull();

hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ private static void addJsonFormatMappers(StrategySelectorImpl strategySelector)
313313
JacksonJsonFormatMapper.SHORT_NAME,
314314
JacksonJsonFormatMapper.class
315315
);
316-
if ( JacksonIntegration.isJacksonOsonExtensionAvailable() ) {
316+
if (JacksonIntegration.isJacksonOsonExtensionAvailable() ) {
317317
strategySelector.registerStrategyImplementor(
318318
FormatMapper.class,
319319
JacksonOsonFormatMapper.SHORT_NAME,

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

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.sql.PreparedStatement;
3232
import java.sql.ResultSet;
3333
import java.sql.SQLException;
34+
import java.sql.SQLFeatureNotSupportedException;
3435

3536
/**
3637
*
@@ -169,7 +170,7 @@ private X fromOson(InputStream osonBytes, WrapperOptions options) throws Excepti
169170

170171
private boolean useUtf8(WrapperOptions options) {
171172
return getEmbeddableMappingType() == null
172-
&& !options.getJsonFormatMapper().supportsTargetType( OracleOsonJacksonHelper.READER_CLASS );
173+
&& !options.getJsonFormatMapper().supportsSourceType(OracleOsonJacksonHelper.READER_CLASS );
173174
}
174175

175176
private X doExtraction(OracleJsonDatum datum, WrapperOptions options) throws SQLException {
@@ -196,74 +197,106 @@ private X fromString(byte[] json, WrapperOptions options) throws SQLException {
196197
);
197198
}
198199

200+
private byte[] getBytesFromResultSetByIndex(ResultSet rs, int index) throws SQLException {
201+
// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
202+
// and getString is not supported on BLOB. W have to try both
203+
try {
204+
return rs.getBytes( index);
205+
} catch (SQLFeatureNotSupportedException nse) {
206+
return rs.getString( index).getBytes();
207+
}
208+
}
209+
210+
private byte[] getBytesFromStatementByIndex(CallableStatement st, int index) throws SQLException {
211+
// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
212+
// and getString is not supported on BLOB. W have to try both
213+
try {
214+
return st.getBytes( index);
215+
} catch (SQLFeatureNotSupportedException nse) {
216+
217+
return st.getString( index).getBytes();
218+
}
219+
}
220+
private byte[] getBytesFromStatementByName(CallableStatement st, String columnName) throws SQLException {
221+
// This can be a BLOB or a CLOB. getBytes is not supported on CLOB
222+
// and getString is not supported on BLOB. W have to try both
223+
try {
224+
return st.getBytes( columnName);
225+
} catch (SQLFeatureNotSupportedException nse) {
226+
return st.getString( columnName).getBytes();
227+
}
228+
}
229+
199230
@Override
200231
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
201-
try {
202232
if ( useUtf8( options ) ) {
203-
return fromString( rs.getBytes( paramIndex ), options );
233+
return fromString(getBytesFromResultSetByIndex(rs, paramIndex), options );
204234
}
205235
else {
236+
try {
206237
OracleJsonDatum ojd = rs.getObject( paramIndex, OracleJsonDatum.class );
207238
return doExtraction( ojd, options );
239+
} catch (SQLException exc) {
240+
if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
241+
// This may happen if we are fetching data from an existing schema
242+
// that uses BLOB for JSON column In that case we assume bytes are
243+
// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
244+
LOG.invalidJSONColumnType( OracleType.BLOB.getName(), OracleType.JSON.getName() );
245+
return fromString(getBytesFromResultSetByIndex(rs, paramIndex), options );
246+
} else {
247+
throw exc;
248+
}
249+
}
208250
}
209-
} catch (SQLException exc) {
210-
if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
211-
// This may happen if we are fetching data from an existing schema
212-
// that uses BLOB for JSON column In that case we assume bytes are
213-
// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
214-
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
215-
return fromString( rs.getBytes( paramIndex ), options );
216-
} else {
217-
throw exc;
218-
}
219-
}
220251
}
221252

222253
@Override
223254
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
224-
try {
225255
if ( useUtf8( options ) ) {
226-
return fromString( statement.getBytes( index ), options );
256+
return fromString(getBytesFromStatementByIndex(statement, index), options);
227257
}
228258
else {
259+
try {
229260
OracleJsonDatum ojd = statement.getObject( index, OracleJsonDatum.class );
230261
return doExtraction( ojd, options );
262+
} catch (SQLException exc) {
263+
if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
264+
// This may happen if we are fetching data from an existing schema
265+
// that uses BLOB for JSON column In that case we assume bytes are
266+
// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
267+
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
268+
return fromString(getBytesFromStatementByIndex(statement, index), options);
269+
} else {
270+
throw exc;
271+
}
272+
}
231273
}
232-
} catch (SQLException exc) {
233-
if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
234-
// This may happen if we are fetching data from an existing schema
235-
// that uses BLOB for JSON column In that case we assume bytes are
236-
// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
237-
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
238-
return fromString( statement.getBytes( index ), options);
239-
} else {
240-
throw exc;
241-
}
242-
}
243274
}
244275

245276
@Override
246277
protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
247278
throws SQLException {
248-
try {
279+
249280
if ( useUtf8( options ) ) {
250-
return fromString( statement.getBytes( name ), options );
281+
return fromString(getBytesFromStatementByName(statement, name), options);
251282
}
252283
else {
284+
try {
253285
OracleJsonDatum ojd = statement.getObject( name, OracleJsonDatum.class );
254286
return doExtraction( ojd, options );
287+
} catch (SQLException exc) {
288+
if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
289+
// This may happen if we are fetching data from an existing schema
290+
// that uses BLOB for JSON column In that case we assume bytes are
291+
// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
292+
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
293+
return fromString(getBytesFromStatementByName(statement, name), options);
294+
} else {
295+
throw exc;
296+
}
297+
}
255298
}
256-
} catch (SQLException exc) {
257-
if ( exc.getErrorCode() == DatabaseError.JDBC_ERROR_BASE + DatabaseError.EOJ_INVALID_COLUMN_TYPE) {
258-
// This may happen if we are fetching data from an existing schema
259-
// that uses BLOB for JSON column In that case we assume bytes are
260-
// UTF-8 bytes (i.e not OSON) and we fall back to previous String-based implementation
261-
LOG.invalidJSONColumnType( OracleType.CLOB.getName(), OracleType.JSON.getName() );
262-
return fromString( statement.getBytes( name ), options);
263-
} else {
264-
throw exc;
265-
}
266-
}
299+
267300

268301
}
269302

hibernate-core/src/main/java/org/hibernate/dialect/type/OracleJdbcHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static boolean isUsable(ServiceRegistry serviceRegistry) {
3434
public static boolean isOsonAvailable(ServiceRegistry serviceRegistry) {
3535
final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
3636
try {
37-
classLoaderService.classForName( "oracle.sql.json.OracleJsonFactory" );
37+
classLoaderService.classForName( "oracle.jdbc.provider.oson.OsonFactory" );
3838
return true;
3939
}
4040
catch (ClassLoadingException ex) {

hibernate-core/src/test/java/org/hibernate/orm/test/mapping/hhh17404/OracleOsonCompatibilityTest.java

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
*/
55
package org.hibernate.orm.test.mapping.hhh17404;
66

7+
import jakarta.persistence.Access;
8+
import jakarta.persistence.AccessType;
9+
import jakarta.persistence.Embeddable;
710
import jakarta.persistence.Entity;
811
import jakarta.persistence.Id;
912
import jakarta.persistence.Table;
@@ -22,6 +25,11 @@
2225
import org.junit.jupiter.api.BeforeEach;
2326
import org.junit.jupiter.api.Test;
2427

28+
import java.time.LocalDate;
29+
import java.time.LocalDateTime;
30+
import java.time.LocalTime;
31+
import java.util.UUID;
32+
2533
import static org.hamcrest.MatcherAssert.assertThat;
2634
import static org.hamcrest.Matchers.is;
2735
import static org.hibernate.testing.orm.junit.DialectContext.getDialect;
@@ -67,16 +75,37 @@ public OracleOsonCompatibilityTest(String jsonType) {
6775
public void setup(SessionFactoryScope scope) {
6876
scope.inTransaction(
6977
(session) -> {
70-
// force creation of a BLOB column type by creating the table ourselves
78+
// force creation of a column type by creating the table ourselves
7179
session.createNativeQuery( session.getDialect().getDropTableString( "TEST_OSON_COMPAT" ) )
7280
.executeUpdate();
73-
session.createNativeQuery( "CREATE TABLE TEST_OSON_COMPAT (id NUMBER, jsonName " + jsonType + " CHECK (jsonName is json) ,primary key (id))" )
74-
.executeUpdate();
81+
StringBuilder create = new StringBuilder();
82+
create.append("CREATE TABLE TEST_OSON_COMPAT (");
83+
create.append( "id NUMBER").append(',');
84+
create.append( "payload ").append(jsonType).append(" CHECK (payload is null or json)").append(',');
85+
create.append( "primary key (id))");
86+
session.createNativeQuery(create.toString()).executeUpdate();
87+
88+
String insert = "INSERT INTO TEST_OSON_COMPAT (id, payload) VALUES(:id,:json)";
7589

76-
String insert = "INSERT INTO TEST_OSON_COMPAT (id, jsonName) VALUES(:id,:json)";
77-
String jsonstr = "{\"string\":\"john\"}";
78-
session.createNativeQuery(insert).setParameter("id",1)
79-
.setParameter( "json", jsonstr).executeUpdate();
90+
LocalDateTime theLocalDateTime = LocalDateTime.of( 2000, 1, 1, 0, 0, 0 );
91+
LocalDate theLocalDate = LocalDate.of( 2000, 1, 1 );
92+
LocalTime theLocalTime = LocalTime.of( 1, 0, 0 );
93+
UUID uuid = UUID.fromString("53886a8a-7082-4879-b430-25cb94415be8");
94+
String theString = "john";
95+
96+
StringBuilder j = new StringBuilder();
97+
j.append( "{" );
98+
j.append( "\"jsonString\":\"").append(theString).append("\"," );
99+
j.append( "\"theUuid\":\"").append(uuid).append("\"," );
100+
j.append( "\"theLocalDateTime\":\"").append(theLocalDateTime).append("\"," );
101+
j.append( "\"theLocalDate\":\"").append(theLocalDate).append("\"," );
102+
j.append( "\"theLocalTime\":\"").append(theLocalTime).append("\"" );
103+
j.append( "}" );
104+
105+
session.createNativeQuery(insert)
106+
.setParameter("id",1)
107+
.setParameter( "json", j.toString())
108+
.executeUpdate();
80109
}
81110
);
82111
}
@@ -95,7 +124,8 @@ public void verifyReadWorks(SessionFactoryScope scope) {
95124
scope.inTransaction(
96125
(session) -> {
97126
JsonEntity entity = session.find( OracleOsonCompatibilityTest.JsonEntity.class, 1 );
98-
assertThat( entity.jsonName.getString(), is( "john" ) );
127+
assertThat( entity.payload.jsonString.getString(), is( "john" ) );
128+
assertThat( entity.payload.theUuid, is( "53886a8a-7082-4879-b430-25cb94415be8" ) );
99129
}
100130
);
101131
}
@@ -105,15 +135,53 @@ public void verifyReadWorks(SessionFactoryScope scope) {
105135
public static class JsonEntity {
106136
@Id
107137
private Integer id;
138+
108139
@JdbcTypeCode( SqlTypes.JSON )
109-
private JsonMappingTests.StringNode jsonName;
140+
private JsonEntityPayload payload;
110141

111142
public JsonEntity() {
112-
super();
113143
}
114-
public JsonEntity(Integer id, JsonMappingTests.StringNode node) {
144+
145+
public JsonEntity(Integer id, JsonEntityPayload payload) {
115146
this.id = id;
116-
this.jsonName = node;
147+
this.payload = payload;
148+
}
149+
150+
public Integer getId() {
151+
return id;
152+
}
153+
154+
public void setId(Integer id) {
155+
this.id = id;
156+
}
157+
158+
public JsonEntityPayload getPayload() {
159+
return payload;
160+
}
161+
162+
public void setPayload(JsonEntityPayload payload) {
163+
this.payload = payload;
164+
}
165+
166+
}
167+
@Embeddable
168+
@Access( AccessType.PROPERTY )
169+
public static class JsonEntityPayload {
170+
private JsonMappingTests.StringNode jsonString;
171+
private UUID theUuid;
172+
private LocalDateTime theLocalDateTime;
173+
private LocalDate theLocalDate;
174+
private LocalTime theLocalTime;
175+
176+
public JsonEntityPayload() {
177+
178+
}
179+
public JsonEntityPayload(JsonMappingTests.StringNode jsonString, UUID theUuid, LocalDateTime theLocalDateTime, LocalDate theLocalDate, LocalTime theLocalTime) {
180+
this.jsonString = jsonString;
181+
this.theUuid = theUuid;
182+
this.theLocalDateTime = theLocalDateTime;
183+
this.theLocalDate = theLocalDate;
184+
this.theLocalTime = theLocalTime;
117185
}
118186
}
119187
}

0 commit comments

Comments
 (0)