All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fi.evolver.basics.spring.status.component.DatabaseStatusReporter Maven / Gradle / Ivy

There is a newer version: 6.7.0
Show newest version
package fi.evolver.basics.spring.status.component;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import fi.evolver.basics.spring.status.model.ComponentStatus;
import fi.evolver.basics.spring.status.model.Reportable;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.persistence.Table;
import jakarta.persistence.metamodel.EntityType;


@Component
@Transactional(readOnly = true)
public class DatabaseStatusReporter implements Reportable {

    private final EntityManager em;
    private final Map groupsByTable;


    @Autowired
    public DatabaseStatusReporter(EntityManager em) {
        this.em = em;
        this.groupsByTable = mapGroupsByTable(em);
    }


    @Override
    public List getStatus() {
        Map tableSizes = fetchTableSizes();
        Map groupSizes = calculateGroupSizes(tableSizes);

        List results = new ArrayList<>(groupSizes.size());
        groupSizes.forEach((g, s) -> results.add(createStatus(g, s)));
        results.add(createStatus("Total", tableSizes.values().stream().mapToLong(v -> v).sum()));
        return results;
    }


    private static ComponentStatus createStatus(String group, long size) {
        return new ComponentStatus(
                "DbTableGroup",
                group,
                Collections.singletonMap("Bytes", size));
    }


    @SuppressWarnings("unchecked")
    private Map fetchTableSizes() {
        Query query = em.createNativeQuery("SELECT\n" +
                "  regexp_replace(relname, '^ht_.*', 'hibernate_cache') as table_name,\n" +
                "  sum(pg_total_relation_size(C.oid)) as table_size\n" +
                "FROM\n" +
                "  pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)\n" +
                "WHERE\n" +
                "  nspname NOT IN ('pg_catalog', 'information_schema')\n" +
                "AND\n" +
                "  C.relkind = 'r'\n" +
                "AND\n" +
                "  nspname !~ '^pg_toast'\n" +
                "GROUP BY\n" +
                "  regexp_replace(relname, '^ht_.*', 'hibernate_cache')\n" +
                "ORDER BY\n" +
                "  regexp_replace(relname, '^ht_.*', 'hibernate_cache')");

        Map map = new TreeMap<>();
        query.getResultStream().forEach(r -> map.put(
                (String)((Object[])r)[0],
                ((BigDecimal)((Object[])r)[1]).longValue()));
        return map;
    }


    private Map calculateGroupSizes(Map tableSizes) {
        Map results = new TreeMap<>();
        tableSizes.forEach((table, size) ->
            results.merge(groupsByTable.getOrDefault(table, "Other"), size, (a, b) -> a + b)
        );
        return results;
    }


    private static String capitalize(String string) {
        if (string.isEmpty())
            return string;

        return string.substring(0, 1).toUpperCase() + string.substring(1).toLowerCase();
    }


    private static Map mapGroupsByTable(EntityManager em) {
        Map> tablesByPackage = new HashMap<>();
        for (EntityType entityType: em.getMetamodel().getEntities()) {
            Class javaType = entityType.getJavaType();
            String tableName = getTableName(javaType);
            tablesByPackage.computeIfAbsent(javaType.getPackageName(), k -> new ArrayList<>()).add(tableName);
        }

        Map results = new HashMap<>();
        tablesByPackage.forEach((pkg, tables) -> {
            List parts = new ArrayList<>(Arrays.asList(pkg.split("\\.")));
            if (parts.get(parts.size() - 1).equals("entity"))
                parts.remove(parts.size() - 1);

            String group;
            int match = findMatchingPart(parts, tables);
            if (match == -1) {
                group = capitalize(parts.get(parts.size() - 1));
            }
            else {
                group = parts.subList(match, parts.size()).stream()
                        .map(DatabaseStatusReporter::capitalize)
                        .collect(Collectors.joining());
            }

            tables.forEach(t -> results.put(t, group));
        });

        return results;
    }


    private static int findMatchingPart(List parts, List tables) {
        int result = -1;
        for (String table: tables) {
            int index = findMatchingPart(parts, table);
            if (result == -1)
                result = index;
            if (result != index || index == -1)
                return -1;
        }
        return result;
    }

    private static int findMatchingPart(List parts, String table) {
        String prefix = table.split("_")[0];
        for (int i = parts.size() - 1; i >= 0; --i) {
            if (parts.get(i).equals(prefix))
                return i;
        }
        return -1;
    }


    private static String getTableName(Class javaType) {
        Table table = javaType.getAnnotation(Table.class);
        if (table != null)
            return table.name();
        return Arrays.stream(javaType.getSimpleName().split("(?<=[a-z])(?=[A-Z])"))
                .map(String::toLowerCase)
                .collect(Collectors.joining("_"));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy