✅복습
조회할때->em.createQuery(sql문쓰기)
연관관계 매핑
mappedBy속성
-> 주인 지정
외래키가 있는곳을 연관관계 주인 무조건.😊
데이터베이스에 외래 키가 있는 테이블을 수정하려면 연관 관계의 주인만 변경하는 것이 맞는가? 맞습니다.
@DiscriminatorValue -> 구분자
begin, commit을 자동으로 수행해준다.
예외 발생 시 rollback 처리를 자동으로 수행해준다.
Transaction은 4가지 성질을 가지고 있다; 원자성, 일관성, 격리성, 영속성
중간에 Exception이 발생해서 Service 로직이 비정상종료되면 이전에 완료한 작업을 모두 rollback시키기 위함
logger 자동으로 만들어줘서 바로 log.info 로 사용 가능
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 MemeberRepository {
@Autowired
private final EntityManager em;
public MemeberRepository(EntityManager em) {
this.em = em;
}
//맴버저장
public void memberSave(Member member) {
em.persist(member);
}
}
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.MemeberRepository;
@Service
@Transactional
public class MemberService {
//멤버레파지토리 연결시킴
// 생성자 Injection 많이 사용, 생성자가 하나면 생략 가능
private final MemeberRepository memeberRepository;
@Autowired
public MemberService(MemeberRepository memeberRepository ) {
this.memeberRepository= memeberRepository; //맴버서비스에 repository를 가지고 들어감
}
//저장
public Long memberSave(Member member) {
System.out.println("MemberService memberSave Before. . . ");
memeberRepository.memberSave(member);
System.out.println("MemberService memberSave after member.getId()-->" +member.getId());
return member.getId();
}
}
- 생성자주입의 단점은 위의 Constructor(생성자) 코드처럼 생성자를 만들기 번거롭다는 것이다. 하지만 이를 보완하기위해 롬복을 사용하여 간단한 방법으로 생성자 주입 방식의 코딩을 할 수 있다.
final이 붙거나 @NotNull 이 붙은 필드의 생성자를 자동 생성해주는 롬복 어노테이션
- 어떠한 빈(Bean)에 생성자가 오직 하나만 있고, 생성자의 파라미터 타입이 빈으로 등록 가능한 존재라면 이 빈은
@Autowired 어노테이션 없이
도 의존성 주입이 가능하다.
@RequiredArgsConstructor를 사용하지 않으면 원래는 이렇게 생성자 주입을 해야한다
//멤버저장
package com.oracle.oBootJpa03.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.oracle.oBootJpa03.form.MemberForm;
import com.oracle.oBootJpa03.service.MemberService;
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
//멤버서비스를 가지고 들어옴
this.memberService = memberService;
}
@GetMapping(value = "/members/new")
public String createFrom(Model model) {
model.addAttribute("memberForm", new MemberForm());
return "members/creatMemberForm";
}
}
🔼위랑 같지만 다른 방법
참고 *
memberForm이라고 쓰는 이유 --> 화면단 어노테이션 @Entity 랑 같이쓰지말고 빼기
@NotEmpty 써준이유 ->Validation때문
@Valid쓰면 BindResult써주기 --->결과값끌고옴
form에 th:object걸어주면 바로 받을수 있음
써주면 널값이라도 들어감
package com.oracle.oBootJpa03.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
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;
//@Autowired
//public MemberController(MemberService memberService) {
//멤버서비스를 가지고 들어옴
// this.memberService = memberService;
// }
//회원가입화면
@GetMapping(value = "/members/new")
public String createFrom(Model model) {
System.out.println(" MemberController/members/new start. . . ");
log.info("/members/new Slf4j. . . ");
model.addAttribute("memberForm", new MemberForm());
return "members/creatMemberForm";
}
//회원가입
@PostMapping(value = "/member/Save")
public String memberSave(@Valid MemberForm form, BindingResult result) {
if(result.hasErrors()) { //에러가 발생하면 creatMemberForm 로 바로보냄 보내서 에러메시지 띄움
return "members/creatMemberForm" ;
} //form있는거 하나씩 꺼내쓸수있음
Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode()); //address만든후
Member member = new Member(); //멤버 생성후
member.setName(form.getName()); //멤버에 넣기
member.setAddress(address);
memberService.memberSave(member); // 멤버서비스에 넣었던 멤버 저장
return "redirect:/";
}
}
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="form" action="/member/Save" 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-conbtrol' ">
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect data</p>
</div>
<div class="form-group">
<lable th:for="city">도시</lable>
<input type="text" th:field="*{city}" class="form-control" placeholder="도시를 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="street">거리</lable>
<input type="text" th:field="*{street}" class="form-control" placeholder="도시를 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="zipcode">우편번호</lable>
<input type="text" th:field="*{zipcode}" class="form-control" placeholder="도시를 입력하세요 " >
</div>
<button type="submit" class="btn btn-primary">Submit</button>
//서브밋해주면 Controller에 /member/Save로 이동
</form>
</div>
</body>
</html>
🔽에러 설명
MemberForm에 valid를 통해 에러를 검증하고 다시보내서 에러를 뿌려줌
🔽입력하면 db에 저장됨
//회원목록
@GetMapping(value = "/members")
public String memberList(Model model) {
log.info("members memberList. . . ");
List<Member> members = memberService.findMembers();
//Repository--> findAll()
model.addAttribute ( "members" ,members);
return "members/memerList";
}
<!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>
<td>#</td>
<td>이름</td>
<td>도시</td>
<td>주소</td>
<td>우편번호</td>
</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>
</tbody>
</table>
</div>
</body>
</html>
//전체회원조회
public List<Member> findMembers() {
List<Member> listMembers= memeberRepository.findAll();
System.out.println("MemeberRepository findMember ListMembers size()->"+listMembers.size());
return listMembers;
}
//현실 ->압축해서 한줄로 사용가능
public List<Member> findAll2() {
return em.createQuery("select m from Member m" ,Member.class)
.getResultList();
}
//기초
public List<Member> findAll() {
List<Member> listMembers =em.createQuery("select m from Member m" ,Member.class)
.getResultList();
System.out.println("JpaMemberRepository findAll memberList.size()->"+listMembers.size());
return listMembers;
}
기본연결
package com.oracle.oBootJpa03.repository;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.oracle.oBootJpa03.domain.item.Item;
import lombok.RequiredArgsConstructor;
//2
@Repository
//3 생성자자동주입
@RequiredArgsConstructor
public class ItemRepository { //1
private final EntityManager em;
//4
public void itemSave(Item item) {
em.persist(item);
}
}
package com.oracle.oBootJpa03.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.oracle.oBootJpa03.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor //생성자 자동주입
@Transactional //service에선 트랜잭션 연결해주기
public class ItemService {
private final ItemRepository itemRepository; //레파지토리 연결
}
package com.oracle.oBootJpa03.controller;
import org.springframework.stereotype.Controller;
import com.oracle.oBootJpa03.service.ItemService;
import lombok.RequiredArgsConstructor;
@Controller
@RequiredArgsConstructor //자동 생성자 주입
public class ItemController {
private final ItemService itemService; //service연결
@GetMapping(value = "/items/new")
public String createForm(Model model) {
model.addAttribute("form", new BookForm());
return "items/createItemForm";
-->화면단으로 받으면 따로 빼기
}
}
▶화면단으로 받으면 따로 빼기
package com.oracle.oBootJpa03.form;
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 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-control" placeholder="이름을 입력하세요" >
</div>
<div class="form-group">
<lable th:for="price">가격</lable>
<input type="number" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="stockQuantity">수량</lable>
<input type="number" th:field="*{stockQuantity}" class="form-control" placeholder="도시를 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="author">저자</lable>
<input type="text" th:field="*{author}" class="form-control" placeholder="저자를 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="isbn">ISBN</lable>
<input type="text" th:field="*{isbn}" class="form-control" placeholder="ISBN을 입력하세요 " >
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</body>
</html>
//상품저장
@PostMapping(value = "items/save")
public String itemSave(BookForm form) {
//1.BookForm ->book
//2.itemService ->saveItem
//3 ItemRepository.itemSave
System.out.println("ItemController itemSave start. . . ");
Book book = new Book();
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
itemService.saveItem(book); //book을 저장할 페이지에 저장
return "redirect:/";
}
public void saveItem(Item item) { //book은 item을 상속받았기대문에 item으로 받을 수 잇음
itemRepository.itemSave(item);
}
public void saveItem(Item item) {
em.persist(item);
}
▶ 다입력하고 서브밋하면 오류가뜬다
▶오류뜬 이유
▶오류수정하기
❓@GeneratedValue 입력해주니 오류 해결됨
왜 ? -> 시퀸스로 잡기위해서
▶실행하면 DB로 저장되어짐
▶@DiscriminatorValue("B") 구분자 설정 해줬기 때문에 B로 뜸
<!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>
<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>
//상품목록
@GetMapping(value = "/items")
public String itemList(Model model) {
// 1. List<Item> items --> itemService 안에 findItems();
// 2. itemService --> itemRepository.findAll()
// 3. items --> Model
System.out.println("ItemController itemList . . . ");
List<Item> items = itemService.findItems();
model.addAttribute("items" ,items);
return "items/itemList";
}
//상품조회
public List<Item> findItems() {
List<Item> itemList = itemRepository.findAll();
System.out.println("ItemRepository findMember ListMembers size()->"+itemList.size());
return itemList;
}
public List<Item> findAll() {
return em.createQuery("select i from Item i", Item.class)
.getResultList();
}
<!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:object="${form}" method="post">
<!-- id -->
<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">
<lable th:for="price">가격</lable>
<input type="number" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="stockQuantity">수량</lable>
<input type="number" th:field="*{stockQuantity}" class="form-control" placeholder="도시를 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="author">저자</lable>
<input type="text" th:field="*{author}" class="form-control" placeholder="저자를 입력하세요 " >
</div>
<div class="form-group">
<lable th:for="isbn">ISBN</lable>
<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>
//상품수정
@GetMapping(value = "/items/{itemId}/edit") //itemid로 넘어오는 값
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";
}
//상품수정 화면
//book ->item (아버지) 으로 바꿔주기
public Item findOne(Long itemId) {
Item item = itemRepository.findOne(itemId);//받은거 그대로 담아서 넘기기
return item;
}
//상품수정 화면
public Item findOne(Long id) {
//하나니깐 find해주면 됨
System.out.println("ItemRepository findOne id-> "+id);
Item item = em.find(Item.class, id);
return item;
}