HHH-17909 Use domain type for NAMED_ENUM
This commit is contained in:
parent
af269ae182
commit
dc82a3c5e3
|
@ -95,6 +95,8 @@ import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
|
||||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
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.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
@ -167,7 +169,7 @@ public class OracleDialect extends Dialect {
|
||||||
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
|
public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
|
||||||
|
|
||||||
private static final String yqmSelect =
|
private static final String yqmSelect =
|
||||||
"(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH') + (least(extract(day from %2$s), extract(day from last_day(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH')))) - 1))";
|
"(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH') + (least(extract(day from %2$s), extract(day from last_day(trunc(%2$s, 'MONTH') + numtoyminterval(%1$s, 'MONTH')))) - 1))";
|
||||||
|
|
||||||
private static final String ADD_YEAR_EXPRESSION = String.format( yqmSelect, "?2*12", "?3" );
|
private static final String ADD_YEAR_EXPRESSION = String.format( yqmSelect, "?2*12", "?3" );
|
||||||
private static final String ADD_QUARTER_EXPRESSION = String.format( yqmSelect, "?2*3", "?3" );
|
private static final String ADD_QUARTER_EXPRESSION = String.format( yqmSelect, "?2*3", "?3" );
|
||||||
|
@ -716,10 +718,10 @@ public class OracleDialect extends Dialect {
|
||||||
switch ( sqlTypeCode ) {
|
switch ( sqlTypeCode ) {
|
||||||
case BOOLEAN:
|
case BOOLEAN:
|
||||||
if ( getVersion().isSameOrAfter( 23 ) ) {
|
if ( getVersion().isSameOrAfter( 23 ) ) {
|
||||||
return super.columnType( sqlTypeCode );
|
return super.columnType( sqlTypeCode );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return "number(1,0)";
|
return "number(1,0)";
|
||||||
}
|
}
|
||||||
case TINYINT:
|
case TINYINT:
|
||||||
return "number(3,0)";
|
return "number(3,0)";
|
||||||
|
@ -746,8 +748,8 @@ public class OracleDialect extends Dialect {
|
||||||
return "date";
|
return "date";
|
||||||
case TIME:
|
case TIME:
|
||||||
return "timestamp($p)";
|
return "timestamp($p)";
|
||||||
// the only difference between date and timestamp
|
// the only difference between date and timestamp
|
||||||
// on Oracle is that date has no fractional seconds
|
// on Oracle is that date has no fractional seconds
|
||||||
case TIME_WITH_TIMEZONE:
|
case TIME_WITH_TIMEZONE:
|
||||||
return "timestamp($p) with time zone";
|
return "timestamp($p) with time zone";
|
||||||
|
|
||||||
|
@ -781,6 +783,11 @@ public class OracleDialect extends Dialect {
|
||||||
|
|
||||||
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, false ) );
|
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, false ) );
|
||||||
ddlTypeRegistry.addDescriptor( TABLE, new ArrayDdlTypeImpl( this, false ) );
|
ddlTypeRegistry.addDescriptor( TABLE, new ArrayDdlTypeImpl( this, false ) );
|
||||||
|
|
||||||
|
if(getVersion().isSameOrAfter(23)) {
|
||||||
|
ddlTypeRegistry.addDescriptor(new NamedNativeEnumDdlTypeImpl(this));
|
||||||
|
ddlTypeRegistry.addDescriptor( new NamedNativeOrdinalEnumDdlTypeImpl( this ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -973,8 +980,14 @@ public class OracleDialect extends Dialect {
|
||||||
typeContributions.getTypeConfiguration()
|
typeContributions.getTypeConfiguration()
|
||||||
.getJavaTypeRegistry()
|
.getJavaTypeRegistry()
|
||||||
.getDescriptor( Object.class )
|
.getDescriptor( Object.class )
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if(getVersion().isSameOrAfter(23)) {
|
||||||
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
||||||
|
jdbcTypeRegistry.addDescriptor(OracleEnumJdbcType.INSTANCE);
|
||||||
|
jdbcTypeRegistry.addDescriptor(OracleOrdinalEnumJdbcType.INSTANCE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1256,10 +1269,10 @@ public class OracleDialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
return DISTINCT_KEYWORD_PATTERN.matcher( sql ).find()
|
return DISTINCT_KEYWORD_PATTERN.matcher( sql ).find()
|
||||||
|| GROUP_BY_KEYWORD_PATTERN.matcher( sql ).find()
|
|| GROUP_BY_KEYWORD_PATTERN.matcher( sql ).find()
|
||||||
|| UNION_KEYWORD_PATTERN.matcher( sql ).find()
|
|| UNION_KEYWORD_PATTERN.matcher( sql ).find()
|
||||||
|| ORDER_BY_KEYWORD_PATTERN.matcher( sql ).find() && queryOptions.hasLimit()
|
|| ORDER_BY_KEYWORD_PATTERN.matcher( sql ).find() && queryOptions.hasLimit()
|
||||||
|| queryOptions.hasLimit() && queryOptions.getLimit().getFirstRow() != null;
|
|| queryOptions.hasLimit() && queryOptions.getLimit().getFirstRow() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1638,4 +1651,29 @@ public class OracleDialect extends Dialect {
|
||||||
public int getDriverMinorVersion() {
|
public int getDriverMinorVersion() {
|
||||||
return driverMinorVersion;
|
return driverMinorVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEnumTypeDeclaration(String name, String[] values) {
|
||||||
|
return getVersion().isSameOrAfter(23) ? name : super.getEnumTypeDeclaration(name, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCreateEnumTypeCommand(String name, String[] values) {
|
||||||
|
final StringBuilder domain = new StringBuilder();
|
||||||
|
domain.append( "create domain " )
|
||||||
|
.append( name )
|
||||||
|
.append( " as enum (" );
|
||||||
|
String separator = "";
|
||||||
|
for ( String value : values ) {
|
||||||
|
domain.append( separator ).append( value );
|
||||||
|
separator = ", ";
|
||||||
|
}
|
||||||
|
domain.append( ')' );
|
||||||
|
return new String[] { domain.toString() };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getDropEnumTypeCommand(String name) {
|
||||||
|
return new String[] { "drop domain if exists " + name + " force" };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* 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 org.hibernate.boot.model.relational.Database;
|
||||||
|
import org.hibernate.boot.model.relational.NamedAuxiliaryDatabaseObject;
|
||||||
|
import org.hibernate.engine.jdbc.Size;
|
||||||
|
import org.hibernate.type.descriptor.ValueBinder;
|
||||||
|
import org.hibernate.type.descriptor.ValueExtractor;
|
||||||
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.converter.internal.EnumHelper;
|
||||||
|
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 org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
|
import static org.hibernate.type.SqlTypes.NAMED_ENUM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a named {@code enum} type on Oracle 23ai+.
|
||||||
|
* <p>
|
||||||
|
* Hibernate does <em>not</em> automatically use this for enums
|
||||||
|
* mapped as {@link jakarta.persistence.EnumType#STRING}, and
|
||||||
|
* instead this type must be explicitly requested using:
|
||||||
|
* <pre>
|
||||||
|
* @JdbcTypeCode(SqlTypes.NAMED_ENUM)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see org.hibernate.type.SqlTypes#NAMED_ENUM
|
||||||
|
* @see OracleDialect#getEnumTypeDeclaration(String, String[])
|
||||||
|
* @see OracleDialect#getCreateEnumTypeCommand(String, String[])
|
||||||
|
*
|
||||||
|
* @author Loïc Lefèvre
|
||||||
|
*/
|
||||||
|
public class OracleEnumJdbcType implements JdbcType {
|
||||||
|
|
||||||
|
public static final OracleEnumJdbcType INSTANCE = new OracleEnumJdbcType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.VARCHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return NAMED_ENUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||||
|
return (appender, value, dialect, wrapperOptions) -> appender.appendSql( dialect.getEnumTypeDeclaration( (Class<? extends Enum<?>>) javaType.getJavaType() )+"." + ((Enum<?>) value).name() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFriendlyName() {
|
||||||
|
return "ENUM";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EnumTypeDescriptor";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException {
|
||||||
|
st.setNull( index, getJdbcTypeCode() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException {
|
||||||
|
st.setNull( name, getJdbcTypeCode() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
st.setString( index, ((Enum<?>) value).name() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
st.setString( name, ((Enum<?>) value).name() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 getJavaType().wrap( rs.getString( paramIndex ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
return getJavaType().wrap( statement.getString( index ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
return getJavaType().wrap( statement.getString( name ), options );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuxiliaryDatabaseObjects(
|
||||||
|
JavaType<?> javaType,
|
||||||
|
Size columnSize,
|
||||||
|
Database database,
|
||||||
|
JdbcTypeIndicators context) {
|
||||||
|
addAuxiliaryDatabaseObjects( javaType, database, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuxiliaryDatabaseObjects(
|
||||||
|
JavaType<?> javaType,
|
||||||
|
Size columnSize,
|
||||||
|
Database database,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
addAuxiliaryDatabaseObjects( javaType, database, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAuxiliaryDatabaseObjects(
|
||||||
|
JavaType<?> javaType,
|
||||||
|
Database database,
|
||||||
|
boolean sortEnumValues) {
|
||||||
|
final Dialect dialect = database.getDialect();
|
||||||
|
final Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) javaType.getJavaType();
|
||||||
|
final String enumTypeName = enumClass.getSimpleName();
|
||||||
|
final String[] enumeratedValues = EnumHelper.getEnumeratedValues( enumClass );
|
||||||
|
if ( sortEnumValues ) {
|
||||||
|
Arrays.sort( enumeratedValues );
|
||||||
|
}
|
||||||
|
final String[] create = getCreateEnumTypeCommand(
|
||||||
|
javaType.getJavaTypeClass().getSimpleName(),
|
||||||
|
enumeratedValues
|
||||||
|
);
|
||||||
|
final String[] drop = dialect.getDropEnumTypeCommand( enumClass );
|
||||||
|
if ( create != null && create.length > 0 ) {
|
||||||
|
database.addAuxiliaryDatabaseObject(
|
||||||
|
new NamedAuxiliaryDatabaseObject(
|
||||||
|
enumTypeName,
|
||||||
|
database.getDefaultNamespace(),
|
||||||
|
create,
|
||||||
|
drop,
|
||||||
|
emptySet(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to generate the CREATE DDL command for Data Use Case Domain based on VARCHAR2 values.
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param values
|
||||||
|
* @return the DDL command to create that enum
|
||||||
|
*/
|
||||||
|
public String[] getCreateEnumTypeCommand(String name, String[] values) {
|
||||||
|
final StringBuilder domain = new StringBuilder();
|
||||||
|
domain.append( "create domain " )
|
||||||
|
.append( name )
|
||||||
|
.append( " as enum (" );
|
||||||
|
String separator = "";
|
||||||
|
for ( String value : values ) {
|
||||||
|
domain.append( separator ).append( value ).append("='").append(value).append("'");
|
||||||
|
separator = ", ";
|
||||||
|
}
|
||||||
|
domain.append( ')' );
|
||||||
|
return new String[] { domain.toString() };
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* 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 jakarta.persistence.EnumType;
|
||||||
|
import org.hibernate.boot.model.relational.Database;
|
||||||
|
import org.hibernate.boot.model.relational.NamedAuxiliaryDatabaseObject;
|
||||||
|
import org.hibernate.engine.jdbc.Size;
|
||||||
|
import org.hibernate.type.descriptor.ValueBinder;
|
||||||
|
import org.hibernate.type.descriptor.ValueExtractor;
|
||||||
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.converter.internal.EnumHelper;
|
||||||
|
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.JdbcTypeIndicators;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptySet;
|
||||||
|
import static org.hibernate.type.SqlTypes.NAMED_ORDINAL_ENUM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a named {@code enum} type on Oracle 23ai+.
|
||||||
|
* <p>
|
||||||
|
* Hibernate does <em>not</em> automatically use this for enums
|
||||||
|
* mapped as {@link EnumType#ORDINAL}, and
|
||||||
|
* instead this type must be explicitly requested using:
|
||||||
|
* <pre>
|
||||||
|
* @JdbcTypeCode(SqlTypes.NAMED_ORDINAL_ENUM)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @see org.hibernate.type.SqlTypes#NAMED_ORDINAL_ENUM
|
||||||
|
* @see OracleDialect#getEnumTypeDeclaration(String, String[])
|
||||||
|
* @see OracleDialect#getCreateEnumTypeCommand(String, String[])
|
||||||
|
*
|
||||||
|
* @author Loïc Lefèvre
|
||||||
|
*/
|
||||||
|
public class OracleOrdinalEnumJdbcType extends OracleEnumJdbcType {
|
||||||
|
|
||||||
|
public static final OracleOrdinalEnumJdbcType INSTANCE = new OracleOrdinalEnumJdbcType();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getJdbcTypeCode() {
|
||||||
|
return Types.INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultSqlTypeCode() {
|
||||||
|
return NAMED_ORDINAL_ENUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||||
|
return new BasicBinder<>( javaType, this ) {
|
||||||
|
@Override
|
||||||
|
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException {
|
||||||
|
st.setNull( index, getJdbcTypeCode() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException {
|
||||||
|
st.setNull( name, getJdbcTypeCode() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
st.setInt( index, ((Enum<?>) value).ordinal()+1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||||
|
throws SQLException {
|
||||||
|
st.setInt( name, ((Enum<?>) value).ordinal()+1 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
final int value = rs.getInt( paramIndex );
|
||||||
|
if(rs.wasNull()) {
|
||||||
|
return getJavaType().wrap(null, options);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return getJavaType().wrap(value - 1, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||||
|
return getJavaType().wrap( statement.getInt( index ), options );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||||
|
return getJavaType().wrap( statement.getInt( name ), options );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuxiliaryDatabaseObjects(
|
||||||
|
JavaType<?> javaType,
|
||||||
|
Size columnSize,
|
||||||
|
Database database,
|
||||||
|
JdbcTypeIndicators context) {
|
||||||
|
addAuxiliaryDatabaseObjects( javaType, database, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAuxiliaryDatabaseObjects(
|
||||||
|
JavaType<?> javaType,
|
||||||
|
Size columnSize,
|
||||||
|
Database database,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
addAuxiliaryDatabaseObjects( javaType, database, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAuxiliaryDatabaseObjects(
|
||||||
|
JavaType<?> javaType,
|
||||||
|
Database database,
|
||||||
|
boolean sortEnumValues) {
|
||||||
|
final Dialect dialect = database.getDialect();
|
||||||
|
final Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) javaType.getJavaType();
|
||||||
|
final String enumTypeName = enumClass.getSimpleName();
|
||||||
|
final String[] enumeratedValues = EnumHelper.getEnumeratedValues( enumClass );
|
||||||
|
if ( sortEnumValues ) {
|
||||||
|
Arrays.sort( enumeratedValues );
|
||||||
|
}
|
||||||
|
final String[] create = dialect.getCreateEnumTypeCommand(
|
||||||
|
javaType.getJavaTypeClass().getSimpleName(),
|
||||||
|
enumeratedValues
|
||||||
|
);
|
||||||
|
final String[] drop = dialect.getDropEnumTypeCommand( enumClass );
|
||||||
|
if ( create != null && create.length > 0 ) {
|
||||||
|
database.addAuxiliaryDatabaseObject(
|
||||||
|
new NamedAuxiliaryDatabaseObject(
|
||||||
|
enumTypeName,
|
||||||
|
database.getDefaultNamespace(),
|
||||||
|
create,
|
||||||
|
drop,
|
||||||
|
emptySet(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* 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.orm.test.type;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.GenerationType;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import org.hibernate.annotations.JdbcTypeCode;
|
||||||
|
import org.hibernate.dialect.OracleDialect;
|
||||||
|
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.Test;
|
||||||
|
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.Statement;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
@SessionFactory
|
||||||
|
@DomainModel(annotatedClasses = {OracleEnumTest.Timeslot.class, OracleEnumTest.Activity.class, OracleEnumTest.Weather.class, OracleEnumTest.Sky.class})
|
||||||
|
@RequiresDialect(value = OracleDialect.class, majorVersion = 23)
|
||||||
|
public class OracleEnumTest {
|
||||||
|
|
||||||
|
@Test public void testNamedEnum(SessionFactoryScope scope) {
|
||||||
|
Timeslot timeslot = new Timeslot();
|
||||||
|
Activity activity = new Activity();
|
||||||
|
activity.activityType = ActivityType.Play;
|
||||||
|
timeslot.activity = activity;
|
||||||
|
scope.inTransaction( s -> s.persist( timeslot ) );
|
||||||
|
Timeslot ts = scope.fromTransaction( s-> s.createQuery("from Timeslot where activity.activityType = Play", Timeslot.class ).getSingleResult() );
|
||||||
|
assertEquals( ts.activity.activityType, ActivityType.Play );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testOrdinalEnum(SessionFactoryScope scope) {
|
||||||
|
Weather weather = new Weather();
|
||||||
|
Sky sky = new Sky();
|
||||||
|
sky.skyType = SkyType.Sunny;
|
||||||
|
weather.sky = sky;
|
||||||
|
scope.inTransaction( s -> s.persist( weather ) );
|
||||||
|
Weather w = scope.fromTransaction( s-> s.createQuery("from Weather where sky.skyType = Sunny", Weather.class ).getSingleResult() );
|
||||||
|
assertEquals( w.sky.skyType, SkyType.Sunny );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testSchema(SessionFactoryScope scope) {
|
||||||
|
scope.inSession( s -> {
|
||||||
|
s.doWork(
|
||||||
|
c -> {
|
||||||
|
try(Statement stmt = c.createStatement()) {
|
||||||
|
try(ResultSet typeInfo = stmt.executeQuery("select name, decode(instr(data_display,'WHEN '''),0,'NUMBER','VARCHAR2') from user_domains where type='ENUMERATED'")) {
|
||||||
|
while (typeInfo.next()) {
|
||||||
|
String name = typeInfo.getString(1);
|
||||||
|
String baseType = typeInfo.getString(2);
|
||||||
|
if (name.equalsIgnoreCase("ActivityType") && baseType.equals("VARCHAR2")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("named enum type not exported");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
scope.inSession( s -> {
|
||||||
|
s.doWork(
|
||||||
|
c -> {
|
||||||
|
ResultSet tableInfo = c.getMetaData().getColumns(null, null, "ACTIVITY", "ACTIVITYTYPE" );
|
||||||
|
while ( tableInfo.next() ) {
|
||||||
|
String type = tableInfo.getString(6);
|
||||||
|
assertEquals( "VARCHAR2", type );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fail("named enum column not exported");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ActivityType {Work, Play, Sleep }
|
||||||
|
|
||||||
|
@Entity(name = "Activity")
|
||||||
|
public static class Activity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@JdbcTypeCode(SqlTypes.NAMED_ENUM)
|
||||||
|
ActivityType activityType;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Timeslot")
|
||||||
|
public static class Timeslot {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
@ManyToOne(cascade = CascadeType.PERSIST)
|
||||||
|
Activity activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SkyType {Sunny, Cloudy}
|
||||||
|
@Entity(name = "Sky")
|
||||||
|
public static class Sky {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@JdbcTypeCode(SqlTypes.NAMED_ORDINAL_ENUM)
|
||||||
|
SkyType skyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Weather")
|
||||||
|
public static class Weather {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
@ManyToOne(cascade = CascadeType.PERSIST)
|
||||||
|
Sky sky;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue