Hi team!
We are trying to migrate from hibernate criteria to jpa with hibernate 6 and faced with a problem.
in hibernate criteria we have a custom Criterion which render something like this:
select * from (values (?), (?), (?), (?)) as f_tableAlias (property);
in hibernate criteria it looks like this:
@Override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
final String[] columns = criteriaQuery.findColumns(propertyName, criteria);
if (columns.length != 1) {
return "";
}
var column = columns[0];
var columnArray= column.split("\\.");
var tableAlias = columnArray[0];
var property = columnArray[1];
var valuesResult = values.stream().map(v -> "?").collect(Collectors.joining("),("));
return column + " in ( select * from (values (" + valuesResult + ")) as f_" + tableAlias + "(" + property + "))";
}
@Override
public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
return values.stream().map(v -> criteriaQuery.getTypedValue(criteria, propertyName, v)).toArray(TypedValue[]::new);
}
then we adapt it to use in jpa criteria builder in hibernate 5.5.8
and it was created via implemetation of SQLFunction with override render like this:
@Override
public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) throws QueryException {
//all of arguments here is "?" except first - which is "path" and received here as is
var path = (String) arguments.get(0);
var split = path.split("\\.");
var tableAlias = split[0];
var property = split[1];
var values1 = ((List<?>) arguments).stream().skip(1)
.map(this::replaceWithValidate)
.collect(Collectors.joining("),("));
var result = "select * from (values (" + values1 + ")) as f_" + tableAlias + "(" + property + ")";
return result;
}
usage:
public <T> Predicate in(Expression<T> path, Set<T> values) {
return path.in(
criteriaBuilder.function(
FunctionsContributor.IN_SELECT_VALUES_FUNCTION,
path.getJavaType(),
path,
criteriaBuilder.literal(values)
)
);
}
with parameter AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE = BIND
both of this variants generates sql like above.
I was tried to create function that extends AbstractSqmSelfRenderingFunctionDescriptor
like this (simplified):
@Override
public void render(SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
var pathArg = sqlAstArguments.get(0);
var columntRef = ((Expression) pathArg).getColumnReference();
var tableAlias = columntRef.getQualifier();
var fieldName = columntRef.getSelectableName();
var valueArg = sqlAstArguments.get(1);
var valuesCollection = (Collection<?>) ((QueryLiteral<?>) valueArg).getLiteralValue();
var valuesStr = valuesCollection.stream().map(String::valueOf).collect(Collectors.joining("),("));
var result = "select * from (values (" + valuesStr + ")) as f_" + tableAlias + "(" + fieldName + ")";
sqlAppender.append(result);
}
usage:
public <T> Predicate in(Expression<T> path, Set<T> values) {
return path.in(
criteriaBuilder.function(
FunctionsContributor.IN_SELECT_VALUES_FUNCTION,
path.getJavaType(),
path,
criteriaBuilder.literal(values)
)
);
}
but in such case values renders as is to query.
In total:
-
Is there any way to bind literals as parameters in extensions of AbstractSqmSelfRenderingFunctionDescriptor ?
(in other functions i tried walker.render(sqlAstArguments.get(n), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER); with all SqlAstNodeRenderingMode but literal arguments always renders as is instead of parameters -
Can you please give me some advices - how can i achieve the same behavior in hibernate 6.5.* which it was in 5.5.8 ?