JPA, Stereotype, Hibernate

calis_ws·2023년 6월 14일
0
post-custom-banner

Spring Stereotype (Bean)

Stereotype Annotation은 프로젝트 내에서 클래스의 역할 또는 목적을 나타내는 데 사용되며 스프링은 자동으로 이 Stereotype의 클래스들을 탐지하고 Bean으로 등록한다.

Stereotype Annotation

JPA 프로젝트 생성

build gradle 추가

application.yaml 추가

spring:
  datasource:
    url: jdbc:sqlite:db.sqlite
    driver-class-name: org.sqlite.JDBC
  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true
    database-platform: org.hibernate.community.dialect.SQLiteDialect

JpaApplication 실행

yaml - Reload from Disk

db.sqlite 생성

StudentEntity.class

package com.example.jpa.entities;

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity     // 데이터베이스 테이블의 레코드를 나타냄
@Table(name = "students")
public class StudentEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // Column 어노테이션에 인자를 전달해서 테이블 Constraint 추가 가능
//    @Column(name = "username", nullable = false, unique = true)
    private String name;
    private Integer age;
//    @Column(nullable = false)
    private String phone;
    private String email;
}

JpaApp 실행

Table 생성

JPA Repository CRUD, Query Method

JPA

데이터가 어떻게 테이블에 매핑되는지 명세하기 위해 존재하고 인터페이스와 어노테이션으로 구성된다.

자바 ORM(Object Relational Mapping) 기술의 표준 명세로, 객체와 관계형 데이터베이스를 매핑하기 위한 인터페이스를 제공하고, JPA를 이용하면 SQL 코드를 작성하지 않고도 객체를 통해 데이터베이스를 조작할 수 있다.

Hibernate

JPA 명세를 바탕으로 작동하는 ORM 프레임워크로 JPA로 표현된 객체를 실제 데이터베이스에 적용 및 사용한다.

Hibernate를 사용하면 객체와 데이터베이스 테이블을 매핑하여 SQL 코드를 작성하는 과정을 줄일 수 있다.

AppService.class

package com.example.jpa;

import com.example.jpa.entities.StudentEntity;
import com.example.jpa.repos.StudentRepository;
import org.springframework.stereotype.Service;

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

@Service
public class AppService {
    private final StudentRepository studentRepository;

    public AppService(AppRepository repository, StudentRepository studentRepository) {
//        this.repository = repository;
        this.studentRepository = studentRepository;
    }

    // CREATE
    public void createStudent(
            String name,
            Integer age,
            String phone,
            String email
    ) {
        // 새로운(new) Student(Entity)를 만들고 싶다
        StudentEntity newEntity = new StudentEntity();
        newEntity.setName(name);
        newEntity.setAge(age);
        newEntity.setPhone(phone);
        newEntity.setEmail(email);
        // repository.save()
        this.studentRepository.save(newEntity);
    }

    // READ
    public void readStudent(Long id) {
        Optional<StudentEntity> optionalStudentEntity = this.studentRepository.findById(id);
        // 1. 실제 데이터가 온 경우
        if (optionalStudentEntity.isPresent()) {
            System.out.println(optionalStudentEntity.get());
        }
        // 2. 결과가 null 이 되었을 경우
        else {
            System.out.println("no result");
        }
    }

    // READ ALL
    public void readStudentAll() {
        System.out.println(this.studentRepository.findAll());
    }

    // UPDATE
    public void updateStudent(
            // 어떤 대상을 수정할 지 지정해야 한다.
            Long id,
            // 수정할 데이터
            String name
    ) {
        // 수정할 Entity 를 회수
        Optional<StudentEntity> optionalEntity = this.studentRepository.findById(id);
        // 수정할 Entity 를 찾은 경우
        if (optionalEntity.isPresent()) {
            // 실제 객체 회수
            StudentEntity target = optionalEntity.get();
            // 수정할 데이터 적용
            target.setName(name);
            // save
            this.studentRepository.save(target);
        } else {
            // 없으면 없다고 알려준다.
            System.out.println("could not find");
        }
    }

    // DELETE
    public void deleteStudent(Long id) {
        Optional<StudentEntity> optionalEntity = this.studentRepository.findById(id);
        // 삭제할 엔티티를 찾은 경우
        if (optionalEntity.isPresent()) {
            StudentEntity studentEntity = optionalEntity.get();
            // 삭제
            this.studentRepository.delete(studentEntity);
        } else {
            System.out.println("could not find");
        }
    }

    // findAllBy
    public void findAllByTest() {
        System.out.println("findAllByOrderByName");
        List<StudentEntity> studentEntities = this.studentRepository.findAllByOrderByName();
        for (int i = 0; i < 5; i++) {
            System.out.println(studentEntities.get(i));
        }
        System.out.println("...");

        System.out.println("findAllByOrderByAgeDesc");
        studentEntities = this.studentRepository.findAllByOrderByAgeDesc();
        for (int i = 0; i < 5; i++) {
            System.out.println(studentEntities.get(i));
        }
        System.out.println("...");

        System.out.println("findAllByAgeLessThan");
        studentEntities =
                this.studentRepository.findAllByAgeLessThan(21);
        for (int i = 0; i < 5; i++) {
            System.out.println(studentEntities.get(i));
        }
        System.out.println("...");

        System.out.println("findAllByPhoneStartingWith");
        studentEntities =
                this.studentRepository.findAllByPhoneStartingWith("010-");
        for (int i = 0; i < 5; i++) {
            System.out.println(studentEntities.get(i));
        }
        System.out.println("...");
    }

    // 주된 비즈니스 로직이 구현되는 공간
    // Controller -> Service
    // Service
    // 1. 데이터베이스 조회
    // 2. Component 사용
    // 3. 모은 데이터를 가지고 응답 (의사결정)

//    public List<Object> readStudentAll() {
//        List<Object> queryResult = repository.selectStudentAll();
//        // some business logic
//        return queryResult;
//    }
}

AppController.class

package com.example.jpa;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

//@Controller
@RestController
// 모든 메소드에 @ResponseBody 를 붙이는 용도
public class AppController {
    // 사용자의 입력을 직접적으로 받는 요소
    // MVC 에서 C를 담당
    // RequestMapping과 같이 사용

    private final AppService service;

    public AppController(AppService service) {
        this.service = service;
    }

    @GetMapping("create")
    public @ResponseBody String create() {
        this.service.createStudent(
                "alex",
                35,
                "010-1234-5678",
                "alex@gmail.com"
        );

        return "done";
    }

    @GetMapping("read")
    public @ResponseBody String readOne() {
        this.service.readStudent(1L);
        return "done-read-one";
    }

    @GetMapping("read-all")
    public @ResponseBody String readAll() {
        this.service.readStudentAll();
        return "done-read-all";
    }

    @GetMapping("update")
    public @ResponseBody String update() {
        this.service.updateStudent(1L, "alexander");
        return "done-update";
    }

    @GetMapping("delete")
    public @ResponseBody String delete() {
        this.service.deleteStudent(1L);
        return "done-delete";
    }

    @GetMapping("find")
    public @ResponseBody String find() {
        this.service.findAllByTest();
        return "done-find";
    }

//    @RequestMapping("students")
//    public void students() {
//        List<Object> result = service.readStudentAll();
//    }
//
//    @GetMapping("home")
//    public String home() {
//        return "home";
//    }
//
//    @GetMapping("body")
//    public @ResponseBody String body() {
//        return "body";
//    }
}

AppRepository.class

package com.example.jpa;

import org.springframework.stereotype.Repository;

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

@Repository
public class AppRepository {
    // 데이터베이스와의 소통을 담당
    // @Transactional
    public List<Object> selectStudentAll() {
        return new ArrayList<>();
    }
}

AppComponent.class

package com.example.jpa;

import com.google.gson.Gson;
import org.springframework.stereotype.Component;

@Component
public class AppComponent {
    // Controller, Service, Repository 가 아닌
    // 평범한 Bean 객체
    // 외부 API 사용, 공유된 기능 개발, 그냥 IoC 등록하고 싶은 객체

    // 필요로 하는 설정
    private final AppConfigData configData;
    private final Gson gson;
    public AppComponent(AppConfigData configData, Gson gson) {
        this.configData = configData;
        this.gson = gson;
    }

    // 외부 API를 사용하는 메소드
    public void useApi() {
        // send request
    }
}

AppConfigData.class

package com.example.jpa;

public class AppConfigData {
    private final String connectionUrl;
    public AppConfigData(String connectionUrl){
        this.connectionUrl = connectionUrl;
    }
}

AppConfiguration.class

package com.example.jpa;

import com.google.gson.Gson;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfiguration {
    // Spring을 활용하는데 필요한 다양한 설정을 담고 있는 용도

    @Bean
    public AppConfigData connectionUrl() {
        // 이 메소드의 결과(반환 값)를 Bean 객체로 등록
        if (true/* 현재 나의 상황에 따라서 다른 URl을 지급하는 코드 */)
            return new AppConfigData("main-url");
        else return new AppConfigData("backup-url");
    }

    @Bean
    public Gson gson() {
        return new Gson();
    }
}

StudentRepository.interface

package com.example.jpa.repos;

import com.example.jpa.entities.StudentEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

// 데이터베이스와 소통하기 위한 창구
public interface StudentRepository
        // JpaRepository<내가 사용할 Entity, 그 Entity의 ID 컬럼의 타입>
        extends JpaRepository<StudentEntity, Long> {

    // Spring Data JPA - Query Method
    // 메소드 이름을 우리가 조회하고 싶은 조건을 붙여서 만든다.
    // 하나 또는 많이
    // (findBy || findAllBy) + [(Column + 조건) * n] + [OrderBy + Column]
    // SELECT * FROM students ORDER BY name;
    List<StudentEntity> findAllByOrderByName();

    // SELECT * FROM students ORDER BY age;
    List<StudentEntity> findAllByOrderByAgeDesc();

    // SELECT * FROM students WHERE age < 30;
    List<StudentEntity> findAllByAgeLessThan(Integer age);

    // SELECT * FROM students WHERE phone LIKE '010-%';
    List<StudentEntity> findAllByPhoneStartingWith(String phone);
}

application.yaml 수정

spring:
  datasource:
    url: jdbc:sqlite:db.sqlite
    driver-class-name: org.sqlite.JDBC
  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true
    database-platform: org.hibernate.community.dialect.SQLiteDialect
    defer-datasource-initialization: true
  sql:
    init:
      mode: always

resources - data.sql 추가

인사이트 타임

나머지 구하기

https://school.programmers.co.kr/learn/courses/30/lessons/120810

class Solution {
    public int solution(int num1, int num2) {
        return num1 % num2;
    }
}

very easy

Q. num2 = 0 이라면?

그렇다고 합니다. 꿀팁!

공 던지기

https://school.programmers.co.kr/learn/courses/30/lessons/120843
인덱스를 골라내는 법을 찾지 못하여 실패
힌트는 나머지 연산
다음에 재도전!

review

이번 주는 전부 힘들다 정말로. 이제 진짜 생존게임이 시작된 것 같다. 아침부터 오후까지 내내 코드만 줄줄이 치느라 이 수많은 로직들이 머리속에 입력되지 못하고 과부화가 와버렸다. 지금도 수업 마치고 조금 자고 일어나서 포스트를 작성하고 있는데 복습하는 겸 위키를 참고해서 읽어봐도 이 많은 걸 당장에 이해하기도 외우지도 못 할 것 같다. 내가 과연 이걸 이해해서 이용할 수 있을까. 다시 찾아온 걱정과 불안감. 역시 일단 내일만 살자.

profile
반갑습니다람지
post-custom-banner

0개의 댓글