springboot_validation : bean validation - dto
This commit is contained in:
@@ -12,18 +12,18 @@ import javax.validation.constraints.NotNull;
|
||||
//@ScriptAssert(lang = "javascript", script = "_this.price * _this.quantity >= 10000", message = "총 합은 10000원 이상 이다.")
|
||||
public class Item {
|
||||
|
||||
@NotNull(groups = UpdateCheck.class) // 수정 시 요구사항 추가
|
||||
// @NotNull(groups = UpdateCheck.class) // 수정 시 요구사항 추가
|
||||
private Long id;
|
||||
|
||||
@NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
|
||||
// @NotBlank(groups = {SaveCheck.class, UpdateCheck.class})
|
||||
private String itemName;
|
||||
|
||||
@NotNull(groups = {SaveCheck.class, UpdateCheck.class})
|
||||
@Range(min = 1000, max = 1000000, groups = {SaveCheck.class, UpdateCheck.class})
|
||||
// @NotNull(groups = {SaveCheck.class, UpdateCheck.class})
|
||||
// @Range(min = 1000, max = 1000000, groups = {SaveCheck.class, UpdateCheck.class})
|
||||
private Integer price;
|
||||
|
||||
@NotNull(groups = {SaveCheck.class, UpdateCheck.class})
|
||||
@Max(value = 9999, groups = {SaveCheck.class})
|
||||
// @NotNull(groups = {SaveCheck.class, UpdateCheck.class})
|
||||
// @Max(value = 9999, groups = {SaveCheck.class})
|
||||
private Integer quantity;
|
||||
|
||||
public Item() {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package hello.itemservice.web.validation;
|
||||
|
||||
import hello.itemservice.domain.item.Item;
|
||||
import hello.itemservice.domain.item.ItemRepository;
|
||||
import hello.itemservice.domain.item.SaveCheck;
|
||||
import hello.itemservice.domain.item.UpdateCheck;
|
||||
import hello.itemservice.web.validation.form.ItemSaveForm;
|
||||
import hello.itemservice.web.validation.form.ItemUpdateForm;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/validation/v4/items")
|
||||
@RequiredArgsConstructor
|
||||
public class ValidationItemControllerV4 {
|
||||
|
||||
private final ItemRepository itemRepository;
|
||||
|
||||
@GetMapping
|
||||
public String items(Model model) {
|
||||
List<Item> items = itemRepository.findAll();
|
||||
model.addAttribute("items", items);
|
||||
return "validation/v4/items";
|
||||
}
|
||||
|
||||
@GetMapping("/{itemId}")
|
||||
public String item(@PathVariable long itemId, Model model) {
|
||||
Item item = itemRepository.findById(itemId);
|
||||
model.addAttribute("item", item);
|
||||
return "validation/v4/item";
|
||||
}
|
||||
|
||||
@GetMapping("/add")
|
||||
public String addForm(Model model) {
|
||||
model.addAttribute("item", new Item());
|
||||
return "validation/v4/addForm";
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
public String addItem(@Validated @ModelAttribute("item") ItemSaveForm form, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
|
||||
|
||||
if (form.getPrice() != null && form.getQuantity() != null ) {
|
||||
int resultPrice = form.getPrice() * form.getQuantity();
|
||||
if (resultPrice < 10000) {
|
||||
bindingResult.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
|
||||
}
|
||||
}
|
||||
|
||||
// 검증 실패시
|
||||
if (bindingResult.hasErrors()) {
|
||||
return "validation/v4/addForm";
|
||||
}
|
||||
|
||||
Item item = new Item();
|
||||
item.setItemName(form.getItemName());
|
||||
item.setPrice(form.getPrice());
|
||||
item.setQuantity(form.getQuantity());
|
||||
|
||||
// 검증 성공시
|
||||
Item savedItem = itemRepository.save(item);
|
||||
redirectAttributes.addAttribute("itemId", savedItem.getId());
|
||||
redirectAttributes.addAttribute("status", true);
|
||||
return "redirect:/validation/v4/items/{itemId}";
|
||||
}
|
||||
|
||||
@GetMapping("/{itemId}/edit")
|
||||
public String editForm(@PathVariable Long itemId, Model model) {
|
||||
Item item = itemRepository.findById(itemId);
|
||||
model.addAttribute("item", item);
|
||||
return "validation/v4/editForm";
|
||||
}
|
||||
|
||||
@PostMapping("/{itemId}/edit")
|
||||
public String edit(@PathVariable Long itemId,
|
||||
@Validated @ModelAttribute("item") ItemUpdateForm form, BindingResult bindingResult) {
|
||||
if (form.getPrice() != null && form.getQuantity() != null ) {
|
||||
int resultPrice = form.getPrice() * form.getQuantity();
|
||||
if (resultPrice < 10000) {
|
||||
bindingResult.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (bindingResult.hasErrors()) {
|
||||
return "validation/v4/editForm";
|
||||
}
|
||||
|
||||
Item itemParam = new Item();
|
||||
itemParam.setItemName(form.getItemName());
|
||||
itemParam.setPrice(form.getPrice());
|
||||
itemParam.setQuantity(form.getQuantity());
|
||||
itemRepository.update(itemId, itemParam);
|
||||
return "redirect:/validation/v4/items/{itemId}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package hello.itemservice.web.validation.form;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class ItemSaveForm {
|
||||
|
||||
@NotBlank
|
||||
private String itemName;
|
||||
|
||||
@NotNull
|
||||
@Range(min = 1000, max = 1000000)
|
||||
private Integer price;
|
||||
|
||||
@NotNull
|
||||
@Max(value = 9999)
|
||||
private Integer quantity;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package hello.itemservice.web.validation.form;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Range;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class ItemUpdateForm {
|
||||
|
||||
@NotNull
|
||||
private Long id;
|
||||
|
||||
@NotBlank
|
||||
private String itemName;
|
||||
|
||||
@NotNull
|
||||
@Range(min = 1000, max = 1000000)
|
||||
private Integer price;
|
||||
|
||||
private Integer quantity;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link th:href="@{/css/bootstrap.min.css}"
|
||||
href="../css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.container {
|
||||
max-width: 560px;
|
||||
}
|
||||
.field-error {
|
||||
border-color: #dc3545;
|
||||
color: #dc3545;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="py-5 text-center">
|
||||
<h2 th:text="#{page.addItem}">상품 등록</h2>
|
||||
</div>
|
||||
|
||||
<form action="item.html" th:action th:object="${item}" method="post">
|
||||
|
||||
<div th:if="${#fields.hasGlobalErrors()}">
|
||||
<p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">글로벌 오류 메시지</p>
|
||||
</div>
|
||||
<div>
|
||||
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
|
||||
<input type="text" id="itemName" th:field="*{itemName}"
|
||||
th:errorclass="field-error" class="form-control" placeholder="이름을 입력하세요">
|
||||
<div class="field-error" th:errors="*{itemName}">
|
||||
상품명 오류
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="price" th:text="#{label.item.price}">가격</label>
|
||||
<input type="text" id="price" th:field="*{price}"
|
||||
th:errorclass="field-error" class="form-control" placeholder="가격을 입력하세요">
|
||||
<div class="field-error" th:errors="*{price}">
|
||||
가격 오류
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
|
||||
<input type="text" id="quantity" th:field="*{quantity}"
|
||||
th:errorclass="field-error" class="form-control" placeholder="수량을 입력하세요">
|
||||
<div class="field-error" th:errors="*{quantity}">
|
||||
수량 오류
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">상품 등록</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button class="w-100 btn btn-secondary btn-lg"
|
||||
onclick="location.href='items.html'"
|
||||
th:onclick="|location.href='@{/validation/v4/items}'|"
|
||||
type="button" th:text="#{button.cancel}">취소</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div> <!-- /container -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link th:href="@{/css/bootstrap.min.css}"
|
||||
href="../css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.container {
|
||||
max-width: 560px;
|
||||
}
|
||||
.field-error {
|
||||
border-color: #dc3545;
|
||||
color: #dc3545;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="py-5 text-center">
|
||||
<h2 th:text="#{page.updateItem}">상품 수정</h2>
|
||||
</div>
|
||||
|
||||
<form action="item.html" th:action th:object="${item}" method="post">
|
||||
|
||||
<div th:if="${#fields.hasGlobalErrors()}">
|
||||
<p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">글로벌 오류 메시지</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="id" th:text="#{label.item.id}">상품 ID</label>
|
||||
<input type="text" id="id" th:field="*{id}" class="form-control" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
|
||||
<input type="text" id="itemName" th:field="*{itemName}" th:errorclass="field-error" class="form-control">
|
||||
<div class="field-error" th:errors="*{itemName}">
|
||||
상품명 오류
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="price" th:text="#{label.item.price}">가격</label>
|
||||
<input type="text" id="price" th:field="*{price}" th:errorclass="field-error" class="form-control">
|
||||
<div class="field-error" th:errors="*{price}">
|
||||
가격 오류
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
|
||||
<input type="text" id="quantity" th:field="*{quantity}" th:errorclass="field-error" class="form-control">
|
||||
<div class="field-error" th:errors="*{quantity}">
|
||||
수량 오류
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">수정</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button class="w-100 btn btn-secondary btn-lg"
|
||||
onclick="location.href='item.html'"
|
||||
th:onclick="|location.href='@{/validation/v4/items/{itemId}(itemId=${item.id})}'|"
|
||||
type="button" th:text="#{button.cancel}">취소</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div> <!-- /container -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link th:href="@{/css/bootstrap.min.css}"
|
||||
href="../css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.container {
|
||||
max-width: 560px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="py-5 text-center">
|
||||
<h2 th:text="#{page.item}">상품 상세</h2>
|
||||
</div>
|
||||
|
||||
<!-- 추가 -->
|
||||
<h2 th:if="${param.status}" th:text="'저장 완료'"></h2>
|
||||
|
||||
<div>
|
||||
<label for="itemId" th:text="#{label.item.id}">상품 ID</label>
|
||||
<input type="text" id="itemId" name="itemId" class="form-control" value="1" th:value="${item.id}" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
|
||||
<input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label for="price" th:text="#{label.item.price}">가격</label>
|
||||
<input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
|
||||
<input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}" readonly>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="w-100 btn btn-primary btn-lg"
|
||||
onclick="location.href='editForm.html'"
|
||||
th:onclick="|location.href='@{/validation/v4/items/{itemId}/edit(itemId=${item.id})}'|"
|
||||
type="button" th:text="#{page.updateItem}">상품 수정</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button class="w-100 btn btn-secondary btn-lg"
|
||||
onclick="location.href='items.html'"
|
||||
th:onclick="|location.href='@{/validation/v4/items}'|"
|
||||
type="button" th:text="#{button.cancel}">목록으로</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- /container -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link th:href="@{/css/bootstrap.min.css}"
|
||||
href="../css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="container" style="max-width: 600px">
|
||||
<div class="py-5 text-center">
|
||||
<h2 th:text="#{page.items}">상품 목록</h2>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="btn btn-primary float-end"
|
||||
onclick="location.href='addForm.html'"
|
||||
th:onclick="|location.href='@{/validation/v4/items/add}'|"
|
||||
type="button" th:text="#{page.addItem}">상품 등록</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
<div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th th:text="#{label.item.id}">ID</th>
|
||||
<th th:text="#{label.item.itemName}">상품명</th>
|
||||
<th th:text="#{label.item.price}">가격</th>
|
||||
<th th:text="#{label.item.quantity}">수량</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="item : ${items}">
|
||||
<td><a href="item.html" th:href="@{/validation/v4/items/{itemId}(itemId=${item.id})}" th:text="${item.id}">회원id</a></td>
|
||||
<td><a href="item.html" th:href="@{|/validation/v4/items/${item.id}|}" th:text="${item.itemName}">상품명</a></td>
|
||||
<td th:text="${item.price}">10000</td>
|
||||
<td th:text="${item.quantity}">10</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div> <!-- /container -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user