NativeQuery support

- support for `#addScalar(Class,AttributeConverter)`
- support for `#addScalar(Class,Class<AttributeConverter>)`
- fixed problem with mapping of converted enums
This commit is contained in:
Steve Ebersole 2020-07-27 16:44:22 -05:00
parent 3b210c493f
commit 2f8f04747b
29 changed files with 1039 additions and 246 deletions

20
design/6.0-changes.adoc Normal file
View File

@ -0,0 +1,20 @@
= Changes
Changes in 6.0 worth documenting in various places
== New features
Document in release-notes:
* `@SqlTypeCode`
* `@SqlType`
* `@JavaType`
* `NativeQuery#addScalar(Class)`
* `NativeQuery#addScalar(Class,AttributeConverter)`
* `NativeQuery#addScalar(Class,Class<AttributeConverter>)`
== Changes
Document in migration-guide.

View File

@ -173,7 +173,7 @@ public class InferredBasicValueResolver {
switch ( enumStyle ) {
case STRING: {
final JavaTypeDescriptor<String> relationalJtd;
final JavaTypeDescriptor<?> relationalJtd;
if ( explicitJavaType != null ) {
if ( ! String.class.isAssignableFrom( explicitJavaType.getJavaType() ) ) {
throw new MappingException(
@ -182,11 +182,12 @@ public class InferredBasicValueResolver {
" should handle `java.lang.String` as its relational type descriptor"
);
}
//noinspection unchecked
relationalJtd = explicitJavaType;
}
else {
relationalJtd = typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class );
final boolean useCharacter = stdIndicators.getColumnLength() == 1;
final Class<?> relationalJavaType = useCharacter ? Character.class : String.class;
relationalJtd = typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( relationalJavaType );
}
final SqlTypeDescriptor std = explicitSqlType != null ? explicitSqlType : relationalJtd.getJdbcRecommendedSqlType( stdIndicators );

View File

@ -19,6 +19,8 @@ import org.hibernate.mapping.BasicValue;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
@ -93,6 +95,7 @@ public class NamedConverterResolution<J> implements BasicValue.Resolution<J> {
final JavaTypeDescriptor explicitJtd = explicitJtdAccess != null
? explicitJtdAccess.apply( typeConfiguration )
: null;
final JavaTypeDescriptor domainJtd = explicitJtd != null
? explicitJtd
: converter.getDomainJavaDescriptor();
@ -100,7 +103,9 @@ public class NamedConverterResolution<J> implements BasicValue.Resolution<J> {
final SqlTypeDescriptor explicitStd = explicitStdAccess != null
? explicitStdAccess.apply( typeConfiguration )
: null;
final JavaTypeDescriptor relationalJtd = converter.getRelationalJavaDescriptor();
final SqlTypeDescriptor relationalStd = explicitStd != null
? explicitStd
: relationalJtd.getJdbcRecommendedSqlType( sqlTypeIndicators );
@ -139,13 +144,46 @@ public class NamedConverterResolution<J> implements BasicValue.Resolution<J> {
SqlTypeDescriptor relationalStd,
JpaAttributeConverter valueConverter,
MutabilityPlan mutabilityPlan) {
assert domainJtd != null;
this.domainJtd = domainJtd;
assert relationalJtd != null;
this.relationalJtd = relationalJtd;
assert relationalStd != null;
this.relationalStd = relationalStd;
assert valueConverter != null;
this.valueConverter = valueConverter;
assert mutabilityPlan != null;
this.mutabilityPlan = mutabilityPlan;
this.jdbcMapping = new StandardBasicTypeImpl( relationalJtd, relationalStd ).getJdbcMapping();
this.jdbcMapping = new JdbcMapping() {
private final ValueExtractor extractor = relationalStd.getExtractor( relationalJtd );
private final ValueBinder binder = relationalStd.getBinder( relationalJtd );
@Override
public JavaTypeDescriptor getJavaTypeDescriptor() {
return relationalJtd;
}
@Override
public SqlTypeDescriptor getSqlTypeDescriptor() {
return relationalStd;
}
@Override
public ValueExtractor getJdbcValueExtractor() {
return extractor;
}
@Override
public ValueBinder getJdbcValueBinder() {
return binder;
}
};
// this.jdbcMapping = new StandardBasicTypeImpl( relationalJtd, relationalStd );
this.legacyResolvedType = new AttributeConverterTypeAdapter(
ConverterDescriptor.TYPE_NAME_PREFIX + valueConverter.getConverterJavaTypeDescriptor().getJavaType().getName(),
@ -156,8 +194,7 @@ public class NamedConverterResolution<J> implements BasicValue.Resolution<J> {
),
valueConverter,
relationalStd,
domainJtd.getJavaType(),
relationalJtd.getJavaType(),
relationalJtd,
domainJtd
);
}

View File

@ -167,6 +167,17 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
return column;
}
@Override
public long getColumnLength() {
if ( column != null && column instanceof Column ) {
final Long length = ( (Column) column ).getLength();
return length == null ? NO_COLUMN_LENGTH : length;
}
else {
return NO_COLUMN_LENGTH;
}
}
@Override
public void addColumn(Column incomingColumn) {
super.addColumn( incomingColumn );
@ -330,7 +341,6 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
);
}
JavaTypeDescriptor jtd = null;
// determine JTD if we can

View File

@ -576,7 +576,7 @@ public abstract class SimpleValue implements KeyValue {
}
);
final BasicJavaDescriptor entityAttributeJavaTypeDescriptor = (BasicJavaDescriptor) jpaAttributeConverter.getDomainJavaTypeDescriptor();
final BasicJavaDescriptor<?> domainJtd = (BasicJavaDescriptor<?>) jpaAttributeConverter.getDomainJavaTypeDescriptor();
// build the SqlTypeDescriptor adapter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -595,7 +595,7 @@ public abstract class SimpleValue implements KeyValue {
jdbcTypeCode = LobTypeMappings.getLobCodeTypeMapping( jdbcTypeCode );
}
else {
if ( Serializable.class.isAssignableFrom( entityAttributeJavaTypeDescriptor.getJavaType() ) ) {
if ( Serializable.class.isAssignableFrom( domainJtd.getJavaType() ) ) {
jdbcTypeCode = Types.BLOB;
}
else {
@ -642,19 +642,18 @@ public abstract class SimpleValue implements KeyValue {
jpaAttributeConverter.getDomainJavaTypeDescriptor().getJavaType().getSimpleName(),
jpaAttributeConverter.getRelationalJavaTypeDescriptor().getJavaType().getSimpleName()
);
return new AttributeConverterTypeAdapter(
return new AttributeConverterTypeAdapter<>(
name,
description,
jpaAttributeConverter,
sqlTypeDescriptorAdapter,
jpaAttributeConverter.getDomainJavaTypeDescriptor().getJavaType(),
jpaAttributeConverter.getRelationalJavaTypeDescriptor().getJavaType(),
entityAttributeJavaTypeDescriptor
jpaAttributeConverter.getRelationalJavaTypeDescriptor(),
jpaAttributeConverter.getDomainJavaTypeDescriptor()
);
}
public boolean isTypeSpecified() {
return typeName!=null;
return typeName != null;
}
public void setTypeParameters(Properties parameterMap) {
@ -752,11 +751,15 @@ public abstract class SimpleValue implements KeyValue {
private void createParameterImpl() {
try {
String[] columnsNames = new String[columns.size()];
final String[] columnNames = new String[ columns.size() ];
final Long[] columnLengths = new Long[ columns.size() ];
for ( int i = 0; i < columns.size(); i++ ) {
Selectable column = columns.get(i);
if (column instanceof Column){
columnsNames[i] = ((Column) column).getName();
final Selectable selectable = columns.get(i);
if ( selectable instanceof Column ) {
final Column column = (Column) selectable;
columnNames[i] = column.getName();
columnLengths[i] = column.getLength();
}
}
@ -781,7 +784,8 @@ public abstract class SimpleValue implements KeyValue {
table.getSchema(),
table.getName(),
Boolean.valueOf( typeParameters.getProperty( DynamicParameterizedType.IS_PRIMARY_KEY ) ),
columnsNames
columnNames,
columnLengths
)
);
}
@ -799,9 +803,17 @@ public abstract class SimpleValue implements KeyValue {
private final String table;
private final boolean primaryKey;
private final String[] columns;
private final Long[] columnLengths;
private ParameterTypeImpl(Class returnedClass, Annotation[] annotationsMethod, String catalog, String schema,
String table, boolean primaryKey, String[] columns) {
private ParameterTypeImpl(
Class returnedClass,
Annotation[] annotationsMethod,
String catalog,
String schema,
String table,
boolean primaryKey,
String[] columns,
Long[] columnLengths) {
this.returnedClass = returnedClass;
this.annotationsMethod = annotationsMethod;
this.catalog = catalog;
@ -809,6 +821,7 @@ public abstract class SimpleValue implements KeyValue {
this.table = table;
this.primaryKey = primaryKey;
this.columns = columns;
this.columnLengths = columnLengths;
}
@Override
@ -845,5 +858,10 @@ public abstract class SimpleValue implements KeyValue {
public String[] getColumns() {
return columns;
}
@Override
public Long[] getColumnLengths() {
return columnLengths;
}
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.metamodel.model.convert;
import javax.persistence.AttributeConverter;
import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.model.convert.internal.JpaAttributeConverterImpl;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Factory for converter instances
*
* @author Steve Ebersole
*/
public class Converters {
public static <O,R> BasicValueConverter<O,R> jpaAttributeConverter(
JavaTypeDescriptor<R> relationalJtd,
JavaTypeDescriptor<O> domainJtd,
Class<? extends AttributeConverter<O,R>> converterClass,
SessionFactory factory) {
final SessionFactoryImplementor sfi = (SessionFactoryImplementor) factory;
final ManagedBeanRegistry beanRegistry = sfi.getServiceRegistry().getService( ManagedBeanRegistry.class );
final ManagedBean<? extends AttributeConverter<O, R>> converterBean = beanRegistry.getBean( converterClass );
final TypeConfiguration typeConfiguration = sfi.getTypeConfiguration();
final JavaTypeDescriptorRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
final JavaTypeDescriptor<? extends AttributeConverter<O, R>> converterJtd = jtdRegistry.getDescriptor( converterClass );
return new JpaAttributeConverterImpl<>( converterBean, converterJtd, domainJtd, relationalJtd );
}
private Converters() {
}
}

View File

@ -18,14 +18,14 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author Steve Ebersole
*/
public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R> {
private final ManagedBean<AttributeConverter<O,R>> attributeConverterBean;
private final JavaTypeDescriptor<AttributeConverter<O, R>> converterJavaTypeDescriptor;
private final ManagedBean<? extends AttributeConverter<O,R>> attributeConverterBean;
private final JavaTypeDescriptor<? extends AttributeConverter<O, R>> converterJavaTypeDescriptor;
private final JavaTypeDescriptor<O> domainJavaTypeDescriptor;
private final JavaTypeDescriptor<R> relationalJavaTypeDescriptor;
public JpaAttributeConverterImpl(
ManagedBean<AttributeConverter<O, R>> attributeConverterBean,
JavaTypeDescriptor<AttributeConverter<O,R>> converterJavaTypeDescriptor,
ManagedBean<? extends AttributeConverter<O, R>> attributeConverterBean,
JavaTypeDescriptor<? extends AttributeConverter<O,R>> converterJavaTypeDescriptor,
JavaTypeDescriptor<O> domainJavaTypeDescriptor,
JavaTypeDescriptor<R> relationalJavaTypeDescriptor) {
this.attributeConverterBean = attributeConverterBean;
@ -35,7 +35,7 @@ public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R
}
@Override
public ManagedBean<AttributeConverter<O, R>> getConverterBean() {
public ManagedBean<? extends AttributeConverter<O, R>> getConverterBean() {
return attributeConverterBean;
}
@ -50,7 +50,7 @@ public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R
}
@Override
public JavaTypeDescriptor<AttributeConverter<O, R>> getConverterJavaTypeDescriptor() {
public JavaTypeDescriptor<? extends AttributeConverter<O, R>> getConverterJavaTypeDescriptor() {
return converterJavaTypeDescriptor;
}

View File

@ -70,7 +70,7 @@ public class NamedEnumValueConverter<E extends Enum<E>> implements EnumValueConv
@Override
public int getJdbcTypeCode() {
return Types.VARCHAR;
return sqlTypeDescriptor.getJdbcTypeCode();
}
@Override

View File

@ -17,9 +17,9 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author Steve Ebersole
*/
public interface JpaAttributeConverter<O,R> extends BasicValueConverter<O,R> {
JavaTypeDescriptor<AttributeConverter<O,R>> getConverterJavaTypeDescriptor();
JavaTypeDescriptor<? extends AttributeConverter<O,R>> getConverterJavaTypeDescriptor();
ManagedBean<AttributeConverter<O,R>> getConverterBean();
ManagedBean<? extends AttributeConverter<O,R>> getConverterBean();
JavaTypeDescriptor<O> getDomainJavaTypeDescriptor();
JavaTypeDescriptor<R> getRelationalJavaTypeDescriptor();

View File

@ -14,6 +14,7 @@ import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import javax.persistence.AttributeConverter;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Parameter;
@ -76,20 +77,6 @@ public interface NativeQuery<T> extends Query<T>, SynchronizeableQuery {
*/
NativeQuery<T> addScalar(String columnAlias);
/**
* Declare a scalar query result.
* <p/>
* Functions like {@code <return-scalar/>} in {@code hbm.xml} or
* {@link javax.persistence.ColumnResult} in annotations
*
* @param columnAlias The column alias in the result-set to be processed
* as a scalar result
* @param type The Hibernate type as which to treat the value.
*
* @return {@code this}, for method chaining
*/
// NativeQuery<T> addScalar(String columnAlias, Type type);
/**
* Declare a scalar query result.
* <p/>
@ -105,12 +92,45 @@ public interface NativeQuery<T> extends Query<T>, SynchronizeableQuery {
NativeQuery<T> addScalar(String columnAlias, BasicDomainType type);
/**
* Declare a scalar query result with an explicit return type
* Declare a scalar query result using the specified result type.
*
* Hibernate will implicitly determine an appropriate conversion, if
* it can. Otherwise an exception will be thrown
*
* @return {@code this}, for method chaining
*
* @since 6.0
*/
NativeQuery<T> addScalar(String columnAlias, Class<?> javaType);
/**
* Declare a scalar query result with an explicit conversion
*
* @param relationalJavaType The Java type expected by the converter as its
* "relational" type.
* @param converter The conversion to apply. Consumes the JDBC value based
* on `relationalJavaType`.
*
* @return {@code this}, for method chaining
*
* @since 6.0
*/
<C> NativeQuery<T> addScalar(String columnAlias, Class<C> relationalJavaType, AttributeConverter<?,C> converter);
/**
* Declare a scalar query result with an explicit conversion
*
* @param relationalJavaType The Java type expected by the converter as its
* "relational" type.
* @param converter The conversion to apply. Consumes the JDBC value based
* on `relationalJavaType`.
*
* @return {@code this}, for method chaining
*
* @since 6.0
*/
<C> NativeQuery<T> addScalar(String columnAlias, Class<C> relationalJavaType, Class<? extends AttributeConverter<?,C>> converter);
/**
* Add a new root return mapping, returning a {@link RootReturn} to allow
* further definition.

View File

@ -6,55 +6,62 @@
*/
package org.hibernate.query.results;
import java.util.function.Consumer;
import javax.persistence.AttributeConverter;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
* @author Steve Ebersole
*/
public class Builders {
public static ScalarResultBuilder scalar(String columnAlias) {
return new ScalarResultBuilder( columnAlias );
return new StandardScalarResultBuilder( columnAlias );
}
public static ScalarResultBuilder scalar(
String columnAlias,
BasicType<?> type) {
return new ScalarResultBuilder( columnAlias, type );
return new StandardScalarResultBuilder( columnAlias, type );
}
public static ScalarResultBuilder scalar(
String columnAlias,
Class<?> javaType,
SessionFactoryImplementor factory) {
return new ScalarResultBuilder(
columnAlias,
factory.getTypeConfiguration().standardBasicTypeForJavaType( javaType )
);
final JavaTypeDescriptor<?> javaTypeDescriptor = factory.getTypeConfiguration()
.getJavaTypeDescriptorRegistry()
.getDescriptor( javaType );
return new StandardScalarResultBuilder( columnAlias, javaTypeDescriptor );
}
public static DomainResult<?> implicitScalarDomainResult(
int colIndex,
String columnName,
JdbcValuesMetadata jdbcResultsMetadata,
Consumer<SqlSelection> sqlSelectionConsumer,
public static <C> ResultBuilder scalar(
String columnAlias,
Class<C> relationalJavaType,
AttributeConverter<?, C> converter,
SessionFactoryImplementor sessionFactory) {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( colIndex );
final BasicJavaDescriptor<?> javaTypeDescriptor = sqlTypeDescriptor.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
final BasicType<?> jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( javaTypeDescriptor, sqlTypeDescriptor );
sqlSelectionConsumer.accept( new SqlSelectionImpl( colIndex, jdbcMapping ) );
return new BasicResult<>( colIndex, columnName, javaTypeDescriptor );
return ConvertedResultBuilder.from( columnAlias, relationalJavaType, converter, sessionFactory );
}
public static <C> ResultBuilder scalar(
String columnAlias,
Class<C> relationalJavaType,
Class<? extends AttributeConverter<?, C>> converterJavaType,
SessionFactoryImplementor sessionFactory) {
return ConvertedResultBuilder.from( columnAlias, relationalJavaType, converterJavaType, sessionFactory );
}
public static ScalarResultBuilder scalar(int position) {
// will be needed for interpreting legacy HBM <resultset/> mappings
throw new NotYetImplementedFor6Exception();
}
public static ScalarResultBuilder scalar(int position, BasicType<?> type) {
// will be needed for interpreting legacy HBM <resultset/> mappings
throw new NotYetImplementedFor6Exception();
}
public static EntityResultBuilder entity(String tableAlias, String entityName) {

View File

@ -0,0 +1,200 @@
/*
* 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.query.results;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import javax.persistence.AttributeConverter;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.converter.ConvertedValueExtractor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* A ResultBuilder for explicitly converted scalar results
*
* @author Steve Ebersole
*/
public class ConvertedResultBuilder implements ScalarResultBuilder {
public static <C> ResultBuilder from(
String columnAlias,
Class<C> relationalJavaType,
AttributeConverter<?, C> converter,
SessionFactoryImplementor sessionFactory) {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JavaTypeDescriptorRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
final JavaTypeDescriptor<C> relationJtd = jtdRegistry.getDescriptor( relationalJavaType );
return new ConvertedResultBuilder( columnAlias, relationJtd, converter );
}
public static <C> ResultBuilder from(
String columnAlias,
Class<C> relationalJavaType,
Class<? extends AttributeConverter<?, C>> converterJavaType,
SessionFactoryImplementor sessionFactory) {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final JavaTypeDescriptorRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
final JavaTypeDescriptor<C> relationJtd = jtdRegistry.getDescriptor( relationalJavaType );
final ManagedBeanRegistry beans = sessionFactory.getServiceRegistry().getService( ManagedBeanRegistry.class );
final ManagedBean<? extends AttributeConverter<?, C>> bean = beans.getBean( converterJavaType );
final AttributeConverter<?, C> converter = bean.getBeanInstance();
return new ConvertedResultBuilder( columnAlias, relationJtd, converter );
}
private final String columnAlias;
private final JavaTypeDescriptor<?> relationJtd;
private final AttributeConverter<?,?> converter;
public ConvertedResultBuilder(
String columnAlias,
JavaTypeDescriptor<?> relationJtd,
AttributeConverter<?, ?> converter) {
assert columnAlias != null;
this.columnAlias = columnAlias;
assert relationJtd != null;
this.relationJtd = relationJtd;
assert converter != null;
this.converter = converter;
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public DomainResult<?> buildReturn(
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, LegacyFetchBuilder> legacyFetchResolver,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( columnAlias );
final int valuesArrayPosition = jdbcPosition - 1;
final SqlTypeDescriptor std = jdbcResultsMetadata.resolveSqlTypeDescriptor( jdbcPosition );
final ConverterMapping<Object> converterMapping = new ConverterMapping(
relationJtd,
std,
new ConvertedValueExtractor( std.getExtractor( relationJtd ), converter )
);
final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( valuesArrayPosition, converterMapping );
sqlSelectionConsumer.accept( sqlSelection );
return new BasicResult( valuesArrayPosition, columnAlias, converterMapping.getJavaTypeDescriptor() );
}
private static class ConverterMapping<T> implements MappingModelExpressable<T>, JdbcMapping {
private final JavaTypeDescriptor<T> jtd;
private final SqlTypeDescriptor std;
private final ValueExtractor<T> extractor;
public ConverterMapping(
JavaTypeDescriptor<T> jtd,
SqlTypeDescriptor std,
ValueExtractor<T> extractor) {
this.jtd = jtd;
this.std = std;
this.extractor = extractor;
}
@Override
public int getJdbcTypeCount(TypeConfiguration typeConfiguration) {
return 1;
}
@Override
public JavaTypeDescriptor<T> getJavaTypeDescriptor() {
return jtd;
}
@Override
public SqlTypeDescriptor getSqlTypeDescriptor() {
return std;
}
@Override
public ValueExtractor<T> getJdbcValueExtractor() {
return extractor;
}
@Override
public ValueBinder<?> getJdbcValueBinder() {
// this will never get used for binding values
throw new UnsupportedOperationException();
}
@Override
public List<JdbcMapping> getJdbcMappings(TypeConfiguration typeConfiguration) {
return Collections.singletonList( this );
}
@Override
public void visitJdbcTypes(
Consumer<JdbcMapping> action,
Clause clause,
TypeConfiguration typeConfiguration) {
action.accept( this );
}
}
@SuppressWarnings("rawtypes")
private static class SqlSelectionImpl implements SqlSelection {
private final int valuesArrayPosition;
private final ConverterMapping converterMapping;
public SqlSelectionImpl(
int valuesArrayPosition,
ConverterMapping converterMapping) {
this.valuesArrayPosition = valuesArrayPosition;
this.converterMapping = converterMapping;
}
@Override
public int getValuesArrayPosition() {
return valuesArrayPosition;
}
@Override
public ValueExtractor getJdbcValueExtractor() {
return converterMapping.getJdbcValueExtractor();
}
@Override
public MappingModelExpressable getExpressionType() {
return converterMapping;
}
@Override
public void accept(SqlAstWalker sqlAstWalker) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -6,96 +6,10 @@
*/
package org.hibernate.query.results;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @see javax.persistence.ColumnResult
* Nominal extension to ResultBuilder for cases involving scalar results
*
* @author Steve Ebersole
*/
public class ScalarResultBuilder implements ResultBuilder {
private final String explicitName;
private final BasicType<?> explicitType;
private final JavaTypeDescriptor<?> explicitJavaTypeDescriptor;
ScalarResultBuilder(String explicitName, BasicType<?> explicitType) {
assert explicitName != null;
this.explicitName = explicitName;
assert explicitType != null;
this.explicitType = explicitType;
this.explicitJavaTypeDescriptor = null;
}
ScalarResultBuilder(String explicitName, JavaTypeDescriptor<?> explicitJavaTypeDescriptor) {
assert explicitName != null;
this.explicitName = explicitName;
assert explicitJavaTypeDescriptor != null;
this.explicitJavaTypeDescriptor = explicitJavaTypeDescriptor;
this.explicitType = null;
}
public ScalarResultBuilder(String explicitName) {
assert explicitName != null;
this.explicitName = explicitName;
this.explicitType = null;
this.explicitJavaTypeDescriptor = null;
}
public String getExplicitName() {
return explicitName;
}
@Override
public DomainResult<?> buildReturn(
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, LegacyFetchBuilder> legacyFetchResolver,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( explicitName );
final int valuesArrayPosition = jdbcPosition - 1;
final BasicType<?> jdbcMapping;
if ( explicitType != null ) {
jdbcMapping = explicitType;
}
else if ( explicitJavaTypeDescriptor != null ) {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( jdbcPosition );
jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( explicitJavaTypeDescriptor, sqlTypeDescriptor );
}
else {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( jdbcPosition );
final JavaTypeDescriptor<?> javaTypeDescriptor = sqlTypeDescriptor.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( javaTypeDescriptor, sqlTypeDescriptor );
}
final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( valuesArrayPosition, jdbcMapping );
sqlSelectionConsumer.accept( sqlSelection );
return new BasicResult( valuesArrayPosition, explicitName, jdbcMapping.getJavaTypeDescriptor() );
}
public interface ScalarResultBuilder extends ResultBuilder {
}

View File

@ -0,0 +1,109 @@
/*
* 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.query.results;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @see javax.persistence.ColumnResult
*
* @author Steve Ebersole
*/
public class StandardScalarResultBuilder implements ScalarResultBuilder {
private final String explicitName;
private final BasicType<?> explicitType;
private final JavaTypeDescriptor<?> explicitJavaTypeDescriptor;
public StandardScalarResultBuilder(String explicitName) {
assert explicitName != null;
this.explicitName = explicitName;
this.explicitType = null;
this.explicitJavaTypeDescriptor = null;
}
StandardScalarResultBuilder(String explicitName, BasicType<?> explicitType) {
assert explicitName != null;
this.explicitName = explicitName;
assert explicitType != null;
this.explicitType = explicitType;
this.explicitJavaTypeDescriptor = null;
}
StandardScalarResultBuilder(String explicitName, JavaTypeDescriptor<?> explicitJavaTypeDescriptor) {
assert explicitName != null;
this.explicitName = explicitName;
assert explicitJavaTypeDescriptor != null;
this.explicitJavaTypeDescriptor = explicitJavaTypeDescriptor;
this.explicitType = null;
}
StandardScalarResultBuilder(JavaTypeDescriptor<?> explicitJavaTypeDescriptor) {
assert explicitJavaTypeDescriptor != null;
this.explicitJavaTypeDescriptor = explicitJavaTypeDescriptor;
this.explicitName = null;
this.explicitType = null;
}
public String getExplicitName() {
return explicitName;
}
@Override
public DomainResult<?> buildReturn(
JdbcValuesMetadata jdbcResultsMetadata,
BiFunction<String, String, LegacyFetchBuilder> legacyFetchResolver,
Consumer<SqlSelection> sqlSelectionConsumer,
SessionFactoryImplementor sessionFactory) {
final int jdbcPosition = jdbcResultsMetadata.resolveColumnPosition( explicitName );
final int valuesArrayPosition = jdbcPosition - 1;
final BasicType<?> jdbcMapping;
if ( explicitType != null ) {
jdbcMapping = explicitType;
}
else if ( explicitJavaTypeDescriptor != null ) {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( jdbcPosition );
jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( explicitJavaTypeDescriptor, sqlTypeDescriptor );
}
else {
final TypeConfiguration typeConfiguration = sessionFactory.getTypeConfiguration();
final SqlTypeDescriptor sqlTypeDescriptor = jdbcResultsMetadata.resolveSqlTypeDescriptor( jdbcPosition );
final JavaTypeDescriptor<?> javaTypeDescriptor = sqlTypeDescriptor.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve( javaTypeDescriptor, sqlTypeDescriptor );
}
final SqlSelectionImpl sqlSelection = new SqlSelectionImpl( valuesArrayPosition, jdbcMapping );
sqlSelectionConsumer.accept( sqlSelection );
return new BasicResult( valuesArrayPosition, explicitName, jdbcMapping.getJavaTypeDescriptor() );
}
}

View File

@ -19,6 +19,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.AttributeConverter;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FlushModeType;
@ -58,6 +59,7 @@ import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.query.results.Builders;
import org.hibernate.query.results.EntityResultBuilder;
import org.hibernate.query.results.LegacyFetchBuilder;
import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultSetMappingImpl;
import org.hibernate.query.spi.AbstractQuery;
import org.hibernate.query.spi.MutableQueryOptions;
@ -475,22 +477,41 @@ public class NativeQueryImpl<R>
@Override
public NativeQueryImplementor<R> addScalar(String columnAlias) {
resultSetMapping.addResultBuilder( Builders.scalar( columnAlias ) );
return registerBuilder( Builders.scalar( columnAlias ) );
}
protected NativeQueryImplementor<R> registerBuilder(ResultBuilder builder) {
resultSetMapping.addResultBuilder( builder );
return this;
}
@Override
public NativeQueryImplementor<R> addScalar(String columnAlias, BasicDomainType type) {
resultSetMapping.addResultBuilder( Builders.scalar( columnAlias, (BasicType<?>) type ) );
return this;
return registerBuilder( Builders.scalar( columnAlias, (BasicType<?>) type ) );
}
@Override
public NativeQueryImplementor<R> addScalar(String columnAlias, Class<?> javaType) {
resultSetMapping.addResultBuilder( Builders.scalar( columnAlias, javaType, getSessionFactory() ) );
return this;
return registerBuilder( Builders.scalar( columnAlias, javaType, getSessionFactory() ) );
}
@Override
public <C> NativeQueryImplementor<R> addScalar(
String columnAlias,
Class<C> relationalJavaType,
AttributeConverter<?, C> converter) {
return registerBuilder( Builders.scalar( columnAlias, relationalJavaType, converter, getSessionFactory() ) );
}
@Override
public <C> NativeQueryImplementor<R> addScalar(
String columnAlias,
Class<C> relationalJavaType,
Class<? extends AttributeConverter<?, C>> converter) {
return registerBuilder( Builders.scalar( columnAlias, relationalJavaType, converter, getSessionFactory() ) );
}
@Override
public EntityResultBuilder addRoot(String tableAlias, String entityName) {
final EntityResultBuilder resultBuilder = Builders.entity(

View File

@ -14,6 +14,7 @@ import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import javax.persistence.AttributeConverter;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Parameter;
@ -58,6 +59,12 @@ public interface NativeQueryImplementor<R> extends QueryImplementor<R>, NativeQu
@Override
NativeQueryImplementor<R> addScalar(String columnAlias, Class<?> javaType);
@Override
<C> NativeQueryImplementor<R> addScalar(String columnAlias, Class<C> relationalJavaType, AttributeConverter<?,C> converter);
@Override
<C> NativeQueryImplementor<R> addScalar(String columnAlias, Class<C> relationalJavaType, Class<? extends AttributeConverter<?,C>> converter);
@Override
EntityResultBuilder addRoot(String tableAlias, String entityName);

View File

@ -306,7 +306,7 @@ public abstract class AbstractStandardBasicType<T>
}
@SuppressWarnings({ "unchecked" })
protected final void nullSafeSet(PreparedStatement st, Object value, int index, WrapperOptions options) throws SQLException {
protected void nullSafeSet(PreparedStatement st, Object value, int index, WrapperOptions options) throws SQLException {
remapSqlTypeDescriptor( options ).getBinder( javaTypeDescriptor ).bind( st, ( T ) value, index, options );
}
@ -430,6 +430,16 @@ public abstract class AbstractStandardBasicType<T>
);
}
@Override
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
boolean[] settable,
SharedSessionContractImplementor session) throws SQLException {
}
@Override
public void nullSafeSet(CallableStatement st, Object value, String name, SharedSessionContractImplementor session) throws SQLException {
nullSafeSet( st, value, name, (WrapperOptions) session );

View File

@ -64,7 +64,7 @@ import org.jboss.logging.Logger;
* @author Steve Ebersole
*/
@SuppressWarnings("unchecked")
public class EnumType<T extends Enum>
public class EnumType<T extends Enum<T>>
implements EnhancedUserType, DynamicParameterizedType, LoggableUserType, TypeConfigurationAware, Serializable {
private static final Logger LOG = CoreLogging.logger( EnumType.class );
@ -74,7 +74,7 @@ public class EnumType<T extends Enum>
private Class<T> enumClass;
private EnumValueConverter enumValueConverter;
private EnumValueConverter<T,?> enumValueConverter;
private TypeConfiguration typeConfiguration;
@ -106,6 +106,8 @@ public class EnumType<T extends Enum>
if ( reader != null ) {
enumClass = reader.getReturnedClass().asSubclass( Enum.class );
final Long columnLength = reader.getColumnLengths()[0];
final boolean isOrdinal;
final javax.persistence.EnumType enumType = getEnumType( reader );
if ( enumType == null ) {
@ -125,28 +127,31 @@ public class EnumType<T extends Enum>
.getJavaTypeDescriptorRegistry()
.getDescriptor( enumClass );
final BasicJavaDescriptor<?> relationalJavaDescriptor = resolveRelationalJavaTypeDescriptor(
reader,
final LocalSqlTypeDescriptorIndicators indicators = new LocalSqlTypeDescriptorIndicators(
enumType,
columnLength,
reader
);
final BasicJavaDescriptor<?> relationalJtd = resolveRelationalJavaTypeDescriptor(
indicators,
enumJavaDescriptor
);
final SqlTypeDescriptor sqlTypeDescriptor = relationalJavaDescriptor.getJdbcRecommendedSqlType(
new LocalSqlTypeDescriptorIndicators( enumType, reader )
);
final SqlTypeDescriptor sqlTypeDescriptor = relationalJtd.getJdbcRecommendedSqlType( indicators );
if ( isOrdinal ) {
this.enumValueConverter = new OrdinalEnumValueConverter(
enumJavaDescriptor,
sqlTypeDescriptor,
relationalJavaDescriptor
relationalJtd
);
}
else {
this.enumValueConverter = new NamedEnumValueConverter(
enumJavaDescriptor,
sqlTypeDescriptor,
relationalJavaDescriptor
relationalJtd
);
}
}
@ -172,10 +177,9 @@ public class EnumType<T extends Enum>
}
private BasicJavaDescriptor<?> resolveRelationalJavaTypeDescriptor(
ParameterType reader,
javax.persistence.EnumType enumType, EnumJavaTypeDescriptor enumJavaDescriptor) {
return enumJavaDescriptor.getJdbcRecommendedSqlType( new LocalSqlTypeDescriptorIndicators( enumType, reader ) )
.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
LocalSqlTypeDescriptorIndicators indicators,
EnumJavaTypeDescriptor<?> enumJavaDescriptor) {
return enumJavaDescriptor.getJdbcRecommendedSqlType( indicators ).getJdbcRecommendedJavaTypeMapping( typeConfiguration );
}
private javax.persistence.EnumType getEnumType(ParameterType reader) {
@ -204,21 +208,26 @@ public class EnumType<T extends Enum>
return null;
}
private EnumValueConverter interpretParameters(Properties parameters) {
final EnumJavaTypeDescriptor enumJavaDescriptor = (EnumJavaTypeDescriptor) typeConfiguration
private EnumValueConverter<T,?> interpretParameters(Properties parameters) {
final EnumJavaTypeDescriptor<?> enumJavaDescriptor = (EnumJavaTypeDescriptor<?>) typeConfiguration
.getJavaTypeDescriptorRegistry()
.getDescriptor( enumClass );
final ParameterType reader = (ParameterType) parameters.get( PARAMETER_TYPE );
final javax.persistence.EnumType enumType = getEnumType( reader );
final LocalSqlTypeDescriptorIndicators localIndicators = new LocalSqlTypeDescriptorIndicators( enumType, reader );
final BasicJavaDescriptor stringJavaDescriptor = (BasicJavaDescriptor) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class );
final BasicJavaDescriptor integerJavaDescriptor = (BasicJavaDescriptor) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class );
final LocalSqlTypeDescriptorIndicators localIndicators = new LocalSqlTypeDescriptorIndicators(
enumType,
reader.getColumnLengths()[0],
reader
);
final BasicJavaDescriptor<?> stringJavaDescriptor = (BasicJavaDescriptor<?>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class );
final BasicJavaDescriptor<?> integerJavaDescriptor = (BasicJavaDescriptor<?>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Integer.class );
if ( parameters.containsKey( NAMED ) ) {
final boolean useNamed = ConfigurationHelper.getBoolean( NAMED, parameters );
if ( useNamed ) {
//noinspection rawtypes
return new NamedEnumValueConverter(
enumJavaDescriptor,
stringJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
@ -226,6 +235,7 @@ public class EnumType<T extends Enum>
);
}
else {
//noinspection rawtypes
return new OrdinalEnumValueConverter(
enumJavaDescriptor,
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
@ -237,6 +247,7 @@ public class EnumType<T extends Enum>
if ( parameters.containsKey( TYPE ) ) {
final int type = Integer.decode( (String) parameters.get( TYPE ) );
if ( isNumericType( type ) ) {
//noinspection rawtypes
return new OrdinalEnumValueConverter(
enumJavaDescriptor,
integerJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
@ -244,6 +255,7 @@ public class EnumType<T extends Enum>
);
}
else if ( isCharacterType( type ) ) {
//noinspection rawtypes
return new NamedEnumValueConverter(
enumJavaDescriptor,
stringJavaDescriptor.getJdbcRecommendedSqlType( localIndicators ),
@ -336,7 +348,7 @@ public class EnumType<T extends Enum>
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
verifyConfigured();
enumValueConverter.writeValue( st, (Enum) value, index, session );
enumValueConverter.writeValue( st, (T) value, index, session );
}
@Override
@ -383,7 +395,7 @@ public class EnumType<T extends Enum>
@Override
public String toXMLString(Object value) {
verifyConfigured();
return (String) enumValueConverter.getDomainJavaDescriptor().unwrap( (Enum) value, String.class, null );
return enumValueConverter.getDomainJavaDescriptor().unwrap( (T) value, String.class, null );
}
@Override
@ -396,7 +408,7 @@ public class EnumType<T extends Enum>
@Override
public String toLoggableString(Object value, SessionFactoryImplementor factory) {
verifyConfigured();
return enumValueConverter.getDomainJavaDescriptor().toString( (Enum) value );
return enumValueConverter.getDomainJavaDescriptor().toString( (T) value );
}
public boolean isOrdinal() {
@ -406,10 +418,12 @@ public class EnumType<T extends Enum>
private class LocalSqlTypeDescriptorIndicators implements SqlTypeDescriptorIndicators {
private final javax.persistence.EnumType enumType;
private final Long columnLength;
private final ParameterType reader;
public LocalSqlTypeDescriptorIndicators(javax.persistence.EnumType enumType, ParameterType reader) {
public LocalSqlTypeDescriptorIndicators(javax.persistence.EnumType enumType, Long columnLength, ParameterType reader) {
this.enumType = enumType;
this.columnLength = columnLength;
this.reader = reader;
}
@ -444,5 +458,10 @@ public class EnumType<T extends Enum>
return false;
}
@Override
public long getColumnLength() {
return columnLength == null ? NO_COLUMN_LENGTH : columnLength;
}
}
}

View File

@ -6,9 +6,17 @@
*/
package org.hibernate.type.descriptor.converter;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.persistence.AttributeConverter;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
@ -24,13 +32,14 @@ import org.jboss.logging.Logger;
public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStandardBasicType<T> {
private static final Logger log = Logger.getLogger( AttributeConverterTypeAdapter.class );
@SuppressWarnings("unused")
public static final String NAME_PREFIX = ConverterDescriptor.TYPE_NAME_PREFIX;
private final String name;
private final String description;
private final Class modelType;
private final Class jdbcType;
private final JavaTypeDescriptor<T> domainJtd;
private final JavaTypeDescriptor<?> relationalJtd;
private final JpaAttributeConverter<? extends T,?> attributeConverter;
private final MutabilityPlan<T> mutabilityPlan;
@ -40,24 +49,22 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
String name,
String description,
JpaAttributeConverter<? extends T,?> attributeConverter,
SqlTypeDescriptor sqlTypeDescriptorAdapter,
Class modelType,
Class jdbcType,
JavaTypeDescriptor<T> entityAttributeJavaTypeDescriptor) {
super( sqlTypeDescriptorAdapter, entityAttributeJavaTypeDescriptor );
SqlTypeDescriptor std,
JavaTypeDescriptor<?> relationalJtd,
JavaTypeDescriptor<T> domainJtd) {
//noinspection rawtypes
super( std, (JavaTypeDescriptor) relationalJtd );
this.name = name;
this.description = description;
this.modelType = modelType;
this.jdbcType = jdbcType;
this.domainJtd = domainJtd;
this.relationalJtd = relationalJtd;
this.attributeConverter = attributeConverter;
this.mutabilityPlan = entityAttributeJavaTypeDescriptor.getMutabilityPlan().isMutable()
this.mutabilityPlan = domainJtd.getMutabilityPlan().isMutable()
? new AttributeConverterMutabilityPlanImpl<>( attributeConverter )
: ImmutableMutabilityPlan.INSTANCE;
log.debugf( "Created AttributeConverterTypeAdapter -> %s", name );
// throw new UnsupportedOperationException( );
}
@Override
@ -65,18 +72,42 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
return name;
}
public Class getModelType() {
return modelType;
public JavaTypeDescriptor<T> getDomainJtd() {
return domainJtd;
}
public Class getJdbcType() {
return jdbcType;
public JavaTypeDescriptor<?> getRelationalJtd() {
return relationalJtd;
}
public JpaAttributeConverter<? extends T,?> getAttributeConverter() {
return attributeConverter;
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void nullSafeSet(
CallableStatement st,
Object value,
String name,
SharedSessionContractImplementor session) throws SQLException {
final AttributeConverter converter = attributeConverter.getConverterBean().getBeanInstance();
final Object converted = converter.convertToDatabaseColumn( value );
super.nullSafeSet( st, converted, name, session );
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
protected void nullSafeSet(PreparedStatement st, Object value, int index, WrapperOptions options) throws SQLException {
final AttributeConverter converter = attributeConverter.getConverterBean().getBeanInstance();
final Object converted = converter.convertToDatabaseColumn( value );
super.nullSafeSet( st, converted, index, options );
}
@Override
protected MutabilityPlan<T> getMutabilityPlan() {
return mutabilityPlan;

View File

@ -0,0 +1,65 @@
/*
* 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.converter;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.persistence.AttributeConverter;
import javax.persistence.PersistenceException;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class ConvertedValueExtractor<O,R> implements ValueExtractor<O> {
private static final Logger log = Logger.getLogger( ConvertedValueExtractor.class );
private final ValueExtractor<R> relationalExtractor;
private final AttributeConverter<O,R> converter;
public ConvertedValueExtractor(
ValueExtractor<R> relationalExtractor,
AttributeConverter<O, R> converter) {
this.relationalExtractor = relationalExtractor;
this.converter = converter;
}
@Override
public O extract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
return doConversion( relationalExtractor.extract( rs, paramIndex, options ) );
}
@Override
public O extract(CallableStatement statement, int paramIndex, WrapperOptions options) throws SQLException {
return doConversion( relationalExtractor.extract( statement, paramIndex, options ) );
}
@Override
public O extract(CallableStatement statement, String paramName, WrapperOptions options) throws SQLException {
return doConversion( relationalExtractor.extract( statement, paramName, options ) );
}
private O doConversion(R extractedValue) {
try {
O convertedValue = converter.convertToEntityAttribute( extractedValue );
log.debugf( "Converted value on extraction: %s -> %s", extractedValue, convertedValue );
return convertedValue;
}
catch (PersistenceException pe) {
throw pe;
}
catch (RuntimeException re) {
throw new PersistenceException( "Error attempting to apply AttributeConverter", re );
}
}
}

View File

@ -5,10 +5,15 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.type.descriptor.java;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.spi.Primitive;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Character} handling.
@ -25,6 +30,7 @@ public class CharacterTypeDescriptor extends AbstractTypeDescriptor<Character> i
public String toString(Character value) {
return value.toString();
}
@Override
public Character fromString(String string) {
if ( string.length() != 1 ) {

View File

@ -31,6 +31,12 @@ public class EnumJavaTypeDescriptor<T extends Enum<T>> extends AbstractTypeDescr
@Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(SqlTypeDescriptorIndicators context) {
if ( context.getEnumeratedType() != null && context.getEnumeratedType() == EnumType.STRING ) {
if ( context.getColumnLength() == 1 ) {
return context.isNationalized()
? context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.NCHAR )
: context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.CHAR );
}
return context.isNationalized()
? context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.NVARCHAR )
: context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.VARCHAR );

View File

@ -54,4 +54,9 @@ public class JavaTypeDescriptorBasicAdaptor<T> extends AbstractTypeDescriptor<T>
"Wrap strategy not known for this Java type : " + getJavaType().getName()
);
}
@Override
public String toString() {
return "JavaTypeDescriptorBasicAdaptor(" + getJavaType().getName() + ")";
}
}

View File

@ -7,6 +7,9 @@
package org.hibernate.type.descriptor.sql;
import java.sql.Types;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for {@link Types#CHAR CHAR} handling.
*
@ -27,4 +30,9 @@ public class CharTypeDescriptor extends VarcharTypeDescriptor {
public int getSqlType() {
return Types.CHAR;
}
@Override
public <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return super.getJdbcRecommendedJavaTypeMapping( typeConfiguration );
}
}

View File

@ -21,6 +21,8 @@ import org.hibernate.type.spi.TypeConfiguration;
* @author Steve Ebersole
*/
public interface SqlTypeDescriptorIndicators {
int NO_COLUMN_LENGTH = -1;
/**
* Was nationalized character datatype requested for the given Java type?
*
@ -65,6 +67,13 @@ public interface SqlTypeDescriptorIndicators {
return Types.BOOLEAN;
}
/**
* Useful for resolutions based on column length. E.g. choosing between a VARCHAR (String) and a CHAR(1) (Character/char)
*/
default long getColumnLength() {
return NO_COLUMN_LENGTH;
}
/**
* Provides access to the TypeConfiguration for access to various type-system registries.
*/

View File

@ -46,5 +46,6 @@ public interface DynamicParameterizedType extends ParameterizedType {
String[] getColumns();
Long[] getColumnLengths();
}
}

View File

@ -8,6 +8,9 @@ package org.hibernate.orm.test.metamodel.mapping;
import java.sql.Statement;
import java.sql.Types;
import javax.persistence.AttributeConverter;
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
@ -25,6 +28,7 @@ import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.hamcrest.CollectionMatchers;
@ -40,6 +44,7 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
/**
@ -60,9 +65,10 @@ public class SmokeTests {
.getEntityDescriptor( SimpleEntity.class );
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
assert Integer.class.equals( identifierMapping.getMappedTypeDescriptor()
.getMappedJavaTypeDescriptor()
.getJavaType() );
assertThat(
identifierMapping.getMappedTypeDescriptor().getMappedJavaTypeDescriptor().getJavaType(),
sameInstance( Integer.class )
);
{
final ModelPart namePart = entityDescriptor.findSubPart( "name" );
@ -105,6 +111,23 @@ public class SmokeTests {
assertThat( attrMapping.getJdbcMapping().getSqlTypeDescriptor().getJdbcTypeCode(), is( Types.VARCHAR ) );
}
{
final ModelPart part = entityDescriptor.findSubPart( "gender3" );
assert part instanceof BasicValuedSingularAttributeMapping;
final BasicValuedSingularAttributeMapping attrMapping = (BasicValuedSingularAttributeMapping) part;
assert "mapping_simple_entity".equals( attrMapping.getContainingTableExpression() );
assert "gender3".equals( attrMapping.getMappedColumnExpression() );
assertThat( attrMapping.getJavaTypeDescriptor().getJavaType(), equalTo( Gender.class ) );
final BasicValueConverter valueConverter = attrMapping.getValueConverter();
assertThat( valueConverter, instanceOf( JpaAttributeConverter.class ) );
assertThat( valueConverter.getDomainJavaDescriptor(), is( attrMapping.getJavaTypeDescriptor() ) );
assertThat( valueConverter.getRelationalJavaDescriptor().getJavaType(), equalTo( Character.class ) );
assertThat( attrMapping.getJdbcMapping().getSqlTypeDescriptor().getJdbcTypeCode(), is( Types.CHAR ) );
}
{
final ModelPart part = entityDescriptor.findSubPart( "component" );
assert part instanceof EmbeddedAttributeMapping;
@ -223,6 +246,7 @@ public class SmokeTests {
private String name;
private Gender gender;
private Gender gender2;
private Gender gender3;
private Component component;
@Id
@ -260,6 +284,16 @@ public class SmokeTests {
this.gender2 = gender2;
}
@Convert( converter = GenderConverter.class )
@Column( length = 1 )
public Gender getGender3() {
return gender3;
}
public void setGender3(Gender gender3) {
this.gender3 = gender3;
}
@Embedded
public Component getComponent() {
return component;
@ -270,6 +304,35 @@ public class SmokeTests {
}
}
static class GenderConverter implements AttributeConverter<Gender,Character> {
@Override
public Character convertToDatabaseColumn(Gender attribute) {
if ( attribute == null ) {
return null;
}
if ( attribute == Gender.MALE ) {
return 'M';
}
return 'F';
}
@Override
public Gender convertToEntityAttribute(Character dbData) {
if ( dbData == null ) {
return null;
}
if ( 'M' == dbData ) {
return Gender.MALE;
}
return Gender.FEMALE;
}
}
@Embeddable
static class SubComponent {
private String subAttribute1;

View File

@ -6,51 +6,70 @@
*/
package org.hibernate.orm.test.query.sql;
import java.time.LocalDate;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Types;
import java.time.Instant;
import java.util.List;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.BasicValuedSingularAttributeMapping;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.orm.test.metamodel.mapping.SmokeTests;
import org.hibernate.query.sql.spi.NativeQueryImplementor;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.domain.contacts.Contact;
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.hamcrest.CustomMatcher;
import org.hamcrest.Matcher;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
@DomainModel(
standardModels = StandardDomainModel.CONTACTS
standardModels = StandardDomainModel.GAMBIT
)
@SessionFactory
public class NativeQueryScalarTests {
public static final String STRING_VALUE = "a string value";
public static final String URL_STRING = "http://hibernate.org";
@Test
public void fullyImplicitTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final String sql = "select gender, first, last, id from contacts";
final String sql = "select theString, theInteger, id from EntityOfBasics";
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( Object[].class ) );
final Object[] values = (Object[]) result;
assertThat( values.length, is(4 ) );
assertThat( values.length, is(3 ) );
assertThat( ( (Number) values[0] ).intValue(), is( Contact.Gender.OTHER.ordinal() ) );
assertThat( values[1], is( "My First" ) );
assertThat( values[2], is( "Contact" ) );
assertThat( values[3], is( 1 ) );
assertThat( values[ 0 ], is( STRING_VALUE ) );
assertThat( values[ 1 ], is( 2 ) );
assertThat( values[ 2 ], is( 1 ) );
}
);
}
@ -58,71 +77,192 @@ public class NativeQueryScalarTests {
public void explicitOrderTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final String sql = "select gender, first, last, id from contacts";
final String sql = "select theString, theInteger, id from EntityOfBasics";
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
// notice the reverse order from the select clause
query.addScalar( "id" );
query.addScalar( "last" );
query.addScalar( "first" );
query.addScalar( "gender" );
query.addScalar( "theInteger" );
query.addScalar( "theString" );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( Object[].class ) );
final Object[] values = (Object[]) result;
assertThat( values.length, is(4 ) );
assertThat( values.length, is(3 ) );
assertThat( values[0], is( 1 ) );
assertThat( values[1], is( "Contact" ) );
assertThat( values[2], is( "My First" ) );
assertThat( ( (Number) values[3] ).intValue(), is( Contact.Gender.OTHER.ordinal() ) );
assertThat( values[ 0 ], is( 1 ) );
assertThat( values[ 1 ], is( 2 ) );
assertThat( values[ 2 ], is( STRING_VALUE ) );
}
);
}
@Test
// @FailureExpected( reason = "Explicit type support not working atm" )
public void explicitEnumTypeTest(SessionFactoryScope scope) {
final String sql = "select id, gender, ordinal_gender from EntityOfBasics";
// first, without explicit typing
scope.inTransaction(
session -> {
final String sql = "select gender, first, last, id from contacts";
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
// notice the reverse order from the select clause
query.addScalar( "id" );
query.addScalar( "last" );
query.addScalar( "first" );
query.addScalar( "gender", Contact.Gender.class );
query.addScalar( "gender" );
query.addScalar( "ordinal_gender" );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( Object[].class ) );
final Object[] values = (Object[]) result;
assertThat( values.length, is(4 ) );
assertThat( values.length, is(3 ) );
assertThat( values[0], is( 1 ) );
assertThat( values[1], is( "Contact" ) );
assertThat( values[2], is( "My First" ) );
assertThat( values[3], is( Contact.Gender.OTHER ) );
assertThat( values[ 0 ], is( 1 ) );
assertThat( values[ 1 ], is( "MALE" ) );
assertThat( values[ 2 ], matchesOrdinal( EntityOfBasics.Gender.FEMALE ) );
}
);
// then using explicit typing
scope.inTransaction(
session -> {
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
query.addScalar( "id" );
query.addScalar( "gender", EntityOfBasics.Gender.class );
query.addScalar( "ordinal_gender", EntityOfBasics.Gender.class );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( Object[].class ) );
final Object[] values = (Object[]) result;
assertThat( values.length, is(3 ) );
assertThat( values[ 0 ], is( 1 ) );
assertThat( values[ 1 ], is( EntityOfBasics.Gender.MALE ) );
assertThat( values[ 2 ], is( EntityOfBasics.Gender.FEMALE ) );
}
);
}
@Test
public void explicitConversionTest(SessionFactoryScope scope) {
final String sql = "select converted_gender from EntityOfBasics";
// Control
scope.inTransaction(
session -> {
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( String.class ) );
assertThat( result, is( "O" ) );
}
);
// Converter instance
scope.inTransaction(
session -> {
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
query.addScalar(
"converted_gender",
Character.class,
new EntityOfBasics.GenderConverter()
);
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( EntityOfBasics.Gender.class ) );
assertThat( result, is( EntityOfBasics.Gender.OTHER ) );
}
);
// Converter class
scope.inTransaction(
session -> {
final NativeQueryImplementor<?> query = session.createNativeQuery( sql );
query.addScalar(
"converted_gender",
Character.class,
EntityOfBasics.GenderConverter.class
);
final List<?> results = query.list();
assertThat( results.size(), is( 1 ) );
final Object result = results.get( 0 );
assertThat( result, instanceOf( EntityOfBasics.Gender.class ) );
assertThat( result, is( EntityOfBasics.Gender.OTHER ) );
}
);
}
private Matcher matchesOrdinal(Enum enumValue) {
return new CustomMatcher<Object>( "Enum ordinal value" ) {
@Override
public boolean matches(Object item) {
return ( (Number) item ).intValue() == enumValue.ordinal();
}
};
}
@BeforeAll
public void verifyModel(SessionFactoryScope scope) {
final EntityMappingType entityDescriptor = scope.getSessionFactory()
.getRuntimeMetamodels()
.getEntityMappingType( EntityOfBasics.class );
final ModelPart part = entityDescriptor.findSubPart( "convertedGender", null );
assertThat( part, instanceOf( BasicValuedSingularAttributeMapping.class ) );
final BasicValuedSingularAttributeMapping attrMapping = (BasicValuedSingularAttributeMapping) part;
assertThat( attrMapping.getJavaTypeDescriptor().getJavaType(), equalTo( EntityOfBasics.Gender.class ) );
final BasicValueConverter valueConverter = attrMapping.getValueConverter();
assertThat( valueConverter, instanceOf( JpaAttributeConverter.class ) );
assertThat( valueConverter.getDomainJavaDescriptor(), is( attrMapping.getJavaTypeDescriptor() ) );
assertThat( valueConverter.getRelationalJavaDescriptor().getJavaType(), equalTo( Character.class ) );
assertThat( attrMapping.getJdbcMapping().getSqlTypeDescriptor().getJdbcTypeCode(), is( Types.CHAR ) );
}
@BeforeEach
public void prepareData(SessionFactoryScope scope) {
public void prepareData(SessionFactoryScope scope) throws MalformedURLException {
final URL url = new URL( URL_STRING );
scope.inTransaction(
session -> {
session.persist(
new Contact(
1,
new Contact.Name( "My First", "Contact"),
Contact.Gender.OTHER,
LocalDate.of(1990,4,18)
)
final EntityOfBasics entityOfBasics = new EntityOfBasics( 1 );
entityOfBasics.setTheString( STRING_VALUE );
entityOfBasics.setTheInteger( 2 );
entityOfBasics.setGender( EntityOfBasics.Gender.MALE );
entityOfBasics.setOrdinalGender( EntityOfBasics.Gender.FEMALE );
entityOfBasics.setConvertedGender( EntityOfBasics.Gender.OTHER );
entityOfBasics.setTheUrl( url );
entityOfBasics.setTheInstant( Instant.EPOCH );
session.persist( entityOfBasics );
}
);
scope.inTransaction(
session -> {
final EntityOfBasics entity = session.get( EntityOfBasics.class, 1 );
assertThat( entity, notNullValue() );
}
);
}
@ -130,7 +270,7 @@ public class NativeQueryScalarTests {
@AfterEach
public void cleanUpData(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.createQuery( "delete Contact" ).executeUpdate()
session -> session.createQuery( "delete EntityOfBasics" ).executeUpdate()
);
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.testing.orm.domain.gambit;
import java.net.URL;
import java.sql.Clob;
import java.sql.Types;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
@ -26,6 +27,8 @@ import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.SqlTypeCode;
/**
* @author Steve Ebersole
*/
@ -34,7 +37,8 @@ public class EntityOfBasics {
public enum Gender {
MALE,
FEMALE
FEMALE,
OTHER
}
private Integer id;
@ -60,6 +64,13 @@ public class EntityOfBasics {
private ZonedDateTime theZonedDateTime;
private OffsetDateTime theOffsetDateTime;
public EntityOfBasics() {
}
public EntityOfBasics(Integer id) {
this.id = id;
}
@Id
public Integer getId() {
return id;
@ -127,7 +138,8 @@ public class EntityOfBasics {
}
@Convert( converter = GenderConverter.class )
@Column(name = "converted_gender")
@Column(name = "converted_gender", length = 1)
@SqlTypeCode( Types.CHAR )
public Gender getConvertedGender() {
return convertedGender;
}
@ -244,6 +256,10 @@ public class EntityOfBasics {
return null;
}
if ( attribute == Gender.OTHER ) {
return 'O';
}
if ( attribute == Gender.MALE ) {
return 'M';
}
@ -257,6 +273,10 @@ public class EntityOfBasics {
return null;
}
if ( 'O' == dbData ) {
return Gender.OTHER;
}
if ( 'M' == dbData ) {
return Gender.MALE;
}