diff --git a/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java b/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java index 3927dd5..80cddb6 100644 --- a/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java +++ b/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java @@ -33,6 +33,7 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.jdbc.UncategorizedSQLException; import org.springframework.stereotype.Controller; +import org.springframework.transaction.TransactionSystemException; import org.springframework.ui.Model; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -476,6 +477,20 @@ public class DefaultDbAdminController { attr.addFlashAttribute("error", "See below for details"); attr.addFlashAttribute("validationErrors", new ValidationErrorsContainer(e)); 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); + } } diff --git a/src/main/java/tech/ailef/dbadmin/external/dbmapping/CustomJpaRepository.java b/src/main/java/tech/ailef/dbadmin/external/dbmapping/CustomJpaRepository.java index a96b327..d81c139 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dbmapping/CustomJpaRepository.java +++ b/src/main/java/tech/ailef/dbadmin/external/dbmapping/CustomJpaRepository.java @@ -89,7 +89,7 @@ public class CustomJpaRepository extends SimpleJpaRepository { .where( cb.or( 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) ) ); diff --git a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbAdminRepository.java b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbAdminRepository.java index 89ad372..879bf1d 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbAdminRepository.java +++ b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbAdminRepository.java @@ -19,9 +19,7 @@ package tech.ailef.dbadmin.external.dbmapping; -import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -34,7 +32,6 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; 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.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -216,43 +213,47 @@ public class DbAdminRepository { @Transactional("transactionManager") public Object create(DbObjectSchema schema, Map values, Map files, String primaryKey) { DbObject obj = schema.buildObject(values, files); - - Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); - Set> violations = validator.validate(obj.getUnderlyingInstance()); - - if (violations.size() > 0) { - throw new ConstraintViolationException(violations); - } - - SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate).withTableName(schema.getTableName()); - - Map allValues = new HashMap<>(); - allValues.putAll(values); - - values.keySet().forEach(fieldName -> { - if (values.get(fieldName).isBlank()) { - allValues.put(fieldName, null); - } - }); - - files.keySet().forEach(f -> { - 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) - allValues.put(f, files.get(f).getBytes()); - } catch (IOException e) { - throw new DbAdminException(e); - } - }); - - if (primaryKey == null) { - insert = insert.usingGeneratedKeyColumns(schema.getPrimaryKey().getName()); - return insert.executeAndReturnKey(allValues); - } else { - insert.execute(allValues); - return primaryKey; - } + Object save = save(schema, obj); + return new DbObject(save, schema).getPrimaryKeyValue(); +// return save; +// System.out.println(obj); +// Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); +// validator.va +// Set> violations = validator.validate(obj.getUnderlyingInstance()); +// +// if (violations.size() > 0) { +// throw new ConstraintViolationException(violations); +// } +// +// SimpleJdbcInsert insert = new SimpleJdbcInsert(jdbcTemplate).withTableName(schema.getTableName()); +// +// Map allValues = new HashMap<>(); +// allValues.putAll(values); +// +// values.keySet().forEach(fieldName -> { +// if (values.get(fieldName).isBlank()) { +// allValues.put(fieldName, null); +// } +// }); +// +// files.keySet().forEach(f -> { +// 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) +// allValues.put(f, files.get(f).getBytes()); +// } catch (IOException e) { +// throw new DbAdminException(e); +// } +// }); +// +// if (primaryKey == null) { +// insert = insert.usingGeneratedKeyColumns(schema.getPrimaryKey().getName()); +// return insert.executeAndReturnKey(allValues); +// } else { +// insert.execute(allValues); +// return primaryKey; +// } } diff --git a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java index 0bbb435..de676ad 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java +++ b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.GeneratedValue; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; @@ -208,6 +209,12 @@ public class DbField { 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. * a field that is not a relationship to another entity; @@ -220,6 +227,10 @@ public class DbField { && getPrimitiveField().getAnnotation(ManyToMany.class) == null; } + public boolean isGeneratedValue() { + return getPrimitiveField().getAnnotation(GeneratedValue.class) != null; + } + public Set getAllValues() { List findAll = schema.getJpaRepository().findAll(); return findAll.stream() diff --git a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObject.java b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObject.java index b23dac2..9ada87d 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObject.java +++ b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObject.java @@ -32,7 +32,6 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import tech.ailef.dbadmin.external.annotations.DisplayName; import tech.ailef.dbadmin.external.exceptions.DbAdminException; -import tech.ailef.dbadmin.external.misc.Utils; /** * 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) { Method setter = findSetter(fieldName); @@ -229,4 +251,11 @@ public class DbObject { return null; } + + @Override + public String toString() { + return "DbObject [instance=" + instance + ", schema=" + schema + "]"; + } + + } diff --git a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObjectSchema.java b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObjectSchema.java index 512ad35..159fa80 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObjectSchema.java +++ b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbObjectSchema.java @@ -384,6 +384,10 @@ public class DbObjectSchema { if (parsedFieldValue != null && getFieldByName(param).isSettable()) { setter.invoke(instance, parsedFieldValue); } + + if (parsedFieldValue != null && getFieldByName(param).isToOne()) { + dbObject.setRelationship(param, parsedFieldValue); + } } for (String fileParam : files.keySet()) { diff --git a/src/main/java/tech/ailef/dbadmin/external/dto/ValidationErrorsContainer.java b/src/main/java/tech/ailef/dbadmin/external/dto/ValidationErrorsContainer.java index 01265bd..5786cb2 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dto/ValidationErrorsContainer.java +++ b/src/main/java/tech/ailef/dbadmin/external/dto/ValidationErrorsContainer.java @@ -29,4 +29,11 @@ public class ValidationErrorsContainer { public boolean isEmpty() { return errors.isEmpty(); } + + @Override + public String toString() { + return "ValidationErrorsContainer [errors=" + errors + "]"; + } + + } diff --git a/src/main/resources/templates/model/create.html b/src/main/resources/templates/model/create.html index 6d906d4..6b920f7 100644 --- a/src/main/resources/templates/model/create.html +++ b/src/main/resources/templates/model/create.html @@ -31,6 +31,7 @@