@Override
    public void serialize(QueryMetadata metadata, boolean forCountRow) {
        // TODO get rid of this wrapping when Hibernate doesn't require unique aliases anymore
        boolean modified = false;
        Set<String> used = new HashSet<String>();
        Expression<?> projection = metadata.getProjection();
        if (projection instanceof Path) {
            Path<?> path = (Path<?>) projection;
            if (!used.add(path.getMetadata().getName())) {
                String alias = "col_1";
                aliases.computeIfAbsent(projection, NativeSQLSerializer::createArrayList).add(alias);
                projection = ExpressionUtils.as(projection, alias);
                modified = true;
            } else if (path.getAnnotatedElement().isAnnotationPresent(Column.class)) {
                Column column = path.getAnnotatedElement().getAnnotation(Column.class);
                if (!column.name().isEmpty()) {
                    aliases.computeIfAbsent(projection, NativeSQLSerializer::createArrayList).add(column.name());
                } else {
                    aliases.computeIfAbsent(projection, NativeSQLSerializer::createArrayList).add(ColumnMetadata.getName(path));
                }
            } else {
                aliases.computeIfAbsent(projection, NativeSQLSerializer::createArrayList).add(ColumnMetadata.getName(path));
            }
        } else if (projection instanceof FactoryExpression) {
            FactoryExpression<?> factoryExpr = (FactoryExpression<?>) projection;
            List<Expression<?>> fargs = new ArrayList<>(factoryExpr.getArgs());
            for (int j = 0; j < fargs.size(); j++) {
                if (fargs.get(j) instanceof Path) {
                    Path<?> path = (Path<?>) fargs.get(j);
                    String columnName;
                    if (path.getAnnotatedElement().isAnnotationPresent(Column.class)) {
                        Column column = path.getAnnotatedElement().getAnnotation(Column.class);
                        if (!column.name().isEmpty()) {
                            columnName = column.name();
                        } else {
                            columnName = ColumnMetadata.getName(path);
                        }
                    } else {
                        columnName = ColumnMetadata.getName(path);
                    }
                    if (!used.add(columnName)) {
                        String alias = "col_" + (j + 1);
                        aliases.computeIfAbsent(path, NativeSQLSerializer::createArrayList).add(alias);
                        fargs.set(j, ExpressionUtils.as(fargs.get(j), alias));
                    } else {
                        aliases.computeIfAbsent(path, NativeSQLSerializer::createArrayList).add(columnName);
                    }
                } else if (isAlias(fargs.get(j))) {
                    Operation<?> operation = (Operation<?>) fargs.get(j);
                    aliases.computeIfAbsent(operation, NativeSQLSerializer::createArrayList).add(operation.getArg(1).toString());
                } else if (!isAllExpression(fargs.get(j))) {
                    String alias = "col_" + (j + 1);
                    aliases.computeIfAbsent(fargs.get(j), NativeSQLSerializer::createArrayList).add(alias);
                    fargs.set(j, ExpressionUtils.as(fargs.get(j), alias));
                }
            }
            projection = Projections.tuple(new ArrayList<>(fargs));
            modified = true;
        } else if (isAlias(projection)) {
            Operation<?> operation = (Operation<?>) projection;
            aliases.computeIfAbsent(operation, NativeSQLSerializer::createArrayList).add(operation.getArg(1).toString());
        } else {
            // https://github.com/querydsl/querydsl/issues/80
            if (!isAllExpression(projection)) {
                String alias = "col_1";
                aliases.computeIfAbsent(projection, NativeSQLSerializer::createArrayList).add(alias);
                projection = ExpressionUtils.as(projection, alias);
                modified = true;
            }
        }
        if (modified) {
            metadata = metadata.clone();
            metadata.setProjection(projection);
        }
        super.serialize(metadata, forCountRow);
    }
{@code NativeSQLSerializer} extends {@link SQLSerializer} to extract referenced entity paths and change
some serialization formats

@author tiwe
querydsl-jpa/src/main/java/com/querydsl/jpa/NativeSQLSerializer.java