HHH-18806 fix nationalized strings on Sybase jTDS

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-11-01 20:26:35 +01:00
parent 1c87d73d2e
commit 1ef22c01f3
6 changed files with 48 additions and 37 deletions

View File

@ -15,58 +15,57 @@
* @see Dialect#getNationalizationSupport()
*/
public enum NationalizationSupport {
/**
* The {@code CHAR}, {@code VARCHAR}, and {@code CLOB}
* types inherently handle nationalized character data.
* Usually the database will not even define dedicated
* nationalized data types like {@code NVARCHAR}.
*/
IMPLICIT( Types.CHAR, Types.VARCHAR, Types.LONGVARCHAR, Types.CLOB ),
IMPLICIT,
/**
* The database does define and support distinct SQL types
* for representing nationalized character data, typically
* named {@code NCHAR}, {@code NVARCHAR}, and {@code NCLOB}.
*/
EXPLICIT( Types.NCHAR, Types.NVARCHAR, Types.LONGNVARCHAR, Types.NCLOB ),
EXPLICIT,
/**
* The database does not even have support for nationalized
* character data.
*/
UNSUPPORTED;
private final int charVariantCode;
private final int varcharVariantCode;
private final int longVarcharVariantCode;
private final int clobVariantCode;
NationalizationSupport() {
this( -1, -1, -1, -1 );
}
NationalizationSupport(
int charVariantCode,
int varcharVariantCode,
int longVarcharVariantCode,
int clobVariantCode) {
this.charVariantCode = charVariantCode;
this.varcharVariantCode = varcharVariantCode;
this.longVarcharVariantCode = longVarcharVariantCode;
this.clobVariantCode = clobVariantCode;
}
public int getCharVariantCode() {
return charVariantCode;
return switch ( this ) {
case IMPLICIT -> Types.CHAR;
case EXPLICIT -> Types.NCHAR;
case UNSUPPORTED -> throw new UnsupportedOperationException("Nationalized character data not supported on this database");
};
}
public int getVarcharVariantCode() {
return varcharVariantCode;
return switch ( this ) {
case IMPLICIT -> Types.VARCHAR;
case EXPLICIT -> Types.NVARCHAR;
case UNSUPPORTED -> throw new UnsupportedOperationException("Nationalized character data not supported on this database");
};
}
public int getLongVarcharVariantCode() {
return longVarcharVariantCode;
return switch ( this ) {
case IMPLICIT -> Types.LONGVARCHAR;
case EXPLICIT -> Types.LONGNVARCHAR;
case UNSUPPORTED -> throw new UnsupportedOperationException("Nationalized character data not supported on this database");
};
}
public int getClobVariantCode() {
return clobVariantCode;
return switch ( this ) {
case IMPLICIT -> Types.CLOB;
case EXPLICIT -> Types.NCLOB;
case UNSUPPORTED -> throw new UnsupportedOperationException("Nationalized character data not supported on this database");
};
}
}

View File

@ -213,10 +213,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
// The jTDS driver doesn't support the JDBC4 signatures using 'long length' for stream bindings
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
// The jTDS driver doesn't support nationalized types
jdbcTypeRegistry.addDescriptor( Types.NCLOB, ClobJdbcType.CLOB_BINDING );
jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, ClobJdbcType.CLOB_BINDING );
}
else {
// jConnect driver only conditionally supports getClob/getNClob depending on a server setting. See
@ -254,7 +251,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
@Override
public NationalizationSupport getNationalizationSupport() {
// At least the jTDS driver doesn't support this
return driverKind == SybaseDriverKind.JTDS ? NationalizationSupport.IMPLICIT : super.getNationalizationSupport();
return super.getNationalizationSupport();
}
@Override

View File

@ -29,7 +29,6 @@
import org.hibernate.query.sqm.tree.expression.SqmXmlTableFunction;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.Template;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.internal.ColumnQualifierCollectorSqlAstWalker;
import org.hibernate.sql.ast.spi.FromClauseAccess;

View File

@ -18,6 +18,8 @@
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Descriptor for {@link String} handling.
*
@ -68,6 +70,9 @@ public <X> X unwrap(String value, Class<X> type, WrapperOptions options) {
if ( String.class.isAssignableFrom( type ) ) {
return (X) value;
}
if ( byte[].class.isAssignableFrom( type ) ) {
return (X) value.getBytes( UTF_8 );
}
if ( Reader.class.isAssignableFrom( type ) ) {
return (X) new StringReader( value );
}
@ -103,6 +108,9 @@ public <X> String wrap(X value, WrapperOptions options) {
if (value instanceof char[] chars) {
return new String( chars );
}
if (value instanceof byte[] bytes) {
return new String( bytes, UTF_8 );
}
if (value instanceof Reader reader) {
return DataHelper.extractString( reader );
}

View File

@ -105,7 +105,13 @@ public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
if ( options.getDialect().supportsNationalizedMethods() ) {
st.setNString( index, javaType.unwrap( value, String.class, options ) );
try {
st.setNString( index, javaType.unwrap( value, String.class, options ) );
}
// workaround for jTDS driver for Sybase
catch ( AbstractMethodError e ) {
st.setBytes( index, javaType.unwrap( value, byte[].class, options ) );
}
}
else {
st.setString( index, javaType.unwrap( value, String.class, options ) );
@ -131,7 +137,13 @@ public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
@Override
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
if ( options.getDialect().supportsNationalizedMethods() ) {
return javaType.wrap( rs.getNString( paramIndex ), options );
try {
return javaType.wrap( rs.getNString( paramIndex ), options );
}
// workaround for jTDS driver for Sybase
catch ( AbstractMethodError e ) {
return javaType.wrap( rs.getBytes( paramIndex ), options );
}
}
else {
return javaType.wrap( rs.getString( paramIndex ), options );

View File

@ -8,10 +8,8 @@
import jakarta.persistence.Id;
import org.hibernate.annotations.Nationalized;
import org.hibernate.dialect.SybaseASEDialect;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
@ -20,8 +18,6 @@
/**
* @author Vlad Mihalcea
*/
@SkipForDialect(dialectClass = SybaseASEDialect.class,
reason = "Error converting characters into server's character set")
public class NationalizedTest extends BaseEntityManagerFunctionalTestCase {
@Override