HHH-15748 Use JSON DDL type on Oracle 21+ and BLOB on 12+
This commit is contained in:
parent
5b5721f64b
commit
276b7a6f95
|
@ -463,6 +463,8 @@ alter database drop logfile group 1;
|
||||||
alter database drop logfile group 2;
|
alter database drop logfile group 2;
|
||||||
alter database drop logfile group 3;
|
alter database drop logfile group 3;
|
||||||
alter system set open_cursors=1000 sid='*' scope=both;
|
alter system set open_cursors=1000 sid='*' scope=both;
|
||||||
|
create user hibernate_orm_test identified by hibernate_orm_test quota unlimited on users;
|
||||||
|
grant all privileges to hibernate_orm_test;
|
||||||
EOF\""
|
EOF\""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,6 +504,8 @@ alter system set open_cursors=1000 sid='*' scope=both;
|
||||||
alter system set processes=150 scope=spfile;
|
alter system set processes=150 scope=spfile;
|
||||||
alter system set filesystemio_options=asynch scope=spfile;
|
alter system set filesystemio_options=asynch scope=spfile;
|
||||||
alter system set disk_asynch_io=true scope=spfile;
|
alter system set disk_asynch_io=true scope=spfile;
|
||||||
|
create user hibernate_orm_test identified by hibernate_orm_test quota unlimited on users;
|
||||||
|
grant all privileges to hibernate_orm_test;
|
||||||
EOF\""
|
EOF\""
|
||||||
echo "Waiting for Oracle to restart after configuration..."
|
echo "Waiting for Oracle to restart after configuration..."
|
||||||
$CONTAINER_CLI stop oracle
|
$CONTAINER_CLI stop oracle
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.userguide.mapping.basic;
|
package org.hibernate.userguide.mapping.basic;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.sql.Blob;
|
||||||
|
import java.sql.Clob;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -107,13 +110,33 @@ public abstract class JsonMappingTests {
|
||||||
assertThat( entityWithJson.objectMap, is( objectMap ) );
|
assertThat( entityWithJson.objectMap, is( objectMap ) );
|
||||||
assertThat( entityWithJson.list, is( list ) );
|
assertThat( entityWithJson.list, is( list ) );
|
||||||
assertThat( entityWithJson.jsonString, isOneOf( json, alternativeJson ) );
|
assertThat( entityWithJson.jsonString, isOneOf( json, alternativeJson ) );
|
||||||
String nativeJson = session.createNativeQuery(
|
Object nativeJson = session.createNativeQuery(
|
||||||
"select jsonString from EntityWithJson",
|
"select jsonString from EntityWithJson",
|
||||||
String.class
|
Object.class
|
||||||
)
|
)
|
||||||
.getResultList()
|
.getResultList()
|
||||||
.get( 0 );
|
.get( 0 );
|
||||||
assertThat( nativeJson, isOneOf( json, alternativeJson ) );
|
final String jsonText;
|
||||||
|
try {
|
||||||
|
if ( nativeJson instanceof Blob ) {
|
||||||
|
final Blob blob = (Blob) nativeJson;
|
||||||
|
jsonText = new String(
|
||||||
|
blob.getBytes( 1L, (int) blob.length() ),
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if ( nativeJson instanceof Clob ) {
|
||||||
|
final Clob jsonClob = (Clob) nativeJson;
|
||||||
|
jsonText = jsonClob.getSubString( 1L, (int) jsonClob.length() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jsonText = (String) nativeJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new RuntimeException( e );
|
||||||
|
}
|
||||||
|
assertThat( jsonText, isOneOf( json, alternativeJson ) );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,8 +131,8 @@ ext {
|
||||||
oracle_ci : [
|
oracle_ci : [
|
||||||
'db.dialect' : 'org.hibernate.dialect.OracleDialect',
|
'db.dialect' : 'org.hibernate.dialect.OracleDialect',
|
||||||
'jdbc.driver': 'oracle.jdbc.OracleDriver',
|
'jdbc.driver': 'oracle.jdbc.OracleDriver',
|
||||||
'jdbc.user' : 'SYSTEM',
|
'jdbc.user' : 'hibernate_orm_test',
|
||||||
'jdbc.pass' : 'Oracle18',
|
'jdbc.pass' : 'hibernate_orm_test',
|
||||||
'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE',
|
'jdbc.url' : 'jdbc:oracle:thin:@' + dbHost + ':1521:XE',
|
||||||
'connection.init_sql' : ''
|
'connection.init_sql' : ''
|
||||||
],
|
],
|
||||||
|
|
|
@ -62,6 +62,8 @@ dependencies {
|
||||||
implementation libs.logging
|
implementation libs.logging
|
||||||
|
|
||||||
compileOnly libs.loggingAnnotations
|
compileOnly libs.loggingAnnotations
|
||||||
|
// Used for compiling some Oracle specific JdbcTypes
|
||||||
|
compileOnly dbLibs.oracle
|
||||||
|
|
||||||
// JUnit dependencies made up of:
|
// JUnit dependencies made up of:
|
||||||
// * JUnit 5
|
// * JUnit 5
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.dialect.BooleanDecoder;
|
||||||
import org.hibernate.dialect.DatabaseVersion;
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.dialect.OracleArrayJdbcType;
|
import org.hibernate.dialect.OracleArrayJdbcType;
|
||||||
|
import org.hibernate.dialect.OracleTypes;
|
||||||
import org.hibernate.dialect.OracleTypesHelper;
|
import org.hibernate.dialect.OracleTypesHelper;
|
||||||
import org.hibernate.dialect.OracleXmlJdbcType;
|
import org.hibernate.dialect.OracleXmlJdbcType;
|
||||||
import org.hibernate.dialect.Replacer;
|
import org.hibernate.dialect.Replacer;
|
||||||
|
@ -85,6 +86,7 @@ import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JsonBlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
@ -109,6 +111,7 @@ import static org.hibernate.type.SqlTypes.DATE;
|
||||||
import static org.hibernate.type.SqlTypes.DECIMAL;
|
import static org.hibernate.type.SqlTypes.DECIMAL;
|
||||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
import static org.hibernate.type.SqlTypes.INTEGER;
|
import static org.hibernate.type.SqlTypes.INTEGER;
|
||||||
|
import static org.hibernate.type.SqlTypes.JSON;
|
||||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
import static org.hibernate.type.SqlTypes.REAL;
|
import static org.hibernate.type.SqlTypes.REAL;
|
||||||
|
@ -632,6 +635,12 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "SYS.XMLTYPE", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "SYS.XMLTYPE", this ) );
|
||||||
if ( getVersion().isSameOrAfter( 10 ) ) {
|
if ( getVersion().isSameOrAfter( 10 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
||||||
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
|
}
|
||||||
|
else if ( getVersion().isSameOrAfter( 12 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,6 +678,8 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
int scale,
|
int scale,
|
||||||
JdbcTypeRegistry jdbcTypeRegistry) {
|
JdbcTypeRegistry jdbcTypeRegistry) {
|
||||||
switch ( jdbcTypeCode ) {
|
switch ( jdbcTypeCode ) {
|
||||||
|
case OracleTypes.JSON:
|
||||||
|
return jdbcTypeRegistry.getDescriptor( JSON );
|
||||||
case Types.NUMERIC:
|
case Types.NUMERIC:
|
||||||
if ( scale == -127 ) {
|
if ( scale == -127 ) {
|
||||||
// For some reason, the Oracle JDBC driver reports FLOAT
|
// For some reason, the Oracle JDBC driver reports FLOAT
|
||||||
|
@ -744,6 +755,13 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
BlobJdbcType.DEFAULT;
|
BlobJdbcType.DEFAULT;
|
||||||
|
|
||||||
typeContributions.contributeJdbcType( descriptor );
|
typeContributions.contributeJdbcType( descriptor );
|
||||||
|
|
||||||
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
|
typeContributions.contributeJdbcType( OracleTypesHelper.INSTANCE.getJsonJdbcType() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
typeContributions.contributeJdbcType( JsonBlobJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typeContributions.contributeJdbcType( OracleArrayJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( OracleArrayJdbcType.INSTANCE );
|
||||||
|
|
|
@ -74,6 +74,7 @@ import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JsonBlobJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
@ -608,6 +609,12 @@ public class OracleDialect extends Dialect {
|
||||||
|
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "SYS.XMLTYPE", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "SYS.XMLTYPE", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "MDSYS.SDO_GEOMETRY", this ) );
|
||||||
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
|
}
|
||||||
|
else if ( getVersion().isSameOrAfter( 12 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "blob", this ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -643,6 +650,8 @@ public class OracleDialect extends Dialect {
|
||||||
int scale,
|
int scale,
|
||||||
JdbcTypeRegistry jdbcTypeRegistry) {
|
JdbcTypeRegistry jdbcTypeRegistry) {
|
||||||
switch ( jdbcTypeCode ) {
|
switch ( jdbcTypeCode ) {
|
||||||
|
case OracleTypes.JSON:
|
||||||
|
return jdbcTypeRegistry.getDescriptor( JSON );
|
||||||
case Types.NUMERIC:
|
case Types.NUMERIC:
|
||||||
if ( scale == -127 ) {
|
if ( scale == -127 ) {
|
||||||
// For some reason, the Oracle JDBC driver reports FLOAT
|
// For some reason, the Oracle JDBC driver reports FLOAT
|
||||||
|
@ -718,6 +727,13 @@ public class OracleDialect extends Dialect {
|
||||||
BlobJdbcType.DEFAULT;
|
BlobJdbcType.DEFAULT;
|
||||||
|
|
||||||
typeContributions.contributeJdbcType( descriptor );
|
typeContributions.contributeJdbcType( descriptor );
|
||||||
|
|
||||||
|
if ( getVersion().isSameOrAfter( 21 ) ) {
|
||||||
|
typeContributions.contributeJdbcType( OracleTypesHelper.INSTANCE.getJsonJdbcType() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
typeContributions.contributeJdbcType( JsonBlobJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typeContributions.contributeJdbcType( OracleArrayJdbcType.INSTANCE );
|
typeContributions.contributeJdbcType( OracleArrayJdbcType.INSTANCE );
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
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.JdbcLiteralFormatter;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
|
||||||
|
import oracle.jdbc.OracleType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized type mapping for {@code JSON} that encodes as OSON.
|
||||||
|
* This class is used from {@link OracleTypesHelper} reflectively to avoid loading Oracle JDBC classes eagerly.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class OracleJsonJdbcType implements JdbcType {
|
||||||
|
/**
|
||||||
|
* Singleton access
|
||||||
|
*/
|
||||||
|
public static final OracleJsonJdbcType INSTANCE = new OracleJsonJdbcType();
|
||||||
|
|
||||||
|
private static final int JSON_TYPE_CODE = OracleType.JSON.getVendorTypeNumber();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return SqlTypes.BLOB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "OracleJsonJdbcType";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
// No literal support for now
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final String json = options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().toString(
|
||||||
|
value,
|
||||||
|
getJavaType(),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
st.setObject( index, json, JSON_TYPE_CODE );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final String json = options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().toString(
|
||||||
|
value,
|
||||||
|
getJavaType(),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
st.setObject( name, json, JSON_TYPE_CODE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
return getObject( rs.getString( paramIndex ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
return getObject( statement.getString( index ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
return getObject( statement.getString( name ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
private X getObject(String json, WrapperOptions options) throws SQLException {
|
||||||
|
if ( json == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().fromString(
|
||||||
|
json,
|
||||||
|
getJavaType(),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Oracle specific JDBC type code.
|
||||||
|
*/
|
||||||
|
public class OracleTypes {
|
||||||
|
public static final int JSON = 2016;
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ package org.hibernate.dialect;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.ReflectHelper;
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JsonJdbcType;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
@ -27,8 +29,10 @@ public class OracleTypesHelper {
|
||||||
|
|
||||||
private static final String ORACLE_TYPES_CLASS_NAME = "oracle.jdbc.OracleTypes";
|
private static final String ORACLE_TYPES_CLASS_NAME = "oracle.jdbc.OracleTypes";
|
||||||
private static final String DEPRECATED_ORACLE_TYPES_CLASS_NAME = "oracle.jdbc.driver.OracleTypes";
|
private static final String DEPRECATED_ORACLE_TYPES_CLASS_NAME = "oracle.jdbc.driver.OracleTypes";
|
||||||
|
private static final String ORACLE_JSON_JDBC_TYPE_CLASS_NAME = "org.hibernate.dialect.OracleJsonJdbcType";
|
||||||
|
|
||||||
private final int oracleCursorTypeSqlType;
|
private final int oracleCursorTypeSqlType;
|
||||||
|
private final JdbcType jsonJdbcType;
|
||||||
|
|
||||||
private OracleTypesHelper() {
|
private OracleTypesHelper() {
|
||||||
int typeCode = -99;
|
int typeCode = -99;
|
||||||
|
@ -39,6 +43,17 @@ public class OracleTypesHelper {
|
||||||
log.warn( "Unable to resolve Oracle CURSOR JDBC type code: the class OracleTypesHelper was initialized but the Oracle JDBC driver could not be loaded." );
|
log.warn( "Unable to resolve Oracle CURSOR JDBC type code: the class OracleTypesHelper was initialized but the Oracle JDBC driver could not be loaded." );
|
||||||
}
|
}
|
||||||
oracleCursorTypeSqlType = typeCode;
|
oracleCursorTypeSqlType = typeCode;
|
||||||
|
|
||||||
|
JdbcType jsonJdbcType = JsonJdbcType.INSTANCE;
|
||||||
|
try {
|
||||||
|
jsonJdbcType = (JdbcType) ReflectHelper.classForName( ORACLE_JSON_JDBC_TYPE_CLASS_NAME )
|
||||||
|
.getField( "INSTANCE" )
|
||||||
|
.get( null );
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
log.warn( "Unable to resolve OracleJsonJdbcType: the class OracleTypesHelper was initialized but the Oracle JDBC driver could not be loaded." );
|
||||||
|
}
|
||||||
|
this.jsonJdbcType = jsonJdbcType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int extractOracleCursorTypeValue() {
|
private int extractOracleCursorTypeValue() {
|
||||||
|
@ -75,6 +90,10 @@ public class OracleTypesHelper {
|
||||||
return oracleCursorTypeSqlType;
|
return oracleCursorTypeSqlType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JdbcType getJsonJdbcType() {
|
||||||
|
return jsonJdbcType;
|
||||||
|
}
|
||||||
|
|
||||||
// initial code as copied from Oracle8iDialect
|
// initial code as copied from Oracle8iDialect
|
||||||
//
|
//
|
||||||
// private int oracleCursorTypeSqlType = INIT_ORACLETYPES_CURSOR_VALUE;
|
// private int oracleCursorTypeSqlType = INIT_ORACLETYPES_CURSOR_VALUE;
|
||||||
|
|
|
@ -820,7 +820,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
else if ( getFactory().getMappingMetamodel().isEntityClass(resultClass) ) {
|
else if ( getFactory().getMappingMetamodel().isEntityClass(resultClass) ) {
|
||||||
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
query.addEntity( "alias1", resultClass.getName(), LockMode.READ );
|
||||||
}
|
}
|
||||||
else {
|
else if ( resultClass != Object.class && resultClass != Object[].class ) {
|
||||||
query.addScalar( 1, resultClass );
|
query.addScalar( 1, resultClass );
|
||||||
}
|
}
|
||||||
return query;
|
return query;
|
||||||
|
|
|
@ -191,6 +191,7 @@ public class JdbcTypeJavaClassMappings {
|
||||||
workMap.put( SqlTypes.ROWID, RowId.class );
|
workMap.put( SqlTypes.ROWID, RowId.class );
|
||||||
workMap.put( SqlTypes.SQLXML, SQLXML.class );
|
workMap.put( SqlTypes.SQLXML, SQLXML.class );
|
||||||
workMap.put( SqlTypes.UUID, UUID.class );
|
workMap.put( SqlTypes.UUID, UUID.class );
|
||||||
|
workMap.put( SqlTypes.JSON, String.class );
|
||||||
workMap.put( SqlTypes.INET, InetAddress.class );
|
workMap.put( SqlTypes.INET, InetAddress.class );
|
||||||
workMap.put( SqlTypes.TIMESTAMP_UTC, Instant.class );
|
workMap.put( SqlTypes.TIMESTAMP_UTC, Instant.class );
|
||||||
workMap.put( SqlTypes.INTERVAL_SECOND, Duration.class );
|
workMap.put( SqlTypes.INTERVAL_SECOND, Duration.class );
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.type.descriptor.jdbc;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialized type mapping for {@code JSON} and the BLOB SQL data type.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class JsonBlobJdbcType implements JdbcType {
|
||||||
|
/**
|
||||||
|
* Singleton access
|
||||||
|
*/
|
||||||
|
public static final JsonBlobJdbcType INSTANCE = new JsonBlobJdbcType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return SqlTypes.BLOB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return SqlTypes.JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "JsonBlobJdbcType";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
// No literal support for now
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final String json = options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().toString(
|
||||||
|
value,
|
||||||
|
getJavaType(),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
st.setBytes( index, json.getBytes( StandardCharsets.UTF_8 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
final String json = options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().toString(
|
||||||
|
value,
|
||||||
|
getJavaType(),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
st.setBytes( name, json.getBytes( StandardCharsets.UTF_8 ) );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
return getObject( rs.getBytes( paramIndex ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
return getObject( statement.getBytes( index ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
return getObject( statement.getBytes( name ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
private X getObject(byte[] json, WrapperOptions options) throws SQLException {
|
||||||
|
if ( json == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return options.getSessionFactory().getFastSessionServices().getJsonFormatMapper().fromString(
|
||||||
|
new String( json, StandardCharsets.UTF_8 ),
|
||||||
|
getJavaType(),
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ public final class JacksonJsonFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (T) charSequence.toString();
|
return (T) charSequence.toString();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -45,7 +45,7 @@ public final class JacksonJsonFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (String) value;
|
return (String) value;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -33,7 +33,7 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (T) charSequence.toString();
|
return (T) charSequence.toString();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -46,7 +46,7 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (String) value;
|
return (String) value;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -33,7 +33,7 @@ public final class JsonBJsonFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (T) charSequence.toString();
|
return (T) charSequence.toString();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -46,7 +46,7 @@ public final class JsonBJsonFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (String) value;
|
return (String) value;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -51,7 +51,7 @@ public final class JaxbXmlFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (T) charSequence.toString();
|
return (T) charSequence.toString();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -194,7 +194,7 @@ public final class JaxbXmlFormatMapper implements FormatMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
|
||||||
if ( javaType.getJavaType() == String.class ) {
|
if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
|
||||||
return (String) value;
|
return (String) value;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -16,7 +16,7 @@ earlier versions, see any other pertinent migration guides as well.
|
||||||
|
|
||||||
=== UUID mapping changes on MariaDB
|
=== UUID mapping changes on MariaDB
|
||||||
|
|
||||||
On MariaDB, the type code `SqlType.UUID` now by default refers to the DDL type `uuid`, whereas before it was using `binary(16)`.
|
On MariaDB, the type code `SqlTypes.UUID` now by default refers to the DDL type `uuid`, whereas before it was using `binary(16)`.
|
||||||
Due to this change, schema validation errors could occur on existing databases.
|
Due to this change, schema validation errors could occur on existing databases.
|
||||||
|
|
||||||
The migration to `uuid` requires a migration expression like `cast(old as uuid)`.
|
The migration to `uuid` requires a migration expression like `cast(old as uuid)`.
|
||||||
|
@ -25,13 +25,24 @@ To retain backwards compatibility, configure the setting `hibernate.type.preferr
|
||||||
|
|
||||||
=== UUID mapping changes on SQL Server
|
=== UUID mapping changes on SQL Server
|
||||||
|
|
||||||
On SQL Server, the type code `SqlType.UUID` now by default refers to the DDL type `uniqueidentifier`, whereas before it was using `binary(16)`.
|
On SQL Server, the type code `SqlTypes.UUID` now by default refers to the DDL type `uniqueidentifier`, whereas before it was using `binary(16)`.
|
||||||
Due to this change, schema validation errors could occur on existing databases.
|
Due to this change, schema validation errors could occur on existing databases.
|
||||||
|
|
||||||
The migration to `uuid` requires a migration expression like `cast(old as uuid)`.
|
The migration to `uuid` requires a migration expression like `cast(old as uuid)`.
|
||||||
|
|
||||||
To retain backwards compatibility, configure the setting `hibernate.type.preferred_uuid_jdbc_type` to `BINARY`.
|
To retain backwards compatibility, configure the setting `hibernate.type.preferred_uuid_jdbc_type` to `BINARY`.
|
||||||
|
|
||||||
|
=== JSON mapping changes on Oracle
|
||||||
|
|
||||||
|
On Oracle 12.1+, the type code `SqlTypes.JSON` now by default refers to the DDL type `blob` and on 21+ to `json`, whereas before it was using `clob`.
|
||||||
|
Due to this change, schema validation errors could occur on existing databases.
|
||||||
|
|
||||||
|
The migration to `blob` and `json` requires a migration expression like `cast(old as blob)` and `cast(old as json)` respectively.
|
||||||
|
|
||||||
|
To get the old behavior, annotate the column with `@Column(definition = "clob")`.
|
||||||
|
|
||||||
|
This change was done because `blob` and `json` are way more efficient and because we don't expect wide usage of `SqlTypes.JSON` yet.
|
||||||
|
|
||||||
=== Column type inference for `number(n,0)` in native SQL queries on Oracle
|
=== Column type inference for `number(n,0)` in native SQL queries on Oracle
|
||||||
|
|
||||||
Previously, since Hibernate 6.0, columns of type `number` with scale 0 on Oracle were interpreted as `boolean`, `tinyint`, `smallint`, `int`, or `bigint`,
|
Previously, since Hibernate 6.0, columns of type `number` with scale 0 on Oracle were interpreted as `boolean`, `tinyint`, `smallint`, `int`, or `bigint`,
|
||||||
|
|
Loading…
Reference in New Issue