Sie können SEPARATOR
hinzufügen als Stichwort. Implementieren Sie Ihren eigenen DialectResolver
und fügen Sie das Schlüsselwort in Kleinbuchstaben hinzu zum resultierenden Dialekt:
public class MyDialectResolver implements DialectResolver {
public Dialect resolveDialect(DialectResolutionInfo info) {
for (Database database : Database.values()) {
Dialect dialect = database.resolveDialect(info);
if (dialect != null) {
dialect.getKeywords().add("separator");
return dialect;
}
}
return null;
}
}
Dasselbe gilt für Hibernate-Versionen vorher 5.2.13 / 5.3.0:
public class MyDialectResolver extends StandardDialectResolver {
protected Dialect resolveDialectInternal(DatabaseMetaData metaData) throws SQLException {
Dialect dialect = super.resolveDialectInternal(metaData);
dialect.getKeywords().add("separator");
return dialect;
}
}
Sie müssen dann Hibernate mitteilen, dass es Ihren Dialekt-Resolver verwenden soll. Beispielsweise können Sie dies in JPA in Ihrer persistence.xml tun:
<persistence>
<persistence-unit>
...
<property name="hibernate.dialect_resolvers" value="mypackage.MyDialectResolver"/>
</persistence-unit>
</persistence>
Gleiches gilt für Aggregationsfunktionen in anderen Dialekten. Zum Beispiel in Oracle der WITHIN
Schlüsselwort fehlt.
Es gibt eine andere Option, die datenbankunabhängiger ist (und die ich bevorzuge). Erstellen Sie die folgende SQLFunction
:
public class ListAggFunction implements SQLFunction {
/**
* The pattern that describes how the function is build in SQL.
*
* Replacements:
* {path} - is replaced with the path of the list attribute
* {separator} - is replaced with the separator (defaults to '')
* {orderByPath} - is replaced by the path that is used for ordering the elements of the list
*/
private String pattern;
/**
* Creates a new ListAggFunction definition which uses the ANSI SQL:2016 syntax.
*/
public ListAggFunction() {
this("LISTAGG(DISTINCT {path}, {separator}) WITHIN GROUP(ORDER BY {orderByPath})");
}
/**
* Creates a new ListAggFunction definition which uses a database specific syntax.
*
* @param pattern The pattern that describes how the function is build in SQL.
*/
public ListAggFunction(String pattern) {
this.pattern = pattern;
}
public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
return StringType.INSTANCE;
}
public boolean hasArguments() {
return true;
}
public boolean hasParenthesesIfNoArguments() {
return true;
}
public String render(Type firstArgumentType, List arguments,
SessionFactoryImplementor factory) throws QueryException {
if (arguments.isEmpty() || arguments.size() > 3) {
throw new IllegalArgumentException(
"Expected arguments for 'listagg': path [, separator [, order by path]]");
}
String path = (String) arguments.get(0);
String separator = arguments.size() < 2 ? "''" : (String) arguments.get(1);
String orderByPath = arguments.size() <= 2 ? path : (String) arguments.get(2);
return StringUtils.replaceEach(this.pattern, new String[] { "{path}", "{separator}", "{orderByPath}" },
new String[] { path, separator, orderByPath });
}
}
Sie können diese Funktion im DialectResolver auf die gleiche Weise wie das Schlüsselwort oben registrieren:
if ("MySQL".equals(info.getDatabaseName()) || "H2".equals(info.getDatabaseName())) {
dialect.getFunctions().put("listagg", new ListAggFunction("GROUP_CONCAT(DISTINCT {path} ORDER BY {orderByPath} SEPARATOR {separator})"));
} else {
dialect.getFunctions().put("listagg", new ListAggFunction());
}
Jetzt können Sie diese Funktion in Ihren JPQL-/HQL-/Kriterienabfragen verwenden, ohne sich Gedanken über die Syntax des Dialekts machen zu müssen:
SELECT e.group, listagg(e.stringProperty, ', ') FROM Entity e GROUP BY e.group