회원등록

-현재 회원가입 버튼을 누르면 주소창에 members/new처럼 이동 한다.

 

*<a href = ...>

-a라는 태그가 href라는 속성을 가지고 있다.

-hrefhypertext reference의 줄임말이다.

 

1.memberForm 만들기(입력란 만들기)

-여기에서 members/new 회원가입 폼화면이 나와야 한다.

-회원가입 폼화면이 나올 때를 위해 폼객체를 만든다.

 

사진1(실제는 이름만 필수항목 적용)

 

public class MemberForm {

 

@NotEmpty -> javax.validation.constraints

값이 비어있으면 오류메시지를 보여주는 기능

}

 

private final MemberService memberService

Controller가 서비스(MemberService)를 사용한다.

 

*Model model이란?

model.addAttribute(“정보”)

-> controller로 뷰로 넘어갈 때 데이터를 실어서 정보를 넘긴다.

 

return "members/createMemberForm“

->여기로 화면이 출력이 된다.

createMemberForm을 위한 html을 만들어야 한다.

controller가 화면을 이동할 때 빈 memberForm을 가지고 간다,.

validation같은 역할을 해주기 때문에 빈 memberForm을 가지고 가도록 한다.

데이터가 넘어가니 데이터의 화면출력을 위한 createMemberForm.html 생성이 필요하다.

 

2.templates내에 members 디렉터리생성 및 createMemberForm.html 파일생성을 한다.

 

구문사진

화면사진

html 구문 설명

첫구문

a.<head> include style로 작성

 

b.form 태그가 존재한다.

-action ="members/new"로 넘어간다.

-앞서 작성한 @GetMapping members/newget방식으로 오면

-controller에 있는 createform 구문을 통해 createform html이 열린다.

-열려서 렌더링이 된다. 렌더링이 될 때 model addAttributemembeform을 넘겼다.

 때문에 화면에서 이 객체(new member())에 접근할 수 있게 된다.

-이 객체는 createMemberForm.html 파일 내에

<form ~~~~~~th:object="${memberForm}" 과 연결 되어 form내에서는

memberForm 객체를 사용하겠다는 타임리프의 문법적용이 된다.

 

c.th:field=*{name}

-*가 있는 경우는 object(memberform)를 참고하게 되고

MemberControllercreateform구문의 memberform을 참고하게 되고

memberformmemberform 클래스를 참고하여 해당 필드인 name을 참고하게 된다.

 

회원가입작성시 흐름

th:field=*{필드항목}

->th:object="${객체}

->MemberController클래스의 memberform 객체

->memberform 클래스의 해당 필드 private ~필드

 

d.필드항목 구문

-input type 텍스트

-input후 클래스와 placeholder로 넘어간다.

-필드항목 작성시 idname을 각각 항목내용을 똑같은 이름으로 처리 해준다.

-다 작성후 폼 데이터가 button submit을 누르면

members/newmethodpost로 넘어간다.

 

e. 회원가입 클릭 및 필드항목작성후 첫화면으로 돌아가도록 구문 작성

 

*@NotEmpty 어노테이션으로 인하여 회원이름을 빈칸으로 작성시 에러페이지가 나온다.

구문작성

설명

controller의 서버사이드에서 validation을 했는데 필수항목입니다 메시지가 뜬다.

MemberForm에 작성된 메시지임을 확인할 수 있다.

 

MemberFormController 흐름정리

-서버사이드에서 validation을 하고 문제가 생겨서 BindingResult의 데이터가 하나 들어왔다.

result.를 보면 에러에 대한 데이터를 찾아 낼 수 있는 메서드가 많다.

에러가 있으면 다시 createMemberForm으로 보낸다.

타임리프와 스프링부트가 integration이 되어 있기도 하고, 스프링이 바인딩 result를 기본

적으로 createMemberform에 긁어 와서 사용할 수 있도록 작동한다.

 

createformmember.html 흐름정리

th:class="${#fields.hasErrors('name')}? 'form-control fieldError' : 'form-control'">

<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Incorrect date</p>

 

-createMemberform.htmlfields.hasError에서 name에 에러가 있으면 css를 빨간 테두리(form-control fieldError)로 변경하게 해준다.

 

-fields.haserrors의 이름이 있으면 네임필드에 대해서 에러메세지를 출력해준다.

그래서 필수항목이라는 메시지가 렌더링이 된다.

 

사진

 

*회원이름을 작성안해서 에러가 있더라도 다른항목란에 값을 입력했다면 값을 그대로 가지고 온다.

*javax.validation 개념 구글링 통해 공부 필요

*thymleaf도 홈페이지를 통해 공부가능

 

 

웹 계층 개발

 

1.홈 화면

 

2.회원 기능

-회원 등록

-회원 조회

 

3.상품 기능

-상품 등록

-상품 수정

-상품 조회

 

4.주문 기능

-상품 주문

-주문 내역 조회

-주문 취소

 

5.기타

-상품 등록

-상품 목록

-상품 수정

-변경 감지와 병합

-상품 주문

 

홈 화면과 레이아웃

1. Controller 패키지 생성 및 HomeController 클래스 생성

 

2. resources 패키지 -> templates 패키지 -> home.html 파일 생성

-home.html 파일에 타임리프 템플릿을 등록해야 한다.

 

<div th:replace="fragments/bodyHeader :: bodyHeader" />

- jsp include처럼 렌더링 될 때에 fragmentsheader로 바꾼다.

*th라고 타임리프를 관례상 사용한다고 함


사진

설명

-로고가 찍혔음을 확인했고, 화면으로 넘어가서 오류가 났다는 것을 확인할 수 있다.

 

사진2

설명

-header, bodyHeader, footer이름으로 fragments 디렉터리 내에 html파일을 생성하지 않을 경우 에러발생한다.

 

3. fragment 각 대체파일 생성 및 화면출력확인

 

화면출력

*Include-style layouts

-현재는 Include-style layouts으로 작성되었다. 좀 더 단순하게 예제를 만들어 익히는데 중점을 두기 위해서이다.

-좀 더 편하고 실용적으로 작성하길 원한다면 Hierarchical-style layouts로 작성할 것

 

*dev-tools

글자 변경과 같이 수정 된 사항은 앞서 환경설정에서 build.gradle

설치된 dev-tools에 의해 build메뉴->Recompile 실행으로 인해

자동으로 수정변경완료가 되고 재실행 없이, 브라우저화면에서 F5를 누르면 된다.

 

4.view 리소스 등록

a. getbootstrap 사이트에 들어가서 파일을 다운로드 받는다.

b. 부트스트랩 파일을 static에 복사 및 붙여넣기를 한다.

 

c. resources 우클릭 후 Sync(Reload from DISK) 클릭

Build 메뉴 -> Build Project 클릭

*강제로 복사 및 붙여넣기를 했기 때문에 위와 같이 해주어야 한다.

*그래도 되지 않을 경우 부트스트랩 버전을 확인하고 버전에 맞는 integrity 값을 사용해야 한다.

부트스트랩 CDN 및 복붙

d. jumbotron-narrow.css staticcss폴더에 생성후 다시 재실행

 

요약정리

home controller에서 request mapping/로 들어가고

출력된 “home controller”에서 다시 뷰(home)으로 넘어간다.

include 스타일로 리소스들을 include 했고 header는 부트스트랩, 점보트론 css를 사용했다.

 

home.html에 있는 header,bodyHeader, footerfragments에 만들어놓은 html파일로 돌아간다.

부트스트랩(css)를 이용한 홈화면 및 레이아웃 완성!

 

JSP 게시판 프로젝트를 위한 환경설정

 

JSP란

-Java Server Page

<위키백과 참조>

자바 서버 페이지는 HTML내에 자바 코드를 삽입하여 웹 서버에서 동적으로 웹 페이지를 생성하여 웹 브라우저에 돌려주는 언어이다. Java EE 스펙 중 일부로 웹 애플리케이션 서버에서 동작한다.

 

1. JDK 설치

-다운로드 및 설치

 

-자바프로그램 작동을 위해서 환경변수 설정

-운영체제에 맞는 윈도우64bit jdk 다운로드(미리 다운로드)

-자바로 작성된 jsp를 작동할 수 있는 jsp용 웹서버인 tomcat 설치를 한다.

 

2. tomcat설치 및 구동

-tomcat 다운로드 및 압축풀기

 

-tomcat 수동으로 구동 

*설치된 tomcat안에 bin 폴더안에 restart 배치파일(startup)

cmd에서 해당경로에서 startup.bat 명령어를 엔터후 실행시킴으로

 

localhost로 웹서버 작동확인

사진

 

일일이 구동하기 힘드니 웹사이트개발환경

즉 전반적인 것을 관리하는 통합개발환경 이클립스를 설치한다.

 

3. 이클립스 설치(미리다운로드함)

-이클립스 다운로드

-Java EE로 설치

*자바 EE는 자바 SE 플랫폼을 기반으로 되어 있다. 

 자바 EE에서 대규모, 다계층, 확장성, 신뢰성, 보안 네트워킹 어플리케이션의 개발과 실행을 위한 API 그리고 환경을 제 

 공한다.

 

4. 프로젝트(Dynamic web project) 생성 및 tomcat 연동

-new->Dynamic web project 클릭

-New runtime에서 tomcat 버전 설정 및 tomcat설치된 경로 설정

 

 

5. web content

-다양한 웹페이지 저장/브라우저를 통해 페이지에 접속해서 내용을 볼 수 있다.

-new -> index.jsp 파일을 만든다.

 

-index.jsp파일을 구동한다. (finish클릭)

-hello world 출력

 

기본환경설정 완료

1. 회원 기능 테스트

 

테스트 내용

-회원가입기능이 작동해야 한다.

-회원가입할 때 같은 이름이 있으면 예외가 발생해야 한다.

 

테스트케이스

1.MemberService 클래스에서

a.하기 MemberService에서 ctrl + shift + t < create test < ok를 한다.

 

public MemberService {

......

}

 

b. public void 회원가입( ) throws Exception{

//given(이렇게 주어졌을 때)

 

//when(이렇게 하면)

 

//then(이렇게 된다)

}

 

public void 회원_중복_예외() throws Exception{

//given

 

//when

 

//then

}

 

 

테스트 범위

-정말 순수한 단위테스트 보다 jpa가 메모리모드로 실제 디비까지 돌아가는 것을 보기 위해

 완전히 스프링과 integration해서 테스트를 할 것이다.

 

그래서 하기 두가지를 세팅할 것이다.

 

@RunWith(SpringRunner.class)

@SpringBootTest

@Transactional 데이터를 변경해야 하기 때문에 하는 설정,

public class MemberServiceTest {

 

//테스트라 참조해서 테스트할 것이 없기 때문에 하기처럼 작성

@Autowired MemberService memberService;

@Autowired MemberRepository memberRepository;

}

////alt + enter를 누르고 suppress인가를 누르니 memberRepository가 보라색으로 변경됨

 

*@Transanctional

-데이터를 변경해야 하기 때문에 하는 설정

-같은 Transaction안에서 같은 엔티티에서 id(pk)이 같으면 같은 영속성 컨텍스트에서

 똑같은 값이 관리가 된다.

-Transactional이 있어야 롤백이 가능하다.

-insert SQLDB에 날라가지 않는다. 영속성 컨텍스트가 flush가 되지 않는다.

 

참고: 테스트 케이스 작성

Given, When, Then (http://martinfowler.com/bliki/GivenWhenThen.html) >

필수는 아니지만 위에 것을 기본으로 해서 다양하게 응용하는 것을 권장한다고 한다.

 

 

*축약어를 통해 코드구문생성하기

설정경로

- File->Setting->Editer->Live Templates -> tdd 단축단어 생성 -> tdd

 

내용설정

@Test

public void () throws Exception {

//given

 

//when

 

//then

}

 

=> tdd를 사용하면 위에 구문이 자동생성된다.

 

 

@Rollback(false)

-Transacntional을 롤백이 기본적으로 되므로 직접 눈으로 확인하고 싶고

 DB에 값이 들어가는 것을 확인하고 싶을 때에는

 @Rollback(false)public void 회원가입() throws Exception 에 적용하자.

 

-데이터베이스 트랜잭션이 커밋을 하는 순간 flush가 되면서

  jpa 영속성 컨텍스트가 있는 member객체가 insert문이 만들어지면서 DB insert가 나가게 된다.

 

사진

 

em.flush();

assertEquals(member, memberRepository.findOne(savedId));

-insert문이 db로 가는 것을 실행시킬 때 눈으로 볼 수 있고,

 transactionrollback 되도록 하는 설정

 

try catch return

-중복회원 및 회원가입을 다시 테스트

*데이터베이스에 앞서 insertDB로 날려 값(PRIMARY KEY)이 생성 되었다면

 실행시 중복회원에서 에러가 발생하니 조심할 것

 

사진

회원 기능 테스트 완료

 

 

 

 

2. 회원기능테스트 기술적 정리

a.RunWith(SpringRunner.class)

-jUnit 실행할 때 스프링이랑 엮어서 실행을 하겠다.

 

@SpringBootTest

-스프링부트위에서 테스트를 하려면 꼭 필요하다.

-@Autowired 실행이 실패한다.

-컨테이너 안에서 테스트를 한다.

 

b.@Transactional

-테스트가 끝나면 Rollback을 한다.

-서비스클래스나 레포지터리에 붙여서 돌리면 Rollback하지 않는다.

 

c. tdd 단축키 만들어서 테스트구문 생성하기

 

d. 테스트를 완전 격리된 환경에서 하는 방법

-실제 외부에 있는 데이터베이스를 사용했다.

-디비를 외부에 설치해서 테스트해야 할 경우가 생긴다. 테스트가 끝나고

 데이터가 초기화 되는 것이 좋다.

-테스트를 완전 격리된 환경에서 하는 방법이 있다.

-자바안에 데이터베이스를 만들어 테스트 하는 방법이 있는데,

 메모리 데이터베이스를 사용하는 방법이다. 안에 않고 가상의 데이터베이스를 만들어서

 스프링부트를 사용하면 이러한 방법을 무료로 이용할 수 있다.

 

 

종류

1. main

- 실제 개발하는 운영소스 (java, resouces)

-기본적으로 운영로직은 java, resources가 운영권을 가진다.

 

2. test

-테스트하는 운영소스 (java, resources:directory resources생성해주어야 함)

-테스트 운영로직의 우선권은 test안에 java, resources가 우선권을 가진다.

-application.yml을 java->resource로부터 가져온다면 application.yml이 우선권을 가져

 main에 있는 application.yml은 무시되고 test안에 있는 application.yml이 실행된다.

 

(application.yml 복사 test->resources(directory)->application.yml(복사 from main:java)

 

 

 

JVM내에서 실행 및 메모리모드로 데이터베이스실행 테스트

-jvm 안에서 실행되고 memory모드로 데이터베이스를 띄워주기 위해

 application.ymlspingurl을 인메모리 url로 바꾸어 준다.

 

주소는 h2.database->cheat sheet->In-Memory->첫번째 라인

 

사진 h2.database 사이트

 

사진

 

*하지만, spring, jpa 설정 내용이 전혀 없고 logging,level 설정 내용만으로도

 스프링부트의 기능으로 인해 인메모리 모드로 실행이 된다.

 극단적으로 얘기해서 내용이 전혀 없어도 인메모리 모드로 실행이 된다.

회원서비스(MemberService)

 

순서별로 설명

a.@Service

- @service 어노테이션에 ctrl + b로 확인을 해보면 @component가 설정이 되어 있다.

@component의 스캔의 대상이 되어서 자동으로 스프링빈으로 등록이 된다.

 

b.@Transactional(readOnly = true)

-class 전체적용 여기에선 읽기가 많으므로, readOnly으로 설정

-jpa의 모든 데이터변경이나 모든 로직들은 Transactional 안에서 실행 되어야 한다.

 Transactional 설정으로 lazy loading이 가능해진다.

-클래스레벨의@Transacntional 사용시 public method가 적용범주 안에 속한다.

 

*Transactional 종류2가지 javax,

-스프링에 dependency한 로직이많아서 스프링이 제공하는

Transactional 어노테이션사용권장한다. 사용할수있는옵션들이많기 때문에

 

c.@Autowired

//MemberRepository 클래스를 Autowired로 스프링이 스프링빈에 등록되어 있는 MemberRepository를 인젝션해준다.

____________________________________

 

현재구문에 @Autowired 단점

-일단 테스트를 위해 변경을 못하고 액세스할 수 있는 방법이 없다. 그래서 setter 인젝션을 사용한다.

 

단점보완책

1.예시(setter injection)

@Autowired

public void setMemberRepository(MemberRepository memberRepository) {

this.memberRepository = memberRepository;

}

 

-setter injection을 사용, 테스트할때에 memberRepository 목에 직접 입력(임의의 가짜 repository를 주입)해줄 수 있다.

-단점은, 실제 서비스실행시 누군가 바꿀 수있다.

-실제 동작하고 있는데 사실 바꿀일은 없다. 그래서 이보완책은 좋지 않다.

-스프링데이터 jpa에 의해서 다음과 같이 줄이고 보완할 수 있다.

 

2.최적의 보완책 예시(생성자 injection)

@RequiredArgsConstructor 와 private final MemberRepository 설정

-private final MemberRepository memberRepository 권장

-MemberService 에 값을 작성안해주면 위의 memberRepository에 빨간줄이 생긴다.

-, compile 시점에 한번 더 점검 할 수 있다.

*@RequiredArgsConstructor (lombok 참고)

-final에 있는 필드만 가지고 생성자를 만들어준다. 권장.

________________________________________

 

d.@Transactional

-@Transactional defaultTransactional=false 이다. class전체적용이 있어도

 현재 지금의 어노테이션이 우선권을 가져 이것만 적용이 된다.

-읽기전용아닌 곳에서는 @Transactional(readOnly=true)를 하면 안된다.

 

회원가입

validateDuplicateMember(member); 중복 회원 검증 함수이름=Exception

-MemberRepository클래스에서 jpa에서 em.persist를 하면 순간에 영속성컨텍스트에 Member를 올린다.

-@GeneratedValue를 생성하면 id가 항상생성된 것을 보장한다.

 영속성 컨텍스트에 값을 넣어야 하는데 db와 매핑한 키값이 id(pk)값이 된다.

-db에 들어가지 않아도 그래서 memberid도 채워준다는 얘기

 

f.Exception(validateDuplicateMember (Member member)

List<Member> findMembers = memberRepository.findByName(member.getName());

-받아온 이름을 찾아볼 때에 반환값 설정을 findByName에서 단축키ctrl+alt+v-member이름을

 유니크제약조건을 적용해주어야한다. (현재는 적용이 안되어 있음)

 MemberA라는 이름으로 동시에 INSERT를 하면 같은 이름으로 가입이 가능하기 때문에

 

if (!findMembers.isEmpty()) {

throw new IllegalStateException("이미 존재하는 회원입니다.");

}

 

-member이름을 찾아(!findMembers)보고 없으면(isEmpty)이면 throw new

이미존재하는 회원이라고 출력한다.

 

g.회원전체조회 작성

public List<Member> findMembers() and public Member findOne(Long memberId)

-@Transactional(readOnly=true)로 줄 경우 멤버를 찾을 때에 읽기전용으로만 사용하므로

 jpa가 사용되는 곳에서는 성능을 최적화 한다. 리소스를 적게사용한다.

-읽기전용아닌 곳에서는 @Transactional(readOnly=true)를 하면 안된다.

 변경이 안된다.

 

h. MemberRepository 클래스에서

-스프링부트 라이브러리, 스프링데이터 jpa의 지원으로 하기와 같이 가능해져

@Autowired

private EntityManager em;

public MemberRepository(EntityManager em) {

// Alt + Insert 키 이용해서 Generator사용

this.em = em;

}

 

위의 내용을 @RequiredArgsConstructorfinal 조합으로 사용할 수 있다.

*원래는 @Autowired@PersistentContext(이것도 잇어야 하지만, 생략가능하다.

스프링부트에서 있는 것으로 지원을 해주기때문(현재 버전에서 지원가능한지를 체크해야 함)

 

 

@Repository

@RequriedArgsConstructor

public class MemberRepository {

 

private final EntityManager em;

}

으로 줄일 수 있다.

 

사진

목차

회원 도메인 개발

-구현 기능

-회원 등록

-회원 목록 조회

 

순서

-회원 엔티티 코드 다시 보기

-회원 리포지토리 개발

-회원 서비스 개발

-회원 기능 테스트

 

회원도메인개발

1. 회원엔티티 코드 다시보기

 

2. MemberRepository

사진

a.레포지터리 패키지 생성

b.MemberRepository 클래스 생성

c. @Repository 어노테이션 세팅

*스프링에서제공하는 Repository 어노테이션을 설정해주면, 

component 스캔에의해서 스프링빈으로 자동으로 관리가된다.

 

*스프링빈

-자바 객체

-스프링 컨테이너(Spring Container)에 의해서 만들어진 자바 객체를스프링 빈이라고 부른다.

-스프링 빈과 자바 일반 객체와의 차이점은 없다굳이 다른점이라고 한다면, 컨테이너에 의해 만들어졌다.

 

*스프링 컨테이너가 나오게 된 배경

A a = new A( );

A a1 = new A( );

A a2 = new A( );

각각의 객체를 생성해주도록 코드를 작성하여 메모리를 3번 낭비했었는데,

메모리를 효율적으로 관리해주고자 메모리를 1번만 낭비를 하게 하기 위해 스프링 컨테이너가 탄생했다.

스프링 컨테이너에 스프링빈을 넣어 객체를 하나만 생성하여 공통 객체역할로 이 객체를 가지고 3개의 객체를 @Autowired를 활용해 생성한다.

 

예)

@Autowired

a a;

 

@Autowired

a1 a1; 

 

@Autowired

a2 a2; 

 

 

-@PersistenceContext 설정 및 private EntityManager em;

스프링이 엔티티 매니저를 만들어서 em에 인젝션 주입을 해준다.

 

d. 조회하는로직을 작성한다.(save, find)

save

-기본 엔티티매니저 사용은 persist 영속성 컨텍스트에 멤버엔티티객체를 넣고

후에 트랜잭션이 커밋 되는 시점에 디비에 인서트쿼리가 날라가면서 반영된다.

 

find메서드 사용

-단건조회

return em.find(Member.class : 첫번째 타입, id :primary key를 넣어준다.)

 

 

e. 회원전체 로직을 작성한다.(find all)

-createQuery다음에""를타입할것. 첫번째 jpql를 사용 뒤에는 반환타입

-하기코드를합친다.

( return result;에서 단축키 ctrl + alt + n < inline all < refactor )

List<Member> result = em.createQuery("select m from Member m", Member.class) .getResultList(); return result;

 

확인

*jpqlsql 차이점

-기능적으로 동일하고 결국 sql로 번역한다.

-sqlfrom의 대상이 테이블대상으로 쿼리를 한다.

-jpqlfrom의 대상이 엔티티객체를 대상으로 쿼리를 한다. (사진에 있는 Member를 조회한다.)

 

f. parameter binding에 의해서 특정 회원만 찾는 코드

-setParameterintPosition으로설정

아키텍처 설명

 

1.구현범위

회원기능

-회원가입, 회원목록

상품기능

-상품등록, 상품목록

주문기능

-상품주문, 주문내역

 

2.핵심 비즈니스 메서드를 선구현

 

예제 단순화를 위해 하기 기능은 구현x

-로그인과 권한 관리x

-파라미터 검증과 예외 처리 단순화

-상품은 도서만 사용

-카테고리는 사용x

-배송,정보는 사용x

 

3.애플리케이션 아키텍처

계층형 구조 사용 controller, web: 웹 계층

*controllerrepository로 접속할 수 있도록 구축할 예정

service: 비즈니스 로직, 트랜잭션 처리

repository: JPA를 직접 사용하는 계층, 엔티티 매니저 사용

domain: 엔티티가 모여 있는 계층, 모든 계층에서 사용

 

패키지 구조

-jpabook.jpashop

-domain

-exception

-repository

-service

-web

 

개발 순서: 서비스, 리포지토리 계층을 개발하고, 테스트 케이스를 작성해서 검증,

 

마지막에 웹 계층 적용

*웹을 제외한 핵심비즈니스 계층을 먼저 개발한다

주문 검색 기능 개발

-JPA에서 동적 쿼리를 해결방법

 

검색기능

1. 검색 리포지터리 생성

a. OrderSearch 생성 및 정보생성

 

*OrderRepository의 하기 부분을 완성시키기 위한 검색 리포지터리 생성

public List<Order> findAll(OrderSearch orderSearch) { }

 

-파라미터의 조건이 있으면 where문으로 검색이 가능하다.

 

b. OrderRepository에서 OrderSearch 구문 작성

설명

em.createQuery~

-jpql로 작성한다.

-테이블이 아닌 객체로 표현한다. 실제적으로 보면 일반적인 sql join문으로 실행된다.

 

*JPQL이란 <출처:위키백과>

-Java Persistence Query Language

 Java Persistence API 스펙의 일부로 정의 된 플랫폼 독립적인 객체 지향 쿼리 언어

-JPQL은 관계형 데이터베이스에 저장된 엔티티에 대해 조회하는 데 사용

 

값이 다 있다는 가정하에 구문 작성_1

사진 

 

동적쿼리로 변경_2

상태도 다가져오고 네임도 가져오고 createQuery에 있는 내용만 선택을 해야되라고 한다면 동적 쿼리가 된다.

ordersearch에 네임 파라미터 가 없으면 상태값도 선택 되어 있지 않으면 주문이든 주문취소든 다가져와 라고 하려한다면 다음과 같이 작성되어야 한다.

 

사진

 

 

위의 구문을 JPA에서 동적 쿼리로 바꾸어주려면 어떻게 해야 하는가?

JPA에서 동적 쿼리를 어떻게 해결해야 하는가?

 

방법 1 jpql 문자로 무식하게 해결을 한다.

 

 

설명

단점

jpql을 문자로 생성한다는 것은 번거롭고 실수나 버그가 충분히 발생한다.

 

*mybatis를 사용하는 이유가 동적쿼리를 생성하는데 굉장히 편하다는 이점이 있다.

 

JPA Criteria 로 작성 (비권장)_2 -> 실제 포트폴리오에선 이것으로 사용함.

-jpa가 제공하는 jpql를 자바로 작성할 수 있도록 표준인 것이 있다.

 기본편에도 간단하게 설명만 하고 지나갔다.

 

장점

-build하고 나면 결과적으로 jpql이 만들어진다.

-jpql이 자바코드로 작성할 수 있게 jpa criteria가 도와준다.

-동적 쿼리 작성할 때 메리트가 있다.

 

단점

-이것만 봤을 때는 어떤 jpql이 만들어질지 감이 안잡힌다.

 

 

방법 3 QUERYdsl로 작성 (실무에서 권장)

테스트 목표

-상품 주문이 성공해야 한다.

-상품을 주문할 때 재고 수량을 초과하면 안 된다.

-주문 취소가 성공해야 한다.

 

1. OrderServiceTest클래스를 만든다.

-shitft + ctrl + T를 이용하여 JUnit4createTest를 한다.

 

@Autowired

ENTITY매니저로 테스트데이터를 바로 넣기 위해 사용

em.persist(member);

 

 

2. 상품주문 테스트

사진

 

설명

-주석 확인할것

 

3. 상품주문재고수량초과

사진

 

설명

상품주문재고수량초과 상황설정

-주문일어 날 때에 재고수량 초과 하도록 설정

-fail 구문 작성

 

상품수량을 초과되게 작성하면 정상작동후 fail로 내려 온다.

-상품주문재고수량초과 구문이 제대로 작동됨을 확인할 수 있다.

 

상품 수량을 숫자에 맞게 작성하면 에러가 발생한다.

주문수량이 재고수량초과가 되지 않으면

orderService.order(member.getId(), item.getId(), orderCount); 이 시점에서 처리가 끝나므로 fail로 내려가지 않아

fail구문이 작동되지 않으므로 에러가 발생한다.

 

4. 주문취소

-(ctrl + shift + t를 누르면 실제 주문취소구문이 있는 클래스를 왔다갔다 할 수 있다.)

 

사진

 

설명

GIVEN

a.주문취소 상황설정

-Long orderId = orderService.order(member.getId(), item.getId(), orderCount());

:orderId를 생성한다. (주문한 상황까지 상황이 주어져야 한다.)

 

b.주문이 취소 되었을 때

 

c.취소된 주문 Id 확인, 주문 취소 Status, 아이템재고수량결과 구문

 

주문 서비스 개발

1.OrderService 클래스 생성

 

2.주문서비스에서 주문 작성

-주문서비스를 받을 때 이루어지는 정보들 작성

 

-엔티티조회

-배송정보 생성

-주문상품 생성

-주문생성

 

-주문저장

*주문저장에 대한 설명

Cascade

-이전에 order클래스에서 설정해준 Casacade.All 설정 때문에 강제로 Persist를 해준다.

-다른 곳에서도 delivery와 같은 정보를 다른 엔티티에서 참조하거나할때에 cascade를 사용해서는 안된다.

-한 사이클 내에서만 private owner개념으로 사용한다면 casacade를 사용해도 된다.

 

사진

 

정리

엔티티조회

-findOne으로 member,item 엔티티조회를 한다. 이것 때문에 의존관계를 넣었다.

 

배송정보생성

-static 생성메서드를 통해 주문상품 생성

-static 생성메서드를 통해 주문 생성

 

주문저장

-주문 저장시 cascade옵션으로 인해 delivery, orderitem과 자동으로 db persist가 된다.

정확히는, 트랜잭션이 커밋 되는 시점에 flush 가일어나면 insertdb에 들어가게 된다.

 

참고

생성로직을 다르게 작성하면(생성필드를 추가작성한다던가) 다른 스타일로 작성할 때에는 유지보수하기가 어려워진다.

그래서 다른 로직이나, 스타일로 작성하지 못하도록 OrderItem클래스에서 Constructor를 만들때에 protected를 만들어준다. jpaprotected까지 기본생성할 수 있도록 스펙상 허용한다.

 

Protected OrderItem() {

}

 

이럴 경우 OrderService클래스에서 다른 스타일로 쓸 경우 작성된 코드에 빨간밑줄이 생긴다.

 

참고2

생성메서드에서 주문생성에 대해 수정을 하도록 묵시적으로 요구할 때에 작성하는 코드

Order클래스에서 하기와 같이 작성

 

@NoArgsConstructor(access = AccessLevel.PROTECTED)

public class Order{

 

OrderService클래스에서 직접 생성할 경우에 new Order() 로 작성할 경우 빨간밑줄이 생긴다.

이와 같이 코드를 제약을 걸어주는 것이 좋은 설계와 유지보수로 끌어갈 수 있다.

 

3 .주문서비스취소 구문 작성

-취소구문작성

-취소구문에서 cancel에서 흐름은 다음으로 넘어간다.

 

1차 흐름-Order클래스

this.setStatus(OrderStatus.CANCEL);

for (OrderItem orderItem : orderItems) {

orderItem.cancel();

}

 

2차흐름-OrderItem클래스

public void cancel() {

getItem().upStock(count);

}

 

3.2.sqljpa일 때에 차이점

-jpa 장점을 설명

sql은 일일이 수작업을 해주어야 한다.

 

 

참고

jpa orm 사용시 자주 사용하는 패턴

-엔티티의 핵심 비즈니스 로직이 있고 서비스 계층은 단순히 호출(save로 위임하는 형태로 흘러간다.

 엔티티가 비즈니스로직을 가지고 객체지향의 특성을 활용하는 것을 도메인 모델 패턴이라고 한다.

 

sql 사용시 자주 사용하는 패턴

-엔티티에는 비즈니스 로직이 거의 없다거나 서비스 계층에서 대부분의 비즈니스 로직을 처리하는것을

 트랜잭션 스크립트 패턴이라고 한다.

 

무엇이 유지보수 하기 좋은지를 고민 하여 두 개의 패턴을 사용할 것!

 

사진

 

+ Recent posts