mirror of
https://github.com/dalbodeule/snap-admin.git
synced 2025-06-09 05:48:20 +00:00
JPA Validation WIP
This commit is contained in:
parent
4b21437c30
commit
2fb76d445f
@ -33,6 +33,7 @@ import org.springframework.dao.DataIntegrityViolationException;
|
|||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.jdbc.UncategorizedSQLException;
|
import org.springframework.jdbc.UncategorizedSQLException;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.transaction.TransactionSystemException;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
@ -476,6 +477,20 @@ public class DefaultDbAdminController {
|
|||||||
attr.addFlashAttribute("error", "See below for details");
|
attr.addFlashAttribute("error", "See below for details");
|
||||||
attr.addFlashAttribute("validationErrors", new ValidationErrorsContainer(e));
|
attr.addFlashAttribute("validationErrors", new ValidationErrorsContainer(e));
|
||||||
attr.addFlashAttribute("params", params);
|
attr.addFlashAttribute("params", params);
|
||||||
|
} catch (DbAdminException e) {
|
||||||
|
attr.addFlashAttribute("errorTitle", "Error");
|
||||||
|
attr.addFlashAttribute("error", e.getMessage());
|
||||||
|
attr.addFlashAttribute("params", params);
|
||||||
|
} catch (TransactionSystemException e) {
|
||||||
|
if (e.getRootCause() instanceof ConstraintViolationException) {
|
||||||
|
ConstraintViolationException ee = (ConstraintViolationException)e.getRootCause();
|
||||||
|
attr.addFlashAttribute("errorTitle", "Validation error");
|
||||||
|
attr.addFlashAttribute("error", "See below for details");
|
||||||
|
attr.addFlashAttribute("validationErrors", new ValidationErrorsContainer(ee));
|
||||||
|
attr.addFlashAttribute("params", params);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ public class CustomJpaRepository extends SimpleJpaRepository {
|
|||||||
.where(
|
.where(
|
||||||
cb.or(
|
cb.or(
|
||||||
cb.and(finalPredicates.toArray(new Predicate[finalPredicates.size()])), // query search on String fields
|
cb.and(finalPredicates.toArray(new Predicate[finalPredicates.size()])), // query search on String fields
|
||||||
cb.equal(root.get(schema.getPrimaryKey().getName()), q)
|
cb.equal(root.get(schema.getPrimaryKey().getName()).as(String.class), q)
|
||||||
)
|
)
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -19,9 +19,7 @@
|
|||||||
|
|
||||||
package tech.ailef.dbadmin.external.dbmapping;
|
package tech.ailef.dbadmin.external.dbmapping;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -34,7 +32,6 @@ import org.springframework.data.domain.PageRequest;
|
|||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@ -216,43 +213,47 @@ public class DbAdminRepository {
|
|||||||
@Transactional("transactionManager")
|
@Transactional("transactionManager")
|
||||||
public Object create(DbObjectSchema schema, Map<String, String> values, Map<String, MultipartFile> files, String primaryKey) {
|
public Object create(DbObjectSchema schema, Map<String, String> values, Map<String, MultipartFile> files, String primaryKey) {
|
||||||
DbObject obj = schema.buildObject(values, files);
|
DbObject obj = schema.buildObject(values, files);
|
||||||
|
Object save = save(schema, obj);
|
||||||
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
return new DbObject(save, schema).getPrimaryKeyValue();
|
||||||
Set<ConstraintViolation<Object>> violations = validator.validate(obj.getUnderlyingInstance());
|
// return save;
|
||||||
|
// System.out.println(obj);
|
||||||
if (violations.size() > 0) {
|
// Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||||
throw new ConstraintViolationException(violations);
|
// validator.va
|
||||||
}
|
// Set<ConstraintViolation<Object>> violations = validator.validate(obj.getUnderlyingInstance());
|
||||||
|
//
|
||||||
SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate).withTableName(schema.getTableName());
|
// if (violations.size() > 0) {
|
||||||
|
// throw new ConstraintViolationException(violations);
|
||||||
Map<String, Object> allValues = new HashMap<>();
|
// }
|
||||||
allValues.putAll(values);
|
//
|
||||||
|
// SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate).withTableName(schema.getTableName());
|
||||||
values.keySet().forEach(fieldName -> {
|
//
|
||||||
if (values.get(fieldName).isBlank()) {
|
// Map<String, Object> allValues = new HashMap<>();
|
||||||
allValues.put(fieldName, null);
|
// allValues.putAll(values);
|
||||||
}
|
//
|
||||||
});
|
// values.keySet().forEach(fieldName -> {
|
||||||
|
// if (values.get(fieldName).isBlank()) {
|
||||||
files.keySet().forEach(f -> {
|
// allValues.put(fieldName, null);
|
||||||
try {
|
// }
|
||||||
// The file parameter gets sent even if empty, so it's needed
|
// });
|
||||||
// to check if the file has actual content, to avoid storing an empty file
|
//
|
||||||
if (files.get(f).getSize() > 0)
|
// files.keySet().forEach(f -> {
|
||||||
allValues.put(f, files.get(f).getBytes());
|
// try {
|
||||||
} catch (IOException e) {
|
// // The file parameter gets sent even if empty, so it's needed
|
||||||
throw new DbAdminException(e);
|
// // to check if the file has actual content, to avoid storing an empty file
|
||||||
}
|
// if (files.get(f).getSize() > 0)
|
||||||
});
|
// allValues.put(f, files.get(f).getBytes());
|
||||||
|
// } catch (IOException e) {
|
||||||
if (primaryKey == null) {
|
// throw new DbAdminException(e);
|
||||||
insert = insert.usingGeneratedKeyColumns(schema.getPrimaryKey().getName());
|
// }
|
||||||
return insert.executeAndReturnKey(allValues);
|
// });
|
||||||
} else {
|
//
|
||||||
insert.execute(allValues);
|
// if (primaryKey == null) {
|
||||||
return primaryKey;
|
// insert = insert.usingGeneratedKeyColumns(schema.getPrimaryKey().getName());
|
||||||
}
|
// return insert.executeAndReturnKey(allValues);
|
||||||
|
// } else {
|
||||||
|
// insert.execute(allValues);
|
||||||
|
// return primaryKey;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.ManyToMany;
|
import jakarta.persistence.ManyToMany;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
@ -208,6 +209,12 @@ public class DbField {
|
|||||||
return getPrimitiveField().getAnnotation(ReadOnly.class) != null;
|
return getPrimitiveField().getAnnotation(ReadOnly.class) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isToOne() {
|
||||||
|
return (getPrimitiveField().getAnnotation(OneToOne.class) != null &&
|
||||||
|
getPrimitiveField().getAnnotation(OneToOne.class).mappedBy().isBlank())
|
||||||
|
|| getPrimitiveField().getAnnotation(ManyToOne.class) != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if this field is settable with a raw value, i.e.
|
* Returns if this field is settable with a raw value, i.e.
|
||||||
* a field that is not a relationship to another entity;
|
* a field that is not a relationship to another entity;
|
||||||
@ -220,6 +227,10 @@ public class DbField {
|
|||||||
&& getPrimitiveField().getAnnotation(ManyToMany.class) == null;
|
&& getPrimitiveField().getAnnotation(ManyToMany.class) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isGeneratedValue() {
|
||||||
|
return getPrimitiveField().getAnnotation(GeneratedValue.class) != null;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<DbFieldValue> getAllValues() {
|
public Set<DbFieldValue> getAllValues() {
|
||||||
List<?> findAll = schema.getJpaRepository().findAll();
|
List<?> findAll = schema.getJpaRepository().findAll();
|
||||||
return findAll.stream()
|
return findAll.stream()
|
||||||
|
@ -32,7 +32,6 @@ import jakarta.persistence.OneToMany;
|
|||||||
import jakarta.persistence.OneToOne;
|
import jakarta.persistence.OneToOne;
|
||||||
import tech.ailef.dbadmin.external.annotations.DisplayName;
|
import tech.ailef.dbadmin.external.annotations.DisplayName;
|
||||||
import tech.ailef.dbadmin.external.exceptions.DbAdminException;
|
import tech.ailef.dbadmin.external.exceptions.DbAdminException;
|
||||||
import tech.ailef.dbadmin.external.misc.Utils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for all objects retrieved from the database.
|
* Wrapper for all objects retrieved from the database.
|
||||||
@ -183,6 +182,29 @@ public class DbObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void setRelationship(String fieldName, Object primaryKeyValue) {
|
||||||
|
DbField field = schema.getFieldByName(fieldName);
|
||||||
|
DbObjectSchema linkedSchema = field.getConnectedSchema();
|
||||||
|
Optional<?> obj = linkedSchema.getJpaRepository().findById(primaryKeyValue);
|
||||||
|
|
||||||
|
if (!obj.isPresent()) {
|
||||||
|
throw new DbAdminException("Invalid value " + primaryKeyValue + " for " + fieldName
|
||||||
|
+ ": item does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Method setter = findSetter(field.getJavaName());
|
||||||
|
|
||||||
|
if (setter == null) {
|
||||||
|
throw new DbAdminException("Unable to find setter method for " + fieldName + " in " + schema.getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setter.invoke(instance, obj.get());
|
||||||
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void set(String fieldName, Object value) {
|
public void set(String fieldName, Object value) {
|
||||||
Method setter = findSetter(fieldName);
|
Method setter = findSetter(fieldName);
|
||||||
|
|
||||||
@ -229,4 +251,11 @@ public class DbObject {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DbObject [instance=" + instance + ", schema=" + schema + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -384,6 +384,10 @@ public class DbObjectSchema {
|
|||||||
if (parsedFieldValue != null && getFieldByName(param).isSettable()) {
|
if (parsedFieldValue != null && getFieldByName(param).isSettable()) {
|
||||||
setter.invoke(instance, parsedFieldValue);
|
setter.invoke(instance, parsedFieldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsedFieldValue != null && getFieldByName(param).isToOne()) {
|
||||||
|
dbObject.setRelationship(param, parsedFieldValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String fileParam : files.keySet()) {
|
for (String fileParam : files.keySet()) {
|
||||||
|
@ -29,4 +29,11 @@ public class ValidationErrorsContainer {
|
|||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return errors.isEmpty();
|
return errors.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ValidationErrorsContainer [errors=" + errors + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
<form class="form" enctype="multipart/form-data" method="post" th:action="|/${dbadmin_baseUrl}/model/${className}/create|">
|
<form class="form" enctype="multipart/form-data" method="post" th:action="|/${dbadmin_baseUrl}/model/${className}/create|">
|
||||||
<input type="hidden" name="__dbadmin_create" th:value="${create}">
|
<input type="hidden" name="__dbadmin_create" th:value="${create}">
|
||||||
<div th:each="field : ${schema.getSortedFields(false)}" class="mt-2"
|
<div th:each="field : ${schema.getSortedFields(false)}" class="mt-2"
|
||||||
|
th:if="${!field.isGeneratedValue() || !create}"
|
||||||
th:classAppend="|${validationErrors != null && validationErrors.hasErrors(field.getJavaName()) ? 'invalid' : ''}|">
|
th:classAppend="|${validationErrors != null && validationErrors.hasErrors(field.getJavaName()) ? 'invalid' : ''}|">
|
||||||
<label th:for="|__id_${field.getName()}|" class="mb-1 fw-bold">
|
<label th:for="|__id_${field.getName()}|" class="mb-1 fw-bold">
|
||||||
<span th:if="${!field.isNullable() && !field.isPrimaryKey()}">
|
<span th:if="${!field.isNullable() && !field.isPrimaryKey()}">
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<i class="bi bi-cpu"></i>
|
<i class="bi bi-cpu"></i>
|
||||||
</td>
|
</td>
|
||||||
<td th:text="${colName}">
|
<td class="fw-bold" th:text="${colName}">
|
||||||
</td>
|
</td>
|
||||||
<td th:text="${object.compute(colName)}">
|
<td th:text="${object.compute(colName)}">
|
||||||
</td>
|
</td>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user