728x90
반응형
SMALL

jpa

각각의 테이블 클래스 생성

📌 자바 클래스를 JPA를 사용해 Meber라는 테이블 만들기

  1. Member 클래스 생성
  2. @Entity 생성
  3. @Getter 생성 //값을 가져올 때 쓰임
  4. @NoArgsConstructor 생성 // 기본생성자 만들어줌
  5. @Id 생성
  6. @GeneratedValue (strategy = GenerationType.IDENTITY) 생성
  7. private Long id;
  8. @Column(nullable = false)
  9. private String memberName;

해주면 한 열의 데이터 테이블을 완성! 기런식으로 쭉 만들면 데이터 테이블을 만들 수 있다!

package com.example.springjpa.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter // 값을 가져올 때 쓰임 
@NoArgsConstructor  // 기본생성자 만들어줌
@Entity
public class Member {   //자바 클래스를 JPA를 사용해 Member라는 테이블을 만들기
    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false)
    private  String memberName;

}
각 테이블을 연결해주는 JpaRepository를 각 클래스와 설정값 (현재는 Long과 연결!)
을 상속해주는 interface 만들기
public interface MemberRepository extends JpaRepository<Member, Long> {
}

특정 이름 찾을 때 만드는 방법

기존 MemberRepository에 추가

Optional<Member> findByMemberName(String memberName);

본체인 클래스에 추가!

Member member = memberRepository.findByMemberName("삼식이").orElseThrow(
                () -> new RuntimeException("삼식이 없음")
        );
        System.out.println("member.getMemberName() = " + member.getMemberName());

최종 완성!!

 

package com.example.springjpa.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@NoArgsConstructor      // 기본생성자를 만들어줌
@Getter     // 값을 저장할 때 쓰임
@Entity
public class Member {   //자바 클래스를 JPA를 사용해 Member라는 테이블을 만들기
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long member;
    @Column(nullable = false)
    private String memberName;

    @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)  // 연관관계의 주인이 누구인지 중점적으로 생각하면 쉽게 만들 수 있다!
    public List<Orders> orders = new ArrayList<>();
    private String id;

    public Member(String memberName) {  // 단축키 alt + insert
        this.memberName = memberName;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}
package com.example.springjpa.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@NoArgsConstructor      // 기본 생성자를 저장
@Getter     // 값을 저장할 때 쓰임
@Entity
public class Food {     //자바 클래스를 JPA를 사용해 Food라는 테이블을 만들기
    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    private Long id;
    // 음식의 이름 테이블 생성
    @Column (nullable = false)
    private String foodName;
    // 음식의 가격 테이블 생성
    @Column (nullable = false)
    private int price;

    @OneToMany(mappedBy = "food", fetch = FetchType.EAGER)      //OneToMany Food하나에 Order가 여러개가 될수 있기 때문에 사용함    //mappedBy 연관관계의 주인을 지정해 주는것
    private List<Orders> orders = new ArrayList<>();

   // 현재 Food 테이블과 orders테이블을 JoinColumn을 사용해서 Join이 되어 있는데 food_id의 주인이 Food라고 알려주는 것!

    public Food(String foodName, int price) {   // 단축키 alt + insert!
        this.foodName = foodName;
        this.price = price;
    }
}
package com.example.springjpa.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor      // 기본 생성자 만들어줌
@Getter                 // 값을 저장할때
@Entity
public class Orders {    //자바 클래스를 JPA를 사용해 Order라는 테이블을 만들기
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; // 넣으면 클래스 Order의 빨간 줄 사라짐!
    // Food의 Id와 Member의 Id를 받아와야하기 때문에 ManyToOne 사용!
    // JoinColum(name = "Food_id") 사용해서 Food의 Id로 Join 하기
    @ManyToOne
    @JoinColumn(name = "food_id")
    private Food food;               // Food 가져오기

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;

    // 현재 Food 테이블과 orders테이블을 JoinColumn을 사용해서 Join이 되어 있는데 food_id의 주인이 Food라고 알려주는 것!
    // 단축키 alt + insert!
    public Orders(Food food, Member member) {
        this.food = food;
        this.member = member;
    }
}
package com.example.springjpa;

import com.example.springjpa.entity.Food;
import com.example.springjpa.entity.Member;
import com.example.springjpa.entity.Orders;
import com.example.springjpa.repository.FoodRepository;
import com.example.springjpa.repository.MemberRepository;
import com.example.springjpa.repository.OrdersRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Component
@RequiredArgsConstructor
public class Restaurant implements ApplicationRunner {

    private final FoodRepository foodRepository;
    private final OrdersRepository ordersRepository;
    private final MemberRepository memberRepository;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        List<Food> foods = new ArrayList<>();
        Food food1 = new Food("후라이드", 10000);
        foods.add(food1);
        Food food2 = new Food("양념치킨", 12000);
        foods.add(food2);
        Food food3 = new Food("반반치킨", 13000);
        foods.add(food3);
        Food food4 = new Food("고구마피자", 9000);
        foods.add(food4);
        Food food5 = new Food("아보카도피자", 110000);
        foods.add(food5);
        foodRepository.saveAll(foods);

        List<Member> members = new ArrayList<>();
        Member member1 = new Member("삼식이");
        members.add(member1);
        Member member2 = new Member("먹깨비");
        members.add(member2);
        memberRepository.saveAll(members);

        System.out.println("==================================================================");

        System.out.println("Member 데이터");
        List<Member> findMembers = memberRepository.findAll();
        for (Member findMember : findMembers) {
            System.out.println("findMember = " + findMember.getMemberName());
        }

        System.out.println("==================================================================");

        System.out.println("Food 데이터");
        List<Food> findFoods = foodRepository.findAll();
        for (Food findFood : findFoods) {
            System.out.println("findFood = " + findFood.getFoodName());
        }

        List<Orders> ordersList = new ArrayList<>();
        Orders orders1 = new Orders(findFoods.get(0), findMembers.get(0));
        ordersList.add(orders1);
        Orders orders2 = new Orders(findFoods.get(3), findMembers.get(1));
        ordersList.add(orders2);
        Orders orders3 = new Orders(findFoods.get(4), findMembers.get(1));
        ordersList.add(orders3);
        Orders orders4 = new Orders(findFoods.get(2), findMembers.get(0));
        ordersList.add(orders4);
        Orders orders5 = new Orders(findFoods.get(2), findMembers.get(0));
        ordersList.add(orders5);
        Orders orders6 = new Orders(findFoods.get(1), findMembers.get(1));
        ordersList.add(orders6);
        Orders orders7 = new Orders(findFoods.get(1), findMembers.get(0));
        ordersList.add(orders7);
        Orders orders8 = new Orders(findFoods.get(3), findMembers.get(1));
        ordersList.add(orders8);
        ordersRepository.saveAll(ordersList);

        System.out.println("==================================================================");
        int num = 1;

        System.out.println("Orders 데이터");
        List<Orders> orderList = ordersRepository.findAll();

        for (Orders orders : orderList) {
            System.out.println(num);
            System.out.println("주문한 사람 = " + orders.getMember().getMemberName());
            System.out.println("주문한 음식 = " + orders.getFood().getFoodName());
            num++;
        }

        System.out.println("==================================================================");
        System.out.println("삼식이 주문한 음식");
        Member samsik = memberRepository.findById(1L).orElseThrow(
                ()->new RuntimeException("없음")
        );

        num = 1;
        for (Orders orders : samsik.getOrders()) {
            System.out.println(num);
            System.out.println("주문한 음식 = " + orders.getFood().getFoodName());
            System.out.println("주문한 음식 가격 = " + orders.getFood().getPrice());
            num++;
        }

        System.out.println("==================================================================");
        System.out.println("아보카도피자 주문한 사람");
        Food abocado = foodRepository.findById(5L).orElseThrow(
                ()->new RuntimeException("없음")
        );

        for (Orders order : abocado.getOrders()) {
            System.out.println("주문한 사람 = " + order.getMember().getMemberName());
        }

        // 특정 이름 찾을 때 만드는 방법
        Member member = memberRepository.findByMemberName("삼식이").orElseThrow(
                () -> new RuntimeException("삼식이 없음")
        );
        System.out.println("member.getMemberName() = " + member.getMemberName());
        System.out.println("member.getId() = " + member.getId());

    }
}
package com.example.springjpa.repository;

import com.example.springjpa.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {
    Optional<Member> findByMemberName(String memberName);

}
package com.example.springjpa.repository;

import com.example.springjpa.entity.Food;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FoodRepository extends JpaRepository<Food, Long> {

}
package com.example.springjpa.repository;

import com.example.springjpa.entity.Orders;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrdersRepository extends JpaRepository<Orders, Long> {
}
728x90
반응형
LIST

'개발 > Spring' 카테고리의 다른 글

spring 서버 연결 메모만들기  (0) 2023.02.04
mvc  (0) 2023.02.04
SQL 설정 및 연습!  (0) 2023.02.03
SpringBoot 및 서버 이해  (0) 2023.02.03
웹 동작방식 이해하기  (1) 2023.02.03
728x90
반응형
SMALL

인텔리제이 프로에는 왼쪽에 spring이 들어있습니다!

클릭 후 자바 17버전으로 클릭해주면~ 로딩로딩로딩~

그후 데이터 베이스 클릭 후

“+” 클릭!

In-memory로 변경 후 URL에 오류가 안나는 주소 입력!

jdbc:h2:mem:test;MODE=MYSQL;

jdbc:h2:mem:test;MODE=MYSQL;OLD_INFORMATION_SCHEMA=TRUE;

화면에서 우클릭 후

Change Dialect 이 있을 경우 SQL 클릭후 입력!

Change Dialect 이 없을 경우 H2로 입력

차이는 밑에 COMMET가 없는정도

MySQL
CREATE TABLE IF NOT EXISTS MAJOR
(	
	major_code varchar(100) primary key comment '주특기코드', 
	major_name varchar(100) not null comment '주특기명',
	tutor_name varchar(100) not null comment '튜터'
)
COMMENT '주특기' charset=utf8;
H2
CREATE TABLE IF NOT EXISTS MAJOR
(	
	major_code varchar(100) primary key comment '주특기코드', 
	major_name varchar(100) not null comment '주특기명',
	tutor_name varchar(100) not null comment '튜터'
)

설정에서 dialectd을 찾아 SQL로 설정을 해보았는데, 빨간 줄이 뜨고 에러가 발생

설정에서 dialectd을 찾아 SQL를 H2로 설정을 해보았는데, 빨간 줄이 뜨긴하는데 입력은 가능!

해결 방안은 H2로 입력해주면 될거 같다.

join을 사용해서 연결하기

*SELECT s*.name, *s*.major_code, *m*.major_name *FROM* STUDENT *s JOIN* MAJOR *m ON s*.major_code = *m*.major_code;

joun을 사용하지 않고 연결하기

*SELECT s*.name, *s*.major_code, *m*.major_name *FROM* STUDENT *s*, MAJOR *m WHERE s*.major_code = *m*.major_code;

728x90
반응형
LIST

'개발 > Spring' 카테고리의 다른 글

spring 서버 연결 메모만들기  (0) 2023.02.04
mvc  (0) 2023.02.04
JPA 연습!  (0) 2023.02.04
SpringBoot 및 서버 이해  (0) 2023.02.03
웹 동작방식 이해하기  (1) 2023.02.03
728x90
반응형
SMALL

 

 

Intro

 🌐 우리는 지난 시간에, 소위 웹 어플리케이션이 어떠한 원리로 어떻게 동작하는지, 그 중에 웹서버(정확히는 Web Application Server)는 어떻게 동작하는지 아주 간략하게 알아봤습니다. 이번 시간에는 서버가 해주는 일을 조금 더 자세히 살펴보고, 스프링, 스프링부트에 대한 소개를 갖는 시간을 가져보면 좋을 것 같습니다.

 

 🤔 Issue : 서비스의 규모가 커지면 우리가 flask 서버에서 작성하던 것 과 같이 프로그래밍을 할 수 없습니다.

 

서비스 로직이 커져갈수록 연관된 데이터는 훨씬 더 많아지고, 처리해야 할 로직은 더 복잡해지고, 심지어는 이것들이 맞물려 프로그램의 복잡도는 기하급수적으로 올라갑니다. 특히 프로그램의 로직이 수정되어야 하거나 이미 동작하는 서버위에 새로운 기능을 추가하는건 사실상 불가능해지는 시점에 도달하게되고, 결국 실패를 인정하고 아예 서버나 프로그램을 새로 만드는 일도 심심치 않게 일어납니다.

그나마 다행인 점은, 우리의 선배 개발자들이 이와 같은 실패로 얻은 교훈이 헛되지 않게 많은 자료와 사례를 남겨두었고, 지금은 “~~패턴” 또는 “~~아키텍쳐로’ 불리우고 있습니다. 우리는 이러한 것들을 공부하는 것 만으로도 그들이 했었던 실수를 반복하지 않고 개인의 수천시간, 동료들의 수만시간을 아낄 수 있습니다.

소프트웨어 디자인 패턴(software design pattern)은 소프트웨어 공학의 소프트웨어 디자인에서 특정 문맥에서 공통적으로 발생하는 문제에 대해 재사용 가능한 해결책이다. 소스나 기계 코드로 바로 전환될수 있는 완성된 디자인은 아니며, 다른 상황에 맞게 사용될 수 있는 문제들을 해결하는데에 쓰이는 서술이나 템플릿이다. 디자인 패턴은 프로그래머가 어플리케이션이나 시스템을 디자인할 때 공통된 문제들을 해결하는데에 쓰이는 형식화 된 가장 좋은 관행이다.

Software Architecture Patterns

소프트웨어 아키텍처 패턴

 🤔 디자인 패턴을 공부하는 것 역시 매우 중요한 주제이지만, 자칫 너무 맹신하려고 들면 또 오히려 매우 큰 역효과를 마주한다고 합니다. 지금 당장은 더 중요한 배울거리가 많기도 하고 특정 패턴등을 마주 할 때 마다 확실히 이해하고 인지하고 넘어가는 정도의 공부가 지금은 가장 좋은 것 같습니다.

 

복잡한 문제를 해결하는 방법- 문제를 나누자!

실제로 복잡한 문제를 아키텍처 적으로 해결하는 방법은 매우 다양하지만 주로 문제를 나누는 방식이 가장 애용됩니다. 지난 시간의 이야기들과 웹개발 플러스 강의를 통해 여러분들이 구현해둔 예시로 알게된 내용은 아래 그림과 같습니다. 우리의 서버는 “새로운 데이터를 처리하는 부분”, “서비스 로직을 처리하는 부분”, **“기존의 데이터를 이용하는 부분”**으로 되어 있습니다. 실제로 각 부분은 스프링과 스프링부트에서 각각의 레이어로 나뉘어져 있습니다.

레이어드 아키텍처 패턴

출처 : https://jojoldu.tistory.com/603


Presentation 계층

사용자와 상호 작용 처리 계층

CLI, HTTP 요청, HTML 처리 등을 담당한다.

HTTP 요청 처리 및 HTML 렌더링에 대해 알고 있는 웹 계층

흔히 말하는 MVC (Model / View / Controller) 도 이 계층에 속한다.

우리가 URL을 매핑해서 특정 메서드가 해당 URL로 요청이 올 때마다 호출되게 프로그래밍 했었죠? 그 계층을 말하는 것이며, 스프링에서는 @Controller 어노테이션을 사용하여 표현합니다!


Domain(Business or Service) 계층

서비스/시스템의 핵심 로직

유효성 검사 및 계산을 포함하는 Business 논리 계층

애플리케이션이 수행해야하는 도메인과 관련된 작업들을 담당한다.

입력/저장된 데이터를 기반으로 계산

Presentation 계층에서 받은 데이터의 유효성 (Validation) 검사

어떤 Data Access 를 선택할지 결정

우리의 서버 프로그램이 복잡해지면, 비즈니스 로직을 수행하기 위한 별도의 계층(Layer)이 필요합니다. 사실 더 이상적으로는 유능한 서버 프레임워크를 써서 Presentaion, Data Access계층에는 별로 할 일이 없고, 도메인 계층이 비대해지는게 가장 좋습니다. 스프링에서는 @Service 어노테이션을 사용해서 표현합니다!


Data Access(Persistence) 계층

DAO 계층

Database / Message Queue / 외부 API와의 통신 등 처리

데이터베이스 또는 원격 서비스에서 영구 데이터를 관리하는 방법을 분류하는 데이터 접근 계층

우리의 데이터베이스, 혹은 데이터를 저장하는 데이터 소스는 서버 외부에 별개로 존재하는 경우가 매우 많고, 그러한 데이터 소스와의 소통을 해주는 계층이라고 생각하시면 될 것 같습니다. 스프링에서는 @Repository 어노테이션을 사용해서 표현합니다.

 🙂 아래 그림은 서버를 레스토랑으로 비유한 예시이며, 이 그림을 통해 각각의 계층과 스프링에서의 해당 어노테이션이 어떠한 일을 하는지 감을 잡으면 좋을 것 같습니다.

 

Controller, Service, Respository 실제 코드들

 🙂 각각의 레이어와 실제 코드들은 하나하나 자세히 살펴볼 예정입니다! 지금은 이런게 있다는 생각으로 가볍게 봐주시고 실제 실습에서 위와 같은 코드들을 직접 쳐보면서 학습 할 예정입니다.

 

Controller 예시 코드

@Controller // #1
public class ContentController {

    private final ContentService contentService; // #2

		@GetMapping("/content/{contentId}") // #3
		public Content getContent(@PathVariable Long contentId) { // #4
        Content content = contentService.getContent(requestDto); //#2-1
        return "/contentPage";
    }

    @PostMapping("/content") //#5
		@ResponseBody// #6
    public Content createContent(@RequestBody ContentRequestDto requestDto) {
        Content content = contentService.createContent(requestDto);
        return content;
    }
}

#1 : 이 자바 객체가 컨트롤러 역할을 하는 객체라는 것을 알려주는 어노테이션 입니다!

#2 : 각각의 레이어는 “일반적으로” 자기와 인접한 레이어와 직접 소통합니다, 이 경우 ContentService객체를 가지고 있어, 컨트롤러 단에서 서비스 단으로 새로 받아온 데이터를 전달하거나 서비스 로직을 호출 할 수 있습니다. 예를들어 #2-1 처럼 말이죠!

#3 : 플라스크의 @app.route(”/”)와 비슷해서 이미 짐작하셨겠지만, 특정 요청에 호출될 메서드를 지정해주는 어노테이션 입니다!

#4 : 해당 메서드에 넘기는 인자값을 손쉽게 넘기도록 위와 같은 어노테이션을 사용 할 수 있습니다, 이러한 어노테이션이 있으면 자동으로 일치하는 변수값을 메서드 호출되는 시점에 같이 넘겨줍니다. (조금 뒤에 이해하셔도 좋습니다!)

#5 : #3과 #5가 다른 이유는 HttpMethod에 따라서 다른 Controller 메서드를 연결해줄 수 있기 때문입니다, 같은 주소로 온 GET 요청과 POST을 나눠서 각각 처리하기에 용이하겠죠?

#6 : 위의 메서드와 아래의 메서드는 이전에 배웠던, 뷰까지 같이 반환하느냐, 혹은 JSON 형식으로 데이터만 반환하느냐의 차이가 있습니다. 이 부분도 나중에 자세히 학습하겠습니다.

Service 예시 코드

@Service // #1
public class ContentService {
    private final ContentRepository contentRepository; //#2

    public ReturnDto getContent(Long id) {
        ReturnDto returnDto = contentRepository.findById(id);
        return returnDto; //#3
    }
	
    public Content createContent(ContentRequestDto contentRequestDto) {
		    Content content = new Content(contentRequestDto);
		    contentRepository.save(content);
		    return content;
		}

}

#1 : 마찬가지로 이 자바 객체가 서비스 역할을 하는 객체라는 것을 알려주는 어노테이션 입니다!

#2 : 인접한 계층인 Repository 객체를 가지고 있어야 겠죠?

#3 : 사실 이 부분 역시 인접한 계층으로 데이터를 전달하는 중 입니다. 이해가 안가신다면 이 페이지의 내용을 다시 읽어보시면 좋을 것 같네요!

Repository 예시 코드

@Repository
public interface ContentRepository extends JpaRepository<Content, Long> {

}

#1 : 마찬가지로 이 자바 객체가 서비스 역할을 하는 객체라는 것을 알려주는 어노테이션 입니다!

#2 : 사실 리포지토리는 상당히 다양한 기술과 얽혀 있고 다양한 케이스가 있어 예시 코드를 보여드리기는 조금 어려운 것 같기도 합니다. 그래도 우리가 직접 사용하게 될 SpringDataJpa 의 JpaRepository는 이정도로 생겼네요

Outro

Outro 1. IOC? DI?

private final ContentService contentService; // #2
private final ContentRepository contentRepository; //#2

자바를 정말 열심히 공부하시고, 스프링만 처음이라면 위와 같은 코드의 위화감을 느끼셨을 겁니다.

분명 해당 객체의 메서드도 호출하고 있는데, 해당 객체는 어디서 어떻게 들어와있을까요?

이외에도 앞으로 지엽적인 컨트롤러나 리포지토리 서비스쪽 코드를 보게되다 보면

자바 문법과 묘하게 다른 부분들이 보일 겁니다.

때로 어떠한 지식은 뒤의 내용을 알아야 이해가 더 쉬운 경우가 있어 지금 설명하지는 않겠습니다.

위와같이 기존까지 알고 계시던 자바 문법과 다른 부분은 꼭 메모를 하시고 의아해 하시는 습관을 가지시면,

이후 IOC, DI 등 설계 철학 등을 배우고 이해하는데 큰 자산이 될 것 같습니다.

Outro 2. 스프링/스프링부트를 사용하는 이유?

이 부분도 지금은 와닿기 어려운 이유일 수 있습니다.

다만 지겹고 공부해야 할 많은 부분들이 전부 공부해야 할 이유가 있습니다.

결론적으로는 예전에 불편했던 것들을 정말 획기적으로 편하게 해줬기 때문에 스프링 프레임워크를 사용하는데,

구체적인 스프링에 관련된 내용을 학습하다 보면 왜 스프링이 편한지를 소개시켜드리는 계기가 있을 것 같습니다.

 🤔 그래도 일반적으로 가장 많이 꼽는 이유로는, 단순 반복작업부분이 많았던 Controller와 Repository쪽을 개발 관점에서 매우 쉽고 편하게 처리해줘 가장 중요한 핵심 비즈니스 로직인 Service 레이어에 더 집중 할 수 있도록 하게 해준다는 이유를 꼽기도 합니다.

 

 

 

728x90
반응형
LIST

'개발 > Spring' 카테고리의 다른 글

spring 서버 연결 메모만들기  (0) 2023.02.04
mvc  (0) 2023.02.04
JPA 연습!  (0) 2023.02.04
SQL 설정 및 연습!  (0) 2023.02.03
웹 동작방식 이해하기  (1) 2023.02.03

+ Recent posts