HHH-17859, HHH-17858 function() and column() functions

This commit is contained in:
Gavin King 2024-03-16 23:47:54 +01:00
parent 1ba67c2de9
commit 6441c60255
11 changed files with 192 additions and 31 deletions

View File

@ -11,7 +11,6 @@ import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.cte.CteMaterialization; import org.hibernate.sql.ast.tree.cte.CteMaterialization;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.expression.Any; import org.hibernate.sql.ast.tree.expression.Any;
import org.hibernate.sql.ast.tree.expression.Every; import org.hibernate.sql.ast.tree.expression.Every;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;

View File

@ -158,6 +158,7 @@ BY : [bB] [yY];
CASE : [cC] [aA] [sS] [eE]; CASE : [cC] [aA] [sS] [eE];
CAST : [cC] [aA] [sS] [tT]; CAST : [cC] [aA] [sS] [tT];
COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE]; COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE];
COLUMN : [cC] [oO] [lL] [uU] [mM] [nN];
CONFLICT : [cC] [oO] [nN] [fF] [lL] [iI] [cC] [tT]; CONFLICT : [cC] [oO] [nN] [fF] [lL] [iI] [cC] [tT];
CONSTRAINT : [cC] [oO] [nN] [sS] [tT] [rR] [aA] [iI] [nN] [tT]; CONSTRAINT : [cC] [oO] [nN] [sS] [tT] [rR] [aA] [iI] [nN] [tT];
COUNT : [cC] [oO] [uU] [nN] [tT]; COUNT : [cC] [oO] [uU] [nN] [tT];

View File

@ -1083,6 +1083,7 @@ function
| collectionAggregateFunction | collectionAggregateFunction
| collectionFunctionMisuse | collectionFunctionMisuse
| jpaNonstandardFunction | jpaNonstandardFunction
| columnFunction
| genericFunction | genericFunction
; ;
@ -1090,7 +1091,7 @@ function
* A syntax for calling user-defined or native database functions, required by JPQL * A syntax for calling user-defined or native database functions, required by JPQL
*/ */
jpaNonstandardFunction jpaNonstandardFunction
: FUNCTION LEFT_PAREN jpaNonstandardFunctionName (COMMA genericFunctionArguments)? RIGHT_PAREN : FUNCTION LEFT_PAREN jpaNonstandardFunctionName (AS castTarget)? (COMMA genericFunctionArguments)? RIGHT_PAREN
; ;
/** /**
@ -1098,6 +1099,11 @@ jpaNonstandardFunction
*/ */
jpaNonstandardFunctionName jpaNonstandardFunctionName
: STRING_LITERAL : STRING_LITERAL
| identifier
;
columnFunction
: COLUMN LEFT_PAREN path DOT jpaNonstandardFunctionName (AS castTarget)? RIGHT_PAREN
; ;
/** /**
@ -1618,6 +1624,7 @@ rollup
| CASE | CASE
| CAST | CAST
| COLLATE | COLLATE
| COLUMN
| CONFLICT | CONFLICT
| CONSTRAINT | CONSTRAINT
| COUNT | COUNT

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.dialect.function;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.hql.HqlInterpretationException;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.update.Assignable;
import org.hibernate.type.BasicType;
import org.hibernate.type.JavaObjectType;
import java.util.List;
/**
* @author Gavin King
*/
public class SqlColumn extends AbstractSqmSelfRenderingFunctionDescriptor {
private final String columnName;
public SqlColumn(String columnName, BasicType<?> type) {
super(
"column",
StandardArgumentsValidators.min( 1 ),
StandardFunctionReturnTypeResolvers.invariant( type == null ? JavaObjectType.INSTANCE : type ),
null
);
this.columnName = columnName;
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> arguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final SqlAstNode sqlAstNode = arguments.get(0);
final ColumnReference reference;
if ( sqlAstNode instanceof Assignable ) {
final Assignable assignable = (Assignable) sqlAstNode;
reference = assignable.getColumnReferences().get(0);
}
else if ( sqlAstNode instanceof Expression ) {
final Expression expression = (Expression) sqlAstNode;
reference = expression.getColumnReference();
}
else {
throw new HqlInterpretationException( "path did not map to a column" );
}
sqlAppender.appendSql( reference.getQualifier() );
sqlAppender.appendSql( '.' );
sqlAppender.appendSql( columnName );
}
}

View File

@ -62,7 +62,7 @@ public interface ConnectionProvider extends Service, Wrapped {
* <p> * <p>
* Typically, this is only true in managed environments where a container tracks connections * Typically, this is only true in managed environments where a container tracks connections
* by transaction or thread. * by transaction or thread.
* * <p>
* Note that JTA semantic depends on the fact that the underlying connection provider does * Note that JTA semantic depends on the fact that the underlying connection provider does
* support aggressive release. * support aggressive release.
* *

View File

@ -35,6 +35,7 @@ import java.util.Set;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.dialect.function.SqlColumn;
import org.hibernate.grammars.hql.HqlLexer; import org.hibernate.grammars.hql.HqlLexer;
import org.hibernate.grammars.hql.HqlParser; import org.hibernate.grammars.hql.HqlParser;
import org.hibernate.grammars.hql.HqlParserBaseVisitor; import org.hibernate.grammars.hql.HqlParserBaseVisitor;
@ -58,7 +59,6 @@ import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.query.NullPrecedence; import org.hibernate.query.NullPrecedence;
import org.hibernate.query.ParameterLabelException; import org.hibernate.query.ParameterLabelException;
import org.hibernate.query.PathException; import org.hibernate.query.PathException;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.SemanticException; import org.hibernate.query.SemanticException;
import org.hibernate.query.SortDirection; import org.hibernate.query.SortDirection;
import org.hibernate.query.SyntaxException; import org.hibernate.query.SyntaxException;
@ -277,6 +277,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
private static final Logger log = Logger.getLogger( SemanticQueryBuilder.class ); private static final Logger log = Logger.getLogger( SemanticQueryBuilder.class );
private static final Set<String> JPA_STANDARD_FUNCTIONS; private static final Set<String> JPA_STANDARD_FUNCTIONS;
private static final BasicTypeImpl<Object> OBJECT_BASIC_TYPE =
new BasicTypeImpl<>( new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE );
static { static {
final Set<String> jpaStandardFunctions = new HashSet<>(); final Set<String> jpaStandardFunctions = new HashSet<>();
// Extracted from the BNF in JPA spec 4.14. // Extracted from the BNF in JPA spec 4.14.
@ -3910,43 +3913,64 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Functions // Functions
@Override private String toName(HqlParser.JpaNonstandardFunctionNameContext ctx) {
public SqmExpression<?> visitJpaNonstandardFunction(HqlParser.JpaNonstandardFunctionContext ctx) { return ctx.STRING_LITERAL() == null
final String functionName = unquoteStringLiteral( ctx.jpaNonstandardFunctionName().getText() ).toLowerCase(); ? ctx.identifier().getText().toLowerCase()
final List<SqmTypedNode<?>> functionArguments; : unquoteStringLiteral( ctx.STRING_LITERAL().getText() ).toLowerCase();
if ( ctx.getChildCount() > 4 ) {
//noinspection unchecked
functionArguments = (List<SqmTypedNode<?>>) ctx.genericFunctionArguments().accept( this );
}
else {
functionArguments = emptyList();
} }
@Override
public SqmExpression<?> visitJpaNonstandardFunction(HqlParser.JpaNonstandardFunctionContext ctx) {
final String functionName = toName( ctx.jpaNonstandardFunctionName() );
final HqlParser.GenericFunctionArgumentsContext argumentsContext = ctx.genericFunctionArguments();
@SuppressWarnings("unchecked")
final List<SqmTypedNode<?>> functionArguments =
argumentsContext == null
? emptyList()
: (List<SqmTypedNode<?>>) argumentsContext.accept(this);
final BasicType<?> returnableType = returnType( ctx.castTarget() );
SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( functionName ); SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( functionName );
if (functionTemplate == null) { if ( functionTemplate == null ) {
functionTemplate = new NamedSqmFunctionDescriptor( functionTemplate = new NamedSqmFunctionDescriptor(
functionName, functionName,
true, true,
null, null,
StandardFunctionReturnTypeResolvers.invariant( StandardFunctionReturnTypeResolvers.invariant(returnableType),
new BasicTypeImpl<>(
new UnknownBasicJavaType<>( Object.class ),
ObjectJdbcType.INSTANCE
)
),
null null
); );
} }
return functionTemplate.generateSqmExpression( return functionTemplate.generateSqmExpression(
functionArguments, functionArguments,
null, returnableType,
creationContext.getQueryEngine() creationContext.getQueryEngine()
); );
} }
@Override
public SqmExpression<?> visitColumnFunction(HqlParser.ColumnFunctionContext ctx) {
final String columnName = toName( ctx.jpaNonstandardFunctionName() );
final SemanticPathPart semanticPathPart = visitPath( ctx.path() );
final BasicType<?> resultType = returnType( ctx.castTarget() );
return new SqlColumn( columnName, resultType ).generateSqmExpression(
(SqmTypedNode<?>) semanticPathPart,
resultType,
creationContext.getQueryEngine()
);
}
private BasicType<?> returnType(HqlParser.CastTargetContext castTarget) {
if ( castTarget == null ) {
return OBJECT_BASIC_TYPE;
}
else {
return (BasicType<?>) visitCastTarget( castTarget ).getType();
}
}
@Override @Override
public String visitGenericFunctionName(HqlParser.GenericFunctionNameContext ctx) { public String visitGenericFunctionName(HqlParser.GenericFunctionNameContext ctx) {
StringBuilder functionName = new StringBuilder( visitIdentifier( ctx.simplePath().identifier() ) ); final StringBuilder functionName = new StringBuilder( visitIdentifier( ctx.simplePath().identifier() ) );
for ( HqlParser.SimplePathElementContext sp: ctx.simplePath().simplePathElement() ) { for ( HqlParser.SimplePathElementContext sp: ctx.simplePath().simplePathElement() ) {
// allow function names of form foo.bar to be located in the registry // allow function names of form foo.bar to be located in the registry
functionName.append('.').append( visitIdentifier( sp.identifier() ) ); functionName.append('.').append( visitIdentifier( sp.identifier() ) );
@ -4474,7 +4498,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final Integer scale = secondArg == null ? null : Integer.valueOf( secondArg.getText() ); final Integer scale = secondArg == null ? null : Integer.valueOf( secondArg.getText() );
return new SqmCastTarget<>( return new SqmCastTarget<>(
(ReturnableType<?>)
creationContext.getTypeConfiguration() creationContext.getTypeConfiguration()
.resolveCastTargetType( targetName ), .resolveCastTargetType( targetName ),
//TODO: is there some way to interpret as length vs precision/scale here at this point? //TODO: is there some way to interpret as length vs precision/scale here at this point?

View File

@ -41,7 +41,6 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionFactoryRegistry; import org.hibernate.internal.SessionFactoryRegistry;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.model.domain.BasicDomainType;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
@ -468,7 +467,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override @Override
public <X, T> SqmExpression<X> cast(JpaExpression<T> expression, Class<X> castTargetJavaType) { public <X, T> SqmExpression<X> cast(JpaExpression<T> expression, Class<X> castTargetJavaType) {
final BasicDomainType<X> type = getTypeConfiguration().standardBasicTypeForJavaType( castTargetJavaType ); final BasicType<X> type = getTypeConfiguration().standardBasicTypeForJavaType( castTargetJavaType );
return getFunctionDescriptor( "cast" ).generateSqmExpression( return getFunctionDescriptor( "cast" ).generateSqmExpression(
asList( (SqmTypedNode<?>) expression, new SqmCastTarget<>( type, this ) ), asList( (SqmTypedNode<?>) expression, new SqmCastTarget<>( type, this ) ),
type, type,

View File

@ -27,6 +27,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Function; import java.util.function.Function;
@ -323,7 +324,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
* <p> * <p>
* The type names are not case-sensitive. * The type names are not case-sensitive.
*/ */
public BasicValuedMapping resolveCastTargetType(String name) { public BasicType<?> resolveCastTargetType(String name) {
switch ( name.toLowerCase() ) { switch ( name.toLowerCase() ) {
case "string": return getBasicTypeForJavaType( String.class ); case "string": return getBasicTypeForJavaType( String.class );
case "character": return getBasicTypeForJavaType( Character.class ); case "character": return getBasicTypeForJavaType( Character.class );
@ -346,6 +347,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
case "duration": return getBasicTypeForJavaType( Duration.class ); case "duration": return getBasicTypeForJavaType( Duration.class );
case "instant": return getBasicTypeForJavaType( Instant.class ); case "instant": return getBasicTypeForJavaType( Instant.class );
case "binary": return getBasicTypeForJavaType( byte[].class ); case "binary": return getBasicTypeForJavaType( byte[].class );
case "uuid": return getBasicTypeForJavaType( UUID.class );
//this one is very fragile ... works well for BIT or BOOLEAN columns only //this one is very fragile ... works well for BIT or BOOLEAN columns only
//works OK, I suppose, for integer columns, but not at all for char columns //works OK, I suppose, for integer columns, but not at all for char columns
case "boolean": return getBasicTypeForJavaType( Boolean.class ); case "boolean": return getBasicTypeForJavaType( Boolean.class );
@ -353,7 +355,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
case "yesno": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.YES_NO.getName() ); case "yesno": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.YES_NO.getName() );
case "numericboolean": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.NUMERIC_BOOLEAN.getName() ); case "numericboolean": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.NUMERIC_BOOLEAN.getName() );
default: { default: {
final BasicType<Object> registeredBasicType = basicTypeRegistry.getRegisteredType( name ); final BasicType<?> registeredBasicType = basicTypeRegistry.getRegisteredType( name );
if ( registeredBasicType != null ) { if ( registeredBasicType != null ) {
return registeredBasicType; return registeredBasicType;
} }

View File

@ -54,6 +54,7 @@ import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
@ -61,7 +62,6 @@ import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.isOneOf;
import static org.hibernate.cfg.QuerySettings.PORTABLE_INTEGER_DIVISION;
import static org.hibernate.testing.orm.domain.gambit.EntityOfBasics.Gender.FEMALE; import static org.hibernate.testing.orm.domain.gambit.EntityOfBasics.Gender.FEMALE;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -92,6 +92,7 @@ public class FunctionTests {
entity.setTheTime( new Time( 20, 10, 8 ) ); entity.setTheTime( new Time( 20, 10, 8 ) );
entity.setTheDuration( Duration.of(3, ChronoUnit.SECONDS).plus( Duration.of(23,ChronoUnit.MILLIS) ) ); entity.setTheDuration( Duration.of(3, ChronoUnit.SECONDS).plus( Duration.of(23,ChronoUnit.MILLIS) ) );
entity.setTheTimestamp( new Timestamp( 121, 4, 27, 13, 22, 50, 123456789 ) ); entity.setTheTimestamp( new Timestamp( 121, 4, 27, 13, 22, 50, 123456789 ) );
entity.setTheUuid( UUID.randomUUID() );
em.persist(entity); em.persist(entity);
EntityOfLists eol = new EntityOfLists(1,""); EntityOfLists eol = new EntityOfLists(1,"");
@ -1280,10 +1281,14 @@ public class FunctionTests {
session -> { session -> {
assertThat( session.createQuery("select function('lower','HIBERNATE')", String.class).getSingleResult(), assertThat( session.createQuery("select function('lower','HIBERNATE')", String.class).getSingleResult(),
equalTo("hibernate") ); equalTo("hibernate") );
assertThat( session.createQuery("select function(lower as String,'HIBERNATE')", String.class).getSingleResult(),
equalTo("hibernate") );
assertThat( session.createQuery("select 1 where function('lower','HIBERNATE') = 'hibernate'", Integer.class).getSingleResult(), assertThat( session.createQuery("select 1 where function('lower','HIBERNATE') = 'hibernate'", Integer.class).getSingleResult(),
equalTo(1) ); equalTo(1) );
assertThat( session.createQuery("select function('current_user')", String.class).getSingleResult().toLowerCase(), assertThat( session.createQuery("select function('current_user')", String.class).getSingleResult().toLowerCase(),
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") ); isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
assertThat( session.createQuery("select function(current_user as String)", String.class).getSingleResult().toLowerCase(),
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
assertThat( session.createQuery("select lower(function('current_user'))", String.class).getSingleResult(), assertThat( session.createQuery("select lower(function('current_user'))", String.class).getSingleResult(),
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") ); isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
session.createQuery("select 1 where function('current_user') = 'hibernate_orm_test'", Integer.class).getSingleResultOrNull(); session.createQuery("select 1 where function('current_user') = 'hibernate_orm_test'", Integer.class).getSingleResultOrNull();
@ -2268,4 +2273,38 @@ public class FunctionTests {
.getSingleResultOrNull()); .getSingleResultOrNull());
}); });
} }
@Test
public void testColumnFunction(SessionFactoryScope scope) {
scope.inTransaction(s -> {
assertEquals("the string",
s.createSelectionQuery("select column(e.the_column) from EntityOfBasics e", String.class)
.getSingleResultOrNull());
assertEquals("the string",
s.createSelectionQuery("select column(e.'the_column') from EntityOfBasics e", String.class)
.getSingleResultOrNull());
s.createSelectionQuery("from EntityOfBasics e where column(e.the_column as String) = 'the string'", EntityOfBasics.class)
.getSingleResult();
});
}
@Test
public void testUUIDColumnFunction(SessionFactoryScope scope) {
scope.inTransaction(s -> {
byte[] bytes = s.createSelectionQuery("select column(e.theuuid as binary) from EntityOfBasics e", byte[].class)
.getSingleResultOrNull();
UUID uuid = s.createSelectionQuery("select column(e.theuuid as UUID) from EntityOfBasics e", UUID.class)
.getSingleResultOrNull();
});
}
@Test @RequiresDialect(PostgreSQLDialect.class)
public void testCtidColumnFunction(SessionFactoryScope scope) {
scope.inTransaction(s -> {
String string = s.createSelectionQuery("select column(e.ctid as String) from EntityOfBasics e", String.class)
.getSingleResultOrNull();
byte[] bytes = s.createSelectionQuery("select column(e.ctid as binary) from EntityOfBasics e", byte[].class)
.getSingleResultOrNull();
});
}
} }

View File

@ -17,6 +17,8 @@ import java.time.LocalTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Date; import java.util.Date;
import java.util.UUID;
import jakarta.persistence.AttributeConverter; import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Convert; import jakarta.persistence.Convert;
@ -68,6 +70,7 @@ public class EntityOfBasics {
private Gender convertedGender; private Gender convertedGender;
private Gender ordinalGender; private Gender ordinalGender;
private Duration theDuration; private Duration theDuration;
private UUID theUuid;
private LocalDateTime theLocalDateTime; private LocalDateTime theLocalDateTime;
private LocalDate theLocalDate; private LocalDate theLocalDate;
@ -77,6 +80,8 @@ public class EntityOfBasics {
private MutableValue mutableValue; private MutableValue mutableValue;
private String theField = "the string";
public EntityOfBasics() { public EntityOfBasics() {
} }
@ -272,6 +277,15 @@ public class EntityOfBasics {
this.theDuration = theDuration; this.theDuration = theDuration;
} }
@Column(name = "theuuid")
public UUID getTheUuid() {
return theUuid;
}
public void setTheUuid(UUID theUuid) {
this.theUuid = theUuid;
}
public Boolean isTheBoolean() { public Boolean isTheBoolean() {
return theBoolean; return theBoolean;
} }
@ -298,6 +312,15 @@ public class EntityOfBasics {
this.theStringBoolean = theStringBoolean; this.theStringBoolean = theStringBoolean;
} }
@Column(name = "the_column")
public String getTheField() {
return theField;
}
public void setTheField(String theField) {
this.theField = theField;
}
@Convert( converter = MutableValueConverter.class ) @Convert( converter = MutableValueConverter.class )
public MutableValue getMutableValue() { public MutableValue getMutableValue() {
return mutableValue; return mutableValue;

View File

@ -135,6 +135,9 @@ public abstract class MockSessionFactory
implements SessionFactoryImplementor, QueryEngine, RuntimeModelCreationContext, MetadataBuildingOptions, implements SessionFactoryImplementor, QueryEngine, RuntimeModelCreationContext, MetadataBuildingOptions,
BootstrapContext, MetadataBuildingContext, FunctionContributions, SessionFactoryOptions, JdbcTypeIndicators { BootstrapContext, MetadataBuildingContext, FunctionContributions, SessionFactoryOptions, JdbcTypeIndicators {
private static final BasicTypeImpl<Object> OBJECT_BASIC_TYPE =
new BasicTypeImpl<>(new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE);
// static so other things can get at it // static so other things can get at it
// TODO: make a static instance of this whole object instead! // TODO: make a static instance of this whole object instead!
static TypeConfiguration typeConfiguration; static TypeConfiguration typeConfiguration;
@ -972,7 +975,7 @@ public abstract class MockSessionFactory
return (DomainType<?>) elementType; return (DomainType<?>) elementType;
} }
else { else {
return new BasicTypeImpl<>(new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE); return OBJECT_BASIC_TYPE;
} }
} }