mirror of
https://github.com/dalbodeule/snap-admin.git
synced 2025-06-09 05:48:20 +00:00
WIP SQL console: basic prototype working
This commit is contained in:
parent
2352bdcdd7
commit
0691d17867
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package tech.ailef.dbadmin.external.controller;
|
package tech.ailef.dbadmin.external.controller;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -57,6 +56,9 @@ import tech.ailef.dbadmin.external.DbAdminProperties;
|
|||||||
import tech.ailef.dbadmin.external.dbmapping.DbAdminRepository;
|
import tech.ailef.dbadmin.external.dbmapping.DbAdminRepository;
|
||||||
import tech.ailef.dbadmin.external.dbmapping.DbObject;
|
import tech.ailef.dbadmin.external.dbmapping.DbObject;
|
||||||
import tech.ailef.dbadmin.external.dbmapping.DbObjectSchema;
|
import tech.ailef.dbadmin.external.dbmapping.DbObjectSchema;
|
||||||
|
import tech.ailef.dbadmin.external.dbmapping.query.DbQueryOutputField;
|
||||||
|
import tech.ailef.dbadmin.external.dbmapping.query.DbQueryResult;
|
||||||
|
import tech.ailef.dbadmin.external.dbmapping.query.DbQueryResultRow;
|
||||||
import tech.ailef.dbadmin.external.dto.CompareOperator;
|
import tech.ailef.dbadmin.external.dto.CompareOperator;
|
||||||
import tech.ailef.dbadmin.external.dto.FacetedSearchRequest;
|
import tech.ailef.dbadmin.external.dto.FacetedSearchRequest;
|
||||||
import tech.ailef.dbadmin.external.dto.LogsSearchRequest;
|
import tech.ailef.dbadmin.external.dto.LogsSearchRequest;
|
||||||
@ -543,10 +545,10 @@ public class DefaultDbAdminController {
|
|||||||
@GetMapping("/console")
|
@GetMapping("/console")
|
||||||
public String console(Model model, @RequestParam(required=false) String query) {
|
public String console(Model model, @RequestParam(required=false) String query) {
|
||||||
model.addAttribute("activePage", "console");
|
model.addAttribute("activePage", "console");
|
||||||
|
model.addAttribute("query", query == null ? "" : query);
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
List<Map<String, Object>> results = jdbTemplate.query(query, (rs, rowNum) -> {
|
List<DbQueryResultRow> results = jdbTemplate.query(query, (rs, rowNum) -> {
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<DbQueryOutputField, Object> result = new HashMap<>();
|
||||||
|
|
||||||
ResultSetMetaData metaData = rs.getMetaData();
|
ResultSetMetaData metaData = rs.getMetaData();
|
||||||
int cols = metaData.getColumnCount();
|
int cols = metaData.getColumnCount();
|
||||||
@ -554,23 +556,15 @@ public class DefaultDbAdminController {
|
|||||||
for (int i = 0; i < cols; i++) {
|
for (int i = 0; i < cols; i++) {
|
||||||
Object o = rs.getObject(i + 1);
|
Object o = rs.getObject(i + 1);
|
||||||
String columnName = metaData.getColumnName(i + 1);
|
String columnName = metaData.getColumnName(i + 1);
|
||||||
result.put(columnName, o);
|
String tableName = metaData.getTableName(i + 1);
|
||||||
|
DbQueryOutputField field = new DbQueryOutputField(columnName, tableName, dbAdmin);
|
||||||
|
|
||||||
|
result.put(field, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return new DbQueryResultRow(result, query);
|
||||||
});
|
});
|
||||||
|
model.addAttribute("results", new DbQueryResult(results));
|
||||||
|
|
||||||
/*
|
|
||||||
* Print each map in a tabular format
|
|
||||||
*/
|
|
||||||
for (Map<String, Object> obj : results) {
|
|
||||||
System.out.println("-----------------------------------------------------------");
|
|
||||||
for (String key : obj.keySet()) {
|
|
||||||
System.out.printf("%-20s | %s\n", key, obj.get(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
74
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryOutputField.java
vendored
Normal file
74
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryOutputField.java
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package tech.ailef.dbadmin.external.dbmapping.query;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import tech.ailef.dbadmin.external.DbAdmin;
|
||||||
|
import tech.ailef.dbadmin.external.dbmapping.DbField;
|
||||||
|
import tech.ailef.dbadmin.external.dbmapping.DbObjectSchema;
|
||||||
|
import tech.ailef.dbadmin.external.exceptions.DbAdminException;
|
||||||
|
|
||||||
|
public class DbQueryOutputField {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String table;
|
||||||
|
|
||||||
|
private DbField dbField;
|
||||||
|
|
||||||
|
public DbQueryOutputField(String name, String table, DbAdmin dbAdmin) {
|
||||||
|
this.name = name;
|
||||||
|
this.table = table;
|
||||||
|
|
||||||
|
try {
|
||||||
|
DbObjectSchema schema = dbAdmin.findSchemaByTableName(table);
|
||||||
|
DbField dbField = schema.getFieldByName(name);
|
||||||
|
this.dbField = dbField;
|
||||||
|
} catch (DbAdminException e) {
|
||||||
|
// We were unable to map this result column to a table, this happens
|
||||||
|
// for example with COUNT(*) results and similar. We ignore this
|
||||||
|
// as the dbField will be null and handled as such in the rest of the code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTable() {
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrimaryKey() {
|
||||||
|
return dbField != null && dbField.isPrimaryKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForeignKey() {
|
||||||
|
return dbField != null && dbField.isForeignKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBinary() {
|
||||||
|
return dbField != null && dbField.isBinary();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return "TODO TYPE";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(name, table);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
DbQueryOutputField other = (DbQueryOutputField) obj;
|
||||||
|
return Objects.equals(name, other.name) && Objects.equals(table, other.table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
32
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResult.java
vendored
Normal file
32
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResult.java
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package tech.ailef.dbadmin.external.dbmapping.query;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DbQueryResult {
|
||||||
|
private List<DbQueryResultRow> rows;
|
||||||
|
|
||||||
|
public DbQueryResult(List<DbQueryResultRow> rows) {
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DbQueryResultRow> getRows() {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return rows.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DbQueryOutputField> getSortedFields() {
|
||||||
|
if (isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
return rows.get(0).getSortedFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return rows.size();
|
||||||
|
}
|
||||||
|
}
|
31
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResultRow.java
vendored
Normal file
31
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResultRow.java
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package tech.ailef.dbadmin.external.dbmapping.query;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DbQueryResultRow {
|
||||||
|
private Map<DbQueryOutputField, Object> values;
|
||||||
|
|
||||||
|
private String query;
|
||||||
|
|
||||||
|
public DbQueryResultRow(Map<DbQueryOutputField, Object> values, String query) {
|
||||||
|
this.values = values;
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DbQueryOutputField> getSortedFields() {
|
||||||
|
return values.keySet().stream().sorted((f1, f2) -> f1.getName().compareTo(f2.getName())).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(DbQueryOutputField field) {
|
||||||
|
return values.get(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -6,3 +6,4 @@
|
|||||||
#spring.h2.console.enabled=true
|
#spring.h2.console.enabled=true
|
||||||
|
|
||||||
#spring.jpa.show-sql=true
|
#spring.jpa.show-sql=true
|
||||||
|
|
||||||
|
@ -13,9 +13,14 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form th:action="|/${dbadmin_baseUrl}/console|" method="GET">
|
<form th:action="|/${dbadmin_baseUrl}/console|" method="GET">
|
||||||
<textarea class="form-control" rows="6" name="query"></textarea>
|
<textarea class="form-control" rows="6" name="query"
|
||||||
<input class="ui-btn btn btn-primary" type="submit" value="Run">
|
th:text="${query}"></textarea>
|
||||||
|
<input class="ui-btn btn btn-primary mt-3" type="submit" value="Run">
|
||||||
</form>
|
</form>
|
||||||
|
<div class="separator mt-3 mb-3"></div>
|
||||||
|
<div th:replace="~{fragments/generic_table :: table(results=${results})}"></div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
23
src/main/resources/templates/fragments/generic_data_row.html
Normal file
23
src/main/resources/templates/fragments/generic_data_row.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<tr th:fragment="data_row(row)" class="table-data-row">
|
||||||
|
<td th:each="field : ${row.getSortedFields()}"
|
||||||
|
th:classAppend="${field.isBinary() ? 'text-center' : ''}">
|
||||||
|
<th:block th:replace="~{fragments/generic_data_row :: data_row_field(field=${field}, object=${row})}"></th:block>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- data-row-field fragment -->
|
||||||
|
<th:block th:fragment="data_row_field(field, object)">
|
||||||
|
<span th:text="${object.get(field)}"></span>
|
||||||
|
</th:block>
|
||||||
|
<!-- end data-row-field fragment -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
src/main/resources/templates/fragments/generic_table.html
Normal file
41
src/main/resources/templates/fragments/generic_table.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<div th:fragment="table(results)">
|
||||||
|
<div th:if="${results == null || results.isEmpty()}">
|
||||||
|
<p class="alert alert-warning">No results.</p>
|
||||||
|
</div>
|
||||||
|
<div th:if="${results != null && results.size() > 0}">
|
||||||
|
<table class="table table-striped align-middle mt-3">
|
||||||
|
<tr class="table-data-row">
|
||||||
|
<th th:each="field : ${results.getSortedFields()}">
|
||||||
|
<div class="m-0 p-0 d-flex justify-content-between">
|
||||||
|
<div class="column-title">
|
||||||
|
<span th:if="${field.isPrimaryKey()}">
|
||||||
|
<i title="Primary Key" class="bi bi-key"></i>
|
||||||
|
</span>
|
||||||
|
<span th:if="${field.isForeignKey()}">
|
||||||
|
<i title="Foreign Key" class="bi bi-link"></i>
|
||||||
|
</span>
|
||||||
|
<span class="m-0 p-0" th:text="${field.getName()}"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="m-0 p-0 dbfieldtype"><small th:text="${field.getType()}"></small></p>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<th:block th:each="r : ${results.getRows()}">
|
||||||
|
<tr th:replace="~{fragments/generic_data_row :: data_row(row=${r})}">
|
||||||
|
</tr>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user