
fi.evolver.basics.spring.status.component.DatabaseStatusReporter Maven / Gradle / Ivy
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