20221101-68 Spring Boot(4)JPA쇼핑몰만들기/회원가입/회원목록/상품등록/상품목록/상품수정

공현지·2022년 11월 1일
0

spring

목록 보기
12/30

✅복습
조회할때->em.createQuery(sql문쓰기)

연관관계 매핑
mappedBy속성 -> 주인 지정
외래키가 있는곳을 연관관계 주인 무조건.😊
데이터베이스에 외래 키가 있는 테이블을 수정하려면 연관 관계의 주인만 변경하는 것이 맞는가? 맞습니다.

@DiscriminatorValue -> 구분자

👉 Transaction 특징

begin, commit을 자동으로 수행해준다.
예외 발생 시 rollback 처리를 자동으로 수행해준다.
Transaction은 4가지 성질을 가지고 있다; 원자성, 일관성, 격리성, 영속성

👉 @Transactional Service 거는이유 ?

중간에 Exception이 발생해서 Service 로직이 비정상종료되면 이전에 완료한 작업을 모두 rollback시키기 위함

@Slf4j

logger 자동으로 만들어줘서 바로 log.info 로 사용 가능


🟢oBootJpa03

MemeberRepository

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);
	}
	
	
}


MemberService

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();
 	}
 
}

✅@RequiredArgsConstructor 어노테이션 사용하여 생성자주입

  • 생성자주입의 단점은 위의 Constructor(생성자) 코드처럼 생성자를 만들기 번거롭다는 것이다. 하지만 이를 보완하기위해 롬복을 사용하여 간단한 방법으로 생성자 주입 방식의 코딩을 할 수 있다.
  • final이 붙거나 @NotNull 이 붙은 필드의 생성자를 자동 생성해주는 롬복 어노테이션
  • 어떠한 빈(Bean)에 생성자가 오직 하나만 있고, 생성자의 파라미터 타입이 빈으로 등록 가능한 존재라면 이 빈은 @Autowired 어노테이션 없이도 의존성 주입이 가능하다.

✔Membercontroller 기존방식

@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";
	 		
	 		
	 	
	}
}



🔼위랑 같지만 다른 방법

✔ Membercontroller ➡@RequiredArgsConstructor 사용하여 생성자주입

참고 *
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:/";	
	 		}
	 		
    
    
}


MemberForm.java

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;
	

}


creatMemberForm.html

<!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에 저장됨

전체회원목록조회

MemberController

	//회원목록
	 		@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";
	 			
	 			
	 		}
	 		

memerList. html

<!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>

MemberService

//전체회원조회
		public List<Member> findMembers() {
			List<Member> listMembers= memeberRepository.findAll();
			System.out.println("MemeberRepository findMember ListMembers size()->"+listMembers.size());
			return listMembers;
		}
 

MemberRepository


//현실 ->압축해서 한줄로 사용가능 
	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;
	}
	


상품등록 화면

기본연결

ItemRepository


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);
	   
	   
   }
}


ItemService

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;		//레파지토리 연결
	

}

ItemController

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";
    				-->화면단으로 받으면  따로 빼기 
	}
 
 

}

▶화면단으로 받으면 따로 빼기

BookForm.java

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;
	
}



createItemForm

<!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>

상품등록

ItemController

//상품저장
	@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:/";
	}

ItemService

public void saveItem(Item item) {  //book은 item을 상속받았기대문에 item으로 받을 수 잇음 
	itemRepository.itemSave(item);
	
}
	

ItemRepository

public void saveItem(Item item) {
	em.persist(item);
	
}


▶ 다입력하고 서브밋하면 오류가뜬다

▶오류뜬 이유

▶오류수정하기
❓@GeneratedValue 입력해주니 오류 해결됨
왜 ? -> 시퀸스로 잡기위해서


▶실행하면 DB로 저장되어짐

▶@DiscriminatorValue("B") 구분자 설정 해줬기 때문에 B로 뜸

상품목록조회

itemList.html

<!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>

ItemController

  //상품목록
	@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";
	}

	

ItemService


//상품조회
public List<Item> findItems() {
	List<Item> itemList = itemRepository.findAll();
	System.out.println("ItemRepository findMember ListMembers size()->"+itemList.size());

	return itemList;
}
	

ItemRepository

public  List<Item> findAll() {

	return em.createQuery("select i from Item i", Item.class)
			.getResultList();
}


상품수정 화면

UpdateitemForm.html

<!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>

ItemController

//상품수정
	@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";
			
	}

ItemService


//상품수정 화면

					//book ->item (아버지) 으로 바꿔주기 
public  Item findOne(Long itemId) {
	  Item item =  itemRepository.findOne(itemId);//받은거 그대로 담아서 넘기기 

	
	return item;
}
	

ItemRepository

//상품수정 화면 
public Item findOne(Long id) {
						//하나니깐 find해주면 됨
	System.out.println("ItemRepository  findOne id-> "+id);
	 
	 Item item = em.find(Item.class, id);
	 return  item;
	
}

0개의 댓글