Logger anotation -> @Slf4j
생성자 -> @RequiredArgsConstructor
package com.oracle.oBootJpa03.controller;
import java.util.List;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootJpa03.domain.Address;
import com.oracle.oBootJpa03.domain.Member;
import com.oracle.oBootJpa03.form.MemberForm;
import com.oracle.oBootJpa03.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
// public MemberController(MemberService memberService) {
// this.memberService = memberService;
// }
// 회원가입이동
@GetMapping(value = "/members/new")
public String createForm(Model model) {
System.out.println("MemberController /members/new Start...");
log.info("/members/new Start...");
model.addAttribute("memberForm", new MemberForm());
return "/members/createMemberForm";
}
// 회원저장
@PostMapping(value = "/member/Save")
public String memberSave(@Valid MemberForm form, BindingResult result) {
System.out.println("MemberService memberSave Start...");
if(result.hasErrors()) {
return "members/createMemberForm";
}
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.memberSave(member);
System.out.println("MemberController memberSave After");
return "redirect:/";
}
}
package com.oracle.oBootJpa03.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.domain.Member;
import com.oracle.oBootJpa03.repository.MemberRepository;
@Service
@Transactional
public class MemberService {
@Autowired
private final MemberRepository memberRepository;
// 생성자 Injection 많이 사용, 생성자가 하나면 생략 가능
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 회원저장
public Long memberSave(Member member) {
System.out.println("MemberService memberSave Before...");
memberRepository.memberSave(member);
System.out.println("MemberService memberSave After member.getId()->"+member.getId());
return member.getId();
}
}
package com.oracle.oBootJpa03.repository;
import javax.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa03.domain.Member;
@Repository
public class MemberRepository {
private final EntityManager em;
@Autowired
public MemberRepository(EntityManager em) {
this.em = em;
}
public void memberSave(Member member) {
em.persist(member);
}
}
@ToString을 걸어줘서 객체 타입 확인
package com.oracle.oBootJpa03.domain;
import javax.persistence.Embeddable;
import lombok.Getter;
import lombok.ToString;
/*
* 1. 값 타입은 변경 불가능하게 설계.
* 2. @Setter 를 제거하고, 생성자에서 값을 모두 초기화해서 변경 불가능한 클래스를 만들기 권장
*/
@Getter
@ToString
// 값 타입을 정의하는 곳에 표시
@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;
protected Address() {
}
public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
}
Entity -> 테이블에 대한 속성(영구적)
화면(레이아웃)에 대한 유효성검사는 따로 클래스를 만드는 것이 좋다.(한정적)
package com.oracle.oBootJpa03.form;
import javax.validation.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MemberForm {
@NotEmpty
private String name;
private String city;
private String street;
private String zipcode;
}
<!DOCTYPE html>
<html xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<form role="fomr" action="/memberSave" th:object="${memberForm}" method="post">
<div class="form-group">
<label th:for="name">이름</label>
<input type="text" th:field="*{name}" class="form-control" placeholder="이름을 입력하세요"
th:class="${#fields.hasErrors('name')}? 'form-control fieldError' : 'form-control'">
<p th:if="${#fields.hasErrors('name')}" th:error="*{name}">Incorrect name</p>
</div>
<div class="form-group">
<label th:for="city">도시</label>
<input type="text" th:field="*{city}" class="form-control" placeholder="도시를 입력하세요">
</div>
<div class="form-group">
<label th:for="street">거리</label>
<input type="text" th:field="*{street}" class="form-control" placeholder="거리를 입력하세요">
</div>
<div class="form-group">
<label th:for="zipcode">우편번호</label>
<input type="text" th:field="*{zipcode}" class="form-control" placeholder="우편번호를 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
id가 계속 null 값으로 출력됨
-> 🤔이유 : pk인 id에 시퀀스가 생성❌
✍해결방법
-> Member 클래스에 시퀀스를 생성하고 @GeneratedValue 선언
package com.oracle.oBootJpa03.domain;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
@SequenceGenerator(
name = "member3_seq_gen",
sequenceName = "member3_seq_generator",
initialValue = 1,
allocationSize = 1
)
@Table(name = "member3")
public class Member {
@Id
@Column(name = "member_id")
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "member3_seq_gen")
private Long id;
private String name;
@Embedded
private Address address;
// Order Entity의 member field에 의해서 mapper 당함 --> 읽기 전용
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
<!DOCTYPE html>
<html xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<table class="table table-striped" border="1">
<thead>
<tr>
<th>#</th>
<th>이름</th>
<th>도시</th>
<th>주소</th>
<th>우편번호</th>
</tr>
</thead>
<tbody>
<!-- 타임리프에서 ?를 사용하면 null 을 무시 -->
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
<td th:text="${member.address?.city}"></td>
<td th:text="${member.address?.street}"></td>
<td th:text="${member.address?.zipcode}"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
package com.oracle.oBootJpa03.controller;
import java.util.List;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootJpa03.domain.Address;
import com.oracle.oBootJpa03.domain.Member;
import com.oracle.oBootJpa03.form.MemberForm;
import com.oracle.oBootJpa03.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
// public MemberController(MemberService memberService) {
// this.memberService = memberService;
// }
// 회원가입이동
@GetMapping(value = "/members/new")
public String createForm(Model model) {
System.out.println("MemberController /members/new Start...");
log.info("/members/new Start...");
model.addAttribute("memberForm", new MemberForm());
return "/members/createMemberForm";
}
// 회원저장
@PostMapping(value = "/member/Save")
public String memberSave(@Valid MemberForm form, BindingResult result) {
System.out.println("MemberService memberSave Start...");
if(result.hasErrors()) {
return "members/createMemberForm";
}
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.memberSave(member);
System.out.println("MemberController memberSave After");
return "redirect:/";
}
// 전체회원목록
@GetMapping(value = "/members")
public String memberList(Model model) {
log.info("members memberList...");
List<Member> members = memberService.findMembers();
model.addAttribute("members", members);
return "members/memberList";
}
}
package com.oracle.oBootJpa03.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.domain.Member;
import com.oracle.oBootJpa03.repository.MemberRepository;
@Service
@Transactional
public class MemberService {
@Autowired
private final MemberRepository memberRepository;
// 생성자 Injection 많이 사용, 생성자가 하나면 생략 가능
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 회원저장
public Long memberSave(Member member) {
System.out.println("MemberService memberSave Before...");
memberRepository.memberSave(member);
System.out.println("MemberService memberSave After member.getId()->"+member.getId());
return member.getId();
}
// 전체회원목록
public List<Member> findMembers() {
System.out.println("MemberService findMembers Before");
List<Member> listMember = memberRepository.findAll();
System.out.println("MemberService findMembers After listMember.size()->"+listMember.size());
return listMember;
}
}
package com.oracle.oBootJpa03.repository;
import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa03.domain.Member;
@Repository
public class MemberRepository {
private final EntityManager em;
@Autowired
public MemberRepository(EntityManager em) {
this.em = em;
}
// 회원가입
public void memberSave(Member member) {
em.persist(member);
}
// 전체회원목록
public List<Member> findAll() {
System.out.println("MemberRepository findAll Start...");
List<Member> memberList = em.createQuery("select m from Member m", Member.class)
.getResultList();
System.out.println("MemberRepository findAll After memberList.size()->"+memberList.size());
return memberList;
}
}
package com.oracle.oBootJpa03.form;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class BookForm {
private Long id;
private String name;
private int price;
private int stockQuantity;
private String author;
private String isbn;
}
<!DOCTYPE html>
<html xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<form th:action="@{/items/save}" th:object="${form}" method="post">
<div class="form-group">
<label th:for="name">상품명</label>
<input type="text" th:field="*{name}" class="form-contrl" placeholder="이름을 입력하세요">
</div>
<div class="form-group">
<label th:for="price">가격</label>
<input type="number" th:field="*{price}" class="form-contrl" placeholder="가격을 입력하세요">
</div>
<div class="form-group">
<label th:for="stockQuantity">수량</label>
<input type="number" th:field="*{stockQuantity}" class="form-contrl" placeholder="수량을 입력하세요">
</div>
<div class="form-group">
<label th:for="author">저지</label>
<input type="text" th:field="*{author}" class="form-contrl" placeholder="저자를 입력하세요">
</div>
<div class="form-group">
<label th:for="isbn">ISBN</label>
<input type="text" th:field="*{isbn}" class="form-contrl" placeholder="ISBN을 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
package com.oracle.oBootJpa03.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootJpa03.domain.item.Book;
import com.oracle.oBootJpa03.form.BookForm;
import com.oracle.oBootJpa03.service.ItemService;
import lombok.RequiredArgsConstructor;
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
// 상품등록폼이동
@GetMapping(value = "/items/new")
public String createForm(Model model) {
System.out.println("ItemController /items/new Start...");
model.addAttribute("form", new BookForm());
return "items/createItemForm";
}
// 상품등록
@PostMapping(value = "/items/save")
public String itemSave(BookForm form) {
System.out.println("ItemController /items/save Start...");
Book book = new Book();
// 1. BookForm -> Book
book.setId(form.getId());
book.setName(form.getName());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
itemService.saveItem(book);
return "redirect:/";
}
}
package com.oracle.oBootJpa03.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.domain.item.Item;
import com.oracle.oBootJpa03.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
@Service
@Transactional
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
// 상품등록
public void saveItem(Item item) {
System.out.println("ItemService saveItem Start...");
itemRepository.itemSave(item);
System.out.println("ItemService saveItem After...");
}
}
package com.oracle.oBootJpa03.repository;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa03.domain.item.Item;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor
public class ItemRepository {
private final EntityManager em;
// 상품등록
public void itemSave(Item item) {
em.persist(item);
}
}
😡시퀀스가 없어 id가 null로 출력
✍해결방법 : 시퀀스 create
-> @GeneratedValue 이렇게 하면 JPA에서 자동으로 시퀀스를 생성한다(HIBERNATE_SEQUENCE)
package com.oracle.oBootJpa03.domain.item;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import lombok.Getter;
import lombok.Setter;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter
@Setter
public abstract class Item {
@Id
@GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
private int price;
private int stockQuantity;
}
<!DOCTYPE html>
<html xml:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>상품명</th>
<th>가격</th>
<th>재고수량</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${items}">
<td th:text="${item.id}"></td>
<td th:text="${item.name}"></td>
<td th:text="${item.price}"></td>
<td th:text="${item.stockQuantity}"></td>
<td><a href="#" th:href="@{/items/{id}/edit (id=${item.id})}" class="btn btn-primary" role="button">수정</a></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
package com.oracle.oBootJpa03.controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootJpa03.domain.item.Book;
import com.oracle.oBootJpa03.domain.item.Item;
import com.oracle.oBootJpa03.form.BookForm;
import com.oracle.oBootJpa03.service.ItemService;
import lombok.RequiredArgsConstructor;
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
// 상품등록폼이동
@GetMapping(value = "/items/new")
public String createForm(Model model) {
System.out.println("ItemController /items/new Start...");
model.addAttribute("form", new BookForm());
return "items/createItemForm";
}
// 상품등록
@PostMapping(value = "/items/save")
public String itemSave(BookForm form) {
System.out.println("ItemController /items/save Start...");
Book book = new Book();
// 1. BookForm -> Book
book.setId(form.getId());
book.setName(form.getName());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
itemService.saveItem(book);
return "redirect:/";
}
// 상품조회
@GetMapping(value = "/items")
public String itemList(Model model) {
System.out.println("ItemController /items Start...");
List<Item> items = itemService.findItems();
model.addAttribute("items", items);
return "items/itemList";
}
}
package com.oracle.oBootJpa03.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.domain.item.Item;
import com.oracle.oBootJpa03.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
@Service
@Transactional
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
public void saveItem(Item item) {
System.out.println("ItemService saveItem Start...");
itemRepository.itemSave(item);
System.out.println("ItemService saveItem After...");
}
public List<Item> findItems() {
System.out.println("ItemService findItems Start...");
List<Item> itemList = itemRepository.findAll();
System.out.println("ItemService findItems After itemList.size()->"+itemList.size());
return itemList;
}
}
package com.oracle.oBootJpa03.repository;
import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa03.domain.item.Item;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor
public class ItemRepository {
private final EntityManager em;
// 상품등록
public void itemSave(Item item) {
System.out.println("ItemRepository itemSave Start...");
em.persist(item);
}
public List<Item> findAll() {
System.out.println("ItemRepository findAll Start...");
List<Item> itemList = em.createQuery("select i from Item i",Item.class)
.getResultList();
System.out.println("ItemRepository itemList.size()->"+itemList.size());
return itemList;
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div class="container">
<form th:object="${form}" method="post">
<input type="hidden" th:field="*{id}">
<div class="form-group">
<label th:for="name">상품명</label>
<input type="text" th:field="*{name}" class="form-control" placeholder="이름을 입력하세요">
</div>
<div class="form-group">
<label th:for="price">가격</label>
<input type="number" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">
</div>
<div class="form-group">
<label th:for="stockQuantity">수량</label>
<input type="number" th:field="*{stockQuantity}" class="form-control" placeholder="수량을 입력하세요">
</div>
<div class="form-group">
<label th:for="author">저자</label>
<input type="text" th:field="*{author}" class="form-control" placeholder="저자를 입력하세요">
</div>
<div class="form-group">
<label th:for="isbn">ISBN</label>
<input type="text" th:field="*{isbn}" class="form-control" placeholder="ISBN을 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
package com.oracle.oBootJpa03.controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import com.oracle.oBootJpa03.domain.item.Book;
import com.oracle.oBootJpa03.domain.item.Item;
import com.oracle.oBootJpa03.form.BookForm;
import com.oracle.oBootJpa03.service.ItemService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
// 상품등록폼이동
@GetMapping(value = "/items/new")
public String createForm(Model model) {
System.out.println("ItemController /items/new Start...");
model.addAttribute("form", new BookForm());
return "items/createItemForm";
}
// 상품등록
@PostMapping(value = "/items/save")
public String itemSave(BookForm form) {
System.out.println("ItemController /items/save Start...");
Book book = new Book();
// 1. BookForm -> Book
book.setId(form.getId());
book.setName(form.getName());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
itemService.saveItem(book);
return "redirect:/";
}
// 상품조회
@GetMapping(value = "/items")
public String itemList(Model model) {
System.out.println("ItemController /items Start...");
List<Item> items = itemService.findItems();
model.addAttribute("items", items);
return "items/itemList";
}
// 상품수정폼이동
@GetMapping(value = "/items/{itemId}/edit")
public String updateItemForm(@PathVariable("itemId") Long itemId, Model model) {
log.info("itemId->{}",itemId);
Book item = (Book) itemService.findOne(itemId);
BookForm form = new BookForm();
form.setId(item.getId());
form.setName(item.getName());
form.setPrice(item.getPrice());
form.setStockQuantity(item.getStockQuantity());
form.setAuthor(item.getAuthor());
form.setIsbn(item.getIsbn());
model.addAttribute("form", form);
return "items/updateItemForm";
}
}
package com.oracle.oBootJpa03.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.domain.item.Item;
import com.oracle.oBootJpa03.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
@Service
@Transactional
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository itemRepository;
// 상품등록
public void saveItem(Item item) {
System.out.println("ItemService saveItem Start...");
itemRepository.itemSave(item);
System.out.println("ItemService saveItem After...");
}
// 상품조회
public List<Item> findItems() {
System.out.println("ItemService findItems Start...");
List<Item> itemList = itemRepository.findAll();
System.out.println("ItemService findItems After itemList.size()->"+itemList.size());
return itemList;
}
// 상품수정폼이동
public Item findOne(Long itemId) {
System.out.println("ItemService findOne Start...");
Item item = itemRepository.findOne(itemId);
System.out.println("ItemService findOne After");
return item;
}
}
package com.oracle.oBootJpa03.repository;
import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa03.domain.item.Item;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor
public class ItemRepository {
private final EntityManager em;
// 상품등록
public void itemSave(Item item) {
System.out.println("ItemRepository itemSave Start...");
em.persist(item);
}
// 상품조회
public List<Item> findAll() {
System.out.println("ItemRepository findAll Start...");
List<Item> itemList = em.createQuery("select i from Item i",Item.class)
.getResultList();
System.out.println("ItemRepository itemList.size()->"+itemList.size());
return itemList;
}
// 상품수정폼이동
public Item findOne(Long id) {
System.out.println("ItemRepository findOne id->"+id);
Item item = em.find(Item.class, id);
System.out.println("ItemRepository findOne After");
return item;
}
}