4

I am currently working on a report which needs a group_concat for one of the fields.

CriteriaQuery<GameDetailsDto> criteriaQuery = criteriaBuilder
                .createQuery(GameDetailsDto.class);
Root<BetDetails> betDetails = criteriaQuery.from(BetDetails.class);
Expression<String> betSelection = betDetails.get("winningOutcome");
criteriaQuery.multiselect(
    // other fields to select
    criteriaBuilder.function("group_concat", String.class, betSelection),
    // other fields to select
    );
//predicate, where clause and other filters

TypedQuery<GameDetailsDto> typedQuery = entityManager.createQuery(criteriaQuery);

this throws a null pointer exception on the line:

TypedQuery<GameDetailsDto> typedQuery = entityManager.createQuery(criteriaQuery);

did i incorrectly use the function method of the criteriaBuilder?
the documentations says:

function(String name, Class<T> type, Expression<?>... args);
geneqew
  • 2,401
  • 5
  • 33
  • 48

3 Answers3

11

I figured out how to do this with Hibernate-jpa-mysql:

1.) created a GroupConcatFunction class extending org.hibernate.dialect.function.SQLFunction (this is for single column group_concat for now)

public class GroupConcatFunction implements SQLFunction {

@Override
public boolean hasArguments() {
    return true;
}

@Override
public boolean hasParenthesesIfNoArguments() {
    return true;
}

@Override
public Type getReturnType(Type firstArgumentType, Mapping mapping)
        throws QueryException {
    return StandardBasicTypes.STRING;
}

@Override
public String render(Type firstArgumentType, List arguments,
        SessionFactoryImplementor factory) throws QueryException {
    if (arguments.size() != 1) {
        throw new QueryException(new IllegalArgumentException(
                "group_concat shoudl have one arg"));
    }
    return "group_concat(" + arguments.get(0) + ")";
}

}


2.) i created the CustomMySql5Dialect class extending org.hibernate.dialect.MySQL5Dialect and registered the group_concat class created in step 1
3.) On the app context, i updated the jpaVendorAdapter to use the CustomMySql5Dialect as the databasePlatform
4.) Finally to use it

criteriaBuilder.function("group_concat", String.class,
        sampleRoot.get("sampleColumnName"))
geneqew
  • 2,401
  • 5
  • 33
  • 48
4

Simple solution: instead of creating the whole class, just use SQLFunctionTemplate.

new SQLFunctionTemplate(StandardBasicTypes.STRING, "group_concat(?1)")

and then register this function in your own SQL dialect (eg. in constructor)

public class MyOwnSQLDialect extends MySQL5Dialect {

  public MyOwnSQLDialect() {
    super();
    this.registerFunction("group_concat", new SQLFunctionTemplate(StandardBasicTypes.STRING, "group_concat(?1)"));
  }
}
bartektartanus
  • 15,284
  • 6
  • 74
  • 102
  • how will this work. `MyOwnSQLDialect.class` is not a spring `@Component. so how will the program pick this class up. Can you add few more details. – samshers Jul 28 '22 at 21:12
0

Suggested property:

spring.jpa.properties.hibernate.metadata_builder_contributor = com.inn.core.generic.utils.SqlFunctionsMetadataBuilderContributor

and class:

import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.spi.MetadataBuilderContributor;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StandardBasicTypes;
import org.springframework.stereotype.Component;
@Component
public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor {

    @Override
    public void contribute(MetadataBuilder metadataBuilder) {
        metadataBuilder.applySqlFunction("config_json_extract",
                new StandardSQLFunction("json_extract", StandardBasicTypes.STRING));
        metadataBuilder.applySqlFunction("JSON_UNQUOTE",
                new StandardSQLFunction("JSON_UNQUOTE", StandardBasicTypes.STRING));
        metadataBuilder.applySqlFunction("group_concat", 
                new StandardSQLFunction("group_concat", StandardBasicTypes.STRING));

    }

}
Florian
  • 2,562
  • 5
  • 25
  • 35