diff --git a/src/main/java/tech/ailef/dbadmin/controller/DefaultDbAdminController.java b/src/main/java/tech/ailef/dbadmin/controller/DefaultDbAdminController.java index 6c9e890..54607ae 100644 --- a/src/main/java/tech/ailef/dbadmin/controller/DefaultDbAdminController.java +++ b/src/main/java/tech/ailef/dbadmin/controller/DefaultDbAdminController.java @@ -22,11 +22,13 @@ import org.springframework.web.multipart.MultipartFile; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import jakarta.persistence.criteria.Predicate; import tech.ailef.dbadmin.DbAdmin; import tech.ailef.dbadmin.dbmapping.DbAdminRepository; import tech.ailef.dbadmin.dbmapping.DbObject; import tech.ailef.dbadmin.dbmapping.DbObjectSchema; import tech.ailef.dbadmin.dto.PaginatedResult; +import tech.ailef.dbadmin.exceptions.DbAdminException; import tech.ailef.dbadmin.exceptions.InvalidPageException; @Controller @@ -90,7 +92,9 @@ public class DefaultDbAdminController { public String list(Model model, @PathVariable String className, @RequestParam(required=false) Integer page, @RequestParam(required=false) String query, @RequestParam(required=false) Integer pageSize, @RequestParam(required=false) String sortKey, - @RequestParam(required=false) String sortOrder) { + @RequestParam(required=false) String sortOrder, @RequestParam MultiValueMap otherParams) { + System.out.println(otherParams); + if (page == null) page = 1; if (pageSize == null) pageSize = 50; @@ -98,8 +102,8 @@ public class DefaultDbAdminController { try { PaginatedResult result = null; - if (query != null) { - result = repository.search(schema, query, page, pageSize, sortKey, sortOrder); + if (query != null || !otherParams.isEmpty()) { + result = repository.search(schema, query, page, pageSize, sortKey, sortOrder, otherParams); } else { result = repository.findAll(schema, page, pageSize, sortKey, sortOrder); } @@ -337,9 +341,12 @@ public class DefaultDbAdminController { } } + @GetMapping("/settings") public String settings(Model model) { model.addAttribute("activePage", "settings"); return "settings"; } + + } diff --git a/src/main/java/tech/ailef/dbadmin/controller/GlobalController.java b/src/main/java/tech/ailef/dbadmin/controller/GlobalController.java new file mode 100644 index 0000000..c900079 --- /dev/null +++ b/src/main/java/tech/ailef/dbadmin/controller/GlobalController.java @@ -0,0 +1,27 @@ +package tech.ailef.dbadmin.controller; + +import java.util.Map; + +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ModelAttribute; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * This class registers some ModelAttribute objects that are + * used in all templates. + */ +@ControllerAdvice +public class GlobalController { + + /** + * A multi valued map containing the query parameters. It is used primarily + * in building complex URL when performing faceted search with multiple filters. + * @param request the incoming request + * @return multi valued map of request parameters + */ + @ModelAttribute("queryParams") + public Map getQueryParams(HttpServletRequest request) { + return request.getParameterMap(); + } +} \ No newline at end of file diff --git a/src/main/java/tech/ailef/dbadmin/dbmapping/AdvancedJpaRepository.java b/src/main/java/tech/ailef/dbadmin/dbmapping/AdvancedJpaRepository.java index c2d9c97..1da34db 100644 --- a/src/main/java/tech/ailef/dbadmin/dbmapping/AdvancedJpaRepository.java +++ b/src/main/java/tech/ailef/dbadmin/dbmapping/AdvancedJpaRepository.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.stream.Collectors; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; +import org.springframework.util.MultiValueMap; import jakarta.persistence.EntityManager; import jakarta.persistence.criteria.CriteriaBuilder; @@ -12,6 +13,7 @@ import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Path; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Root; +import tech.ailef.dbadmin.exceptions.DbAdminException; @SuppressWarnings("rawtypes") public class AdvancedJpaRepository extends SimpleJpaRepository { @@ -27,47 +29,40 @@ public class AdvancedJpaRepository extends SimpleJpaRepository { this.schema = schema; } - public long count(String q) { + @SuppressWarnings("unchecked") + public long count(String q, MultiValueMap filteringParams) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery query = cb.createQuery(Long.class); Root root = query.from(schema.getJavaClass()); - - List stringFields = - schema.getSortedFields().stream().filter(f -> f.getType() == DbFieldType.STRING) - .collect(Collectors.toList()); - - System.out.println("STRING F = " + stringFields); - List predicates = new ArrayList<>(); - for (DbField f : stringFields) { - Path path = root.get(f.getJavaName()); - predicates.add(cb.like(cb.lower(cb.toString(path)), "%" + q.toLowerCase() + "%")); - } + List finalPredicates = buildPredicates(q, filteringParams, cb, root); + query.select(cb.count(root.get(schema.getPrimaryKey().getName()))) - .where(cb.or(predicates.toArray(new Predicate[predicates.size()]))); + .where( + cb.and( + finalPredicates.toArray(new Predicate[finalPredicates.size()]) + ) + ); Object o = entityManager.createQuery(query).getSingleResult(); return (Long)o; } @SuppressWarnings("unchecked") - public List search(String q, int page, int pageSize, String sortKey, String sortOrder) { - CriteriaBuilder cb = entityManager.getCriteriaBuilder(); + public List search(String q, int page, int pageSize, String sortKey, String sortOrder, MultiValueMap filteringParams) { + CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery query = cb.createQuery(schema.getJavaClass()); Root root = query.from(schema.getJavaClass()); - List stringFields = - schema.getSortedFields().stream().filter(f -> f.getType() == DbFieldType.STRING) - .collect(Collectors.toList()); + List finalPredicates = buildPredicates(q, filteringParams, cb, root); - List predicates = new ArrayList<>(); - for (DbField f : stringFields) { - Path path = root.get(f.getJavaName()); - predicates.add(cb.like(cb.lower(cb.toString(path)), "%" + q.toLowerCase() + "%")); - } - query.select(root) - .where(cb.or(predicates.toArray(new Predicate[predicates.size()]))); + .where( + cb.and( + finalPredicates.toArray(new Predicate[finalPredicates.size()]) // query search on String fields + ) + + ); if (sortKey != null) query.orderBy(sortOrder.equals("DESC") ? cb.desc(root.get(sortKey)) : cb.asc(root.get(sortKey))); @@ -75,19 +70,72 @@ public class AdvancedJpaRepository extends SimpleJpaRepository { .setFirstResult((page - 1) * pageSize).getResultList(); } - public List distinctFieldValues(DbField field) { - CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - - Class outputType = field.getType().getJavaClass(); - if (field.getConnectedType() != null) { - outputType = field.getConnectedSchema().getPrimaryKey().getType().getJavaClass(); - } - - CriteriaQuery query = cb.createQuery(outputType); - Root root = query.from(schema.getJavaClass()); + private List buildPredicates(String q, MultiValueMap filteringParams, + CriteriaBuilder cb, Path root) { + List finalPredicates = new ArrayList<>(); - query.select(root.get(field.getJavaName()).as(outputType)).distinct(true); + List stringFields = + schema.getSortedFields().stream().filter(f -> f.getType() == DbFieldType.STRING) + .collect(Collectors.toList()); - return entityManager.createQuery(query).getResultList(); + List queryPredicates = new ArrayList<>(); + if (q != null) { + for (DbField f : stringFields) { + Path path = root.get(f.getJavaName()); + queryPredicates.add(cb.like(cb.lower(cb.toString(path)), "%" + q.toLowerCase() + "%")); + } + + Predicate queryPredicate = cb.or(queryPredicates.toArray(new Predicate[queryPredicates.size()])); + finalPredicates.add(queryPredicate); + } + + /* + * Compute filtering predicates + */ + if (filteringParams != null) { + List ops = filteringParams.get("filter_op[]"); + List fields = filteringParams.get("filter_field[]"); + List values = filteringParams.get("filter_value[]"); + + + if (ops != null && fields != null && values != null) { + if (ops.size() != fields.size() || fields.size() != values.size() + || ops.size() != values.size()) { + throw new DbAdminException("Filtering parameters must have the same size"); + } + + for (int i = 0; i < ops.size(); i++) { + String op = ops.get(i); + String field = fields.get(i); + String value = values.get(i); + + if (op.equalsIgnoreCase("equals")) { + finalPredicates.add(cb.equal(cb.toString(root.get(field)), value)); + } else if (op.equalsIgnoreCase("contains")) { + System.out.println("CONTAINS"); + finalPredicates.add(cb.like(cb.toString(root.get(field)), "%" + value + "%")); + } + } + } + } + return finalPredicates; } + + +// @SuppressWarnings("unchecked") +// public List distinctFieldValues(DbField field) { +// CriteriaBuilder cb = entityManager.getCriteriaBuilder(); +// +// Class outputType = field.getType().getJavaClass(); +// if (field.getConnectedType() != null) { +// outputType = field.getConnectedSchema().getPrimaryKey().getType().getJavaClass(); +// } +// +// CriteriaQuery query = cb.createQuery(outputType); +// Root root = query.from(schema.getJavaClass()); +// +// query.select(root.get(field.getJavaName()).as(outputType)).distinct(true); +// +// return entityManager.createQuery(query).getResultList(); +// } } diff --git a/src/main/java/tech/ailef/dbadmin/dbmapping/DbAdminRepository.java b/src/main/java/tech/ailef/dbadmin/dbmapping/DbAdminRepository.java index 79e7ba3..11196c5 100644 --- a/src/main/java/tech/ailef/dbadmin/dbmapping/DbAdminRepository.java +++ b/src/main/java/tech/ailef/dbadmin/dbmapping/DbAdminRepository.java @@ -16,6 +16,7 @@ import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; import org.springframework.web.multipart.MultipartFile; import jakarta.transaction.Transactional; @@ -63,8 +64,8 @@ public class DbAdminRepository { * @param query * @return */ - public long count(DbObjectSchema schema, String query) { - return schema.getJpaRepository().count(query); + public long count(DbObjectSchema schema, String query, MultiValueMap filteringParams) { + return schema.getJpaRepository().count(query, filteringParams); } @@ -223,10 +224,11 @@ public class DbAdminRepository { * @param query * @return */ - public PaginatedResult search(DbObjectSchema schema, String query, int page, int pageSize, String sortKey, String sortOrder) { + public PaginatedResult search(DbObjectSchema schema, String query, int page, int pageSize, String sortKey, + String sortOrder, MultiValueMap filteringParams) { AdvancedJpaRepository jpaRepository = schema.getJpaRepository(); - long maxElement = count(schema, query); + long maxElement = count(schema, query, filteringParams); int maxPage = (int)(Math.ceil ((double)maxElement / pageSize)); if (page <= 0) page = 1; @@ -236,7 +238,7 @@ public class DbAdminRepository { return new PaginatedResult( new PaginationInfo(page, maxPage, pageSize, maxElement), - jpaRepository.search(query, page, pageSize, sortKey, sortOrder).stream() + jpaRepository.search(query, page, pageSize, sortKey, sortOrder, filteringParams).stream() .map(o -> new DbObject(o, schema)) .toList() ); @@ -251,7 +253,7 @@ public class DbAdminRepository { public List search(DbObjectSchema schema, String query) { AdvancedJpaRepository jpaRepository = schema.getJpaRepository(); - return jpaRepository.search(query, 1, 50, null, null).stream() + return jpaRepository.search(query, 1, 50, null, null, null).stream() .map(o -> new DbObject(o, schema)) .toList(); } diff --git a/src/main/java/tech/ailef/dbadmin/dbmapping/DbObjectSchema.java b/src/main/java/tech/ailef/dbadmin/dbmapping/DbObjectSchema.java index c9c8f17..581c1f9 100644 --- a/src/main/java/tech/ailef/dbadmin/dbmapping/DbObjectSchema.java +++ b/src/main/java/tech/ailef/dbadmin/dbmapping/DbObjectSchema.java @@ -183,9 +183,9 @@ public class DbObjectSchema { }).toList(); } - public List getFieldValues(DbField field) { - return jpaRepository.distinctFieldValues(field); - } +// public List getFieldValues(DbField field) { +// return jpaRepository.distinctFieldValues(field); +// } public Object[] getInsertArray(Map params, Map files) { int currentIndex = 0; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5a8eb30..85abd79 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,4 +4,5 @@ spring.datasource.username=sa spring.datasource.password=password #spring.h2.console.enabled=true +spring.jpa.show-sql=true diff --git a/src/main/resources/templates/fragments/forms.html b/src/main/resources/templates/fragments/forms.html index 9eaca89..5cf1a9e 100644 --- a/src/main/resources/templates/fragments/forms.html +++ b/src/main/resources/templates/fragments/forms.html @@ -38,5 +38,55 @@ + + +
+
+ + +
+
+ +
+ + + +
+ +
+
+ +
+ +
+ +
+ + + +
+ +
+
+ + +
+
diff --git a/src/main/resources/templates/model/list.html b/src/main/resources/templates/model/list.html index 9ff42b0..71da4df 100644 --- a/src/main/resources/templates/model/list.html +++ b/src/main/resources/templates/model/list.html @@ -45,7 +45,7 @@
-

+

[[ ${schema.getJavaClass().getSimpleName()} ]] [[ ${schema.getTableName()} ]] @@ -55,7 +55,8 @@

+ th:href="|/dbadmin/model/${schema.getClassName()}/create|"> +
@@ -66,20 +67,11 @@

Filters

-
-
- - -
- -
- -
- -
-
-
-
+ + + +
+