[22-06-(30~04)] Spring JDBC DB Access

이상수·2022년 7월 23일
0

TIL_Spring MVC

목록 보기
6/11
post-thumbnail
  1. 시작하게 된 계기 및 다짐 😮
  • 이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.

    • 그 날 배웠던 것을 길지 않아도 좋으니 정리하며 복습하는 습관 기르기
    • 주말에 다음주에 배울 내용들을 예습
    • 코딩 문제와 java코드들은 꾸준히 학습
    • 자료구조를 이용한 알고리즘 문제 해결 학습
  1. 학습 목표 😮
목표결과
JDBC가 무엇인지 이해O
Java에서 JDBC가 어떤 역할을 하는지 이해할 수 있다.O
Spring Data JDBC가 무엇인지 이해O
Spring Data JDBC를 이용해서 데이터의 저장, 수정, 조회, 삭제 작업O
Spring Data JDBC 기반의 엔티티 연관 관계를 매핑O
  1. 정리 😮

JDBC란?


1. JDBC란

  • JDBC(Java Database Connectivity)는 Java레벨에서 데이터를 DB에 저장 / 업데이트하거나 반대로 DB의 저장된 데이터를 java레벨에서 사용할 수 있도록 해주는 API이다.
  • Mysql, Oracle, MS SQL등의 벤더를 이용해서 연동할 수 있음

2. JDBC동작 흐름

  • [사진]
  • JDBC API가 JDBC드라이버를 로딩 한 후 DB와 연결한다.
  • JDBC Driver : DB와 통신을 담당하는 인터페이스로, 이 JDBC구현체를 이용하여 특정 벤더의 DB에 엑세스한다.
    (Service Provider Interface)

3. JDBC API 사용 흐름

  • [사진]
    1). 드라이버 로딩 : Driver Manager클래스를 통해 로딩
    2). Connection 객체 생성 : Driver Manager를 통해 DB와 연동되는 Connection 객체 생성
    3). Statement 객체 생성 : 작성된 SQL쿼리문을 실행하기 위한 객체 생성 후 정적으로 SQL 쿼리 문자열을 입력으로 가짐
    4). Query 실행 : Statement객체의 SQL 쿼리문 실행
    5). ResultSet 객체로부터 데이터 조회 : SQL쿼리문의 결과 데이터셋
    6). ResultSet - Statement - Connection 객체 순서대로 Close

4. Connection Pool

  • JDBC API를 사용하기 위해 DB연결에 필요한 Connection 객체를 생성해 놓는 비중이 많이 드는 작업
  • Application로딩 시점에 Connection객체를 생성하는 것이 아닌,
    미리 만들어둔 Connection객체를 사용함으로써 애플리케이션의 성능을 향상
  • ★[이처럼, Connection 객체를 미리 만들어서 보관하고 필요시 제공해주는 관리자를 Connection Pool이라 한다]

Extra

  1. JDBC에 대한 추가학습

  2. 인메모리(DB)

  3. H2 콘솔 사용법

  4. ★application.properties / application.yml 추가 설정 정보들




Spring Data JDBC란


**0. 앞으로 학습 해야할 기술들
0. Spring Data JDBC : 기본적인 ORM중심 기술로, Spring에서 Data에 접근하는 일관된 방식 제공, JPA와 마찬가지로 ORM기술을 사용하지만 기술적 복잡도를 좀 낮춰놓은 기술
1. Java Data JDBC : 비교적 쉽고 간단, 소규모 및 복잡하지 않은 프로젝트에 좋음
2. JPA : 실무에서 가장 많이 사용하고 있는 기술
3. Java Data JPA : Spring에서 JPA를 편리하게 사용할 수 있게 해주는 기술로, JPA 선행 지식이 있어야한다.


1. 데이터 엑세스 기술 유형

  • Spring JDBC, Spring Data JDBC, JPA, Spring Data JPA등이 있음
  1. SQL 중심 기술

    • mybatis, Spring JDBC가 대표적인 SQL 중심 기술
    • DB에 접근하기 위한 SQL 쿼리문을 애플리케이션 내부에 직접적으로 작성하는 것이 중심
    • Java 진영에서 SQL중심의 기술에서 객체(Object)중심의 기술로 지속적으로 이전을 하고 있는 추세
    • 객체(Object) 중심이 SQL중심보다 비중이 높아지고 있다.
  2. 객체(Object) 중심 기술

    • ★ORM(Object-Relational Mapping)이란 이런 객체 중심 데이터 엑세스 기술이다.★
    • ★ORM의 대표적인 기술이 JPA(Java Persistence API)이다. ★
    • 모든 데이터를 객체 관점으로 보며, DB에 접근 시 SQL쿼리문보다 DB의 테이블에 데이터를 저장 및 조회
      할 경우, Java의 객체를 이용하여 이 객체 자체를 쿼리문으로 자동 변환한 후에 DB테이블에 접근

2. Spring Data JDBC 사전 준비
1. 의존 라이브러리 추가

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
runtimeOnly 'com.h2database:h2'  -> 인메모리 DB를 인 H2 사용
}

 1. 인메모리 DB
     - 일반적인 DB와 달리, 휘발성으로 애플리켄이션이 실행되는 동안에만 데이터가 저장되는 DB공간
     - 개발환경에서 각 종 테스트를 진행하기 위해 이를 제외한 나머지 데이터를 제외하고 테스트하여 정확도가 올라감
     - 이런 이유로, 로컬 개발환경에서는 인메모리DB를 주로 사용
  1. application.yml 파일에 H2 Browser 활성화 추가.
    • src/main/resources 하단에 application.properties라는 파일의 확장자를 yml로 고치고 아래 코드 추가
    • yml파일은 설정정보(프로퍼티)를 depth별로 입력하는 더 나은 방법이기에
spring:
  h2:
    console:
      enabled: true
      path: /h2      --> (1)
  datasource:
      url: jdbc:h2:mem:test --> (2)

 (1) H2 콘솔의 접속 URL Context path를 간결하게 /h2로 설정
 (2) JDBC URL이 매번 바뀌지 않도록 jdbc:h2:mem:test로 설정

3. Spring Data JDBD 실습 (Hello World 샘플)

  1. CrudRepository

    • 인터페이스로, DB의 CRUD(생성,조회,수정,삭제)작업을 진행하기 위해 Spring에서 지원해주는 인터페이스
    • 예제에서는 extends CrudRepository<Message, Long> 제네릭으로 선언
    • Message는 Message엔티티 클래스 객체에 담긴 데이터를 DB 테이블에 생성/수정하거나 조회한 데이터를 Message형태로 변환
    • Long은 멤버변수 중 식별자를 의미하는 @Id라는 애터테이션이 붙어있는 멤버타입의 변수
  2. Spring Data JDBC 적용 순서

    1). build.gradle에 사용할 데이터베이스를 위한 의존 라이브러리를 추가합니다.
    2). application.yml 파일에 사용할 데이터베이스에 대한 설정을 합니다.
    3). ‘schema.sql’ 파일에 필요한 테이블 스크립트를 작성합니다.
    4). application.yml 파일에서 ‘schema.sql’ 파일을 읽어서 테이블을 생성할 수 있도록 초기화 설정을 추가합니다.
    5). 데이터베이스의 테이블과 매핑할 엔티티(Entity) 클래스를 작성합니다.
    6). 작성한 엔티티 클래스를 기반으로 데이터베이스의 작업을 처리할 Repository 인터페이스를 작성합니다.
    7). 작성된 Repository 인터페이스를 서비스 클래스에서 사용할 수 있도록 DI 합니다.
    8). DI 된 Repository의 메서드를 사용해서 서비스 클래스에서 데이터베이스에 CRUD 작업을 수행합니다.


4. application.yml 설정 정리

[예제 Code]
spring:
  h2:
    console:
      enabled: true
      path: /h2                                                   # (1) Context path
  datasource:
    url: jdbc:h2:mem:test                                      # (2) JDBC URL
  sql:
    init:
      schema-locations: classpath*:db/h2/schema.sql   # (3) 테이블 생성 파일 경로
      data-locations: classpath*:db/h2/data.sql
logging:
  level:
    org:
      springframework:
        jdbc:
          core: TRACE

(1) H2콘솔의 접속 URL Context path를 간결하게 /h2로 설정
(2) JDBC URL이 매번 바뀌지 않게 'jdbc:h2:mem:test'로 설정



Spring Data JDBC 기반의 도메인 엔티티 및 테이블 설계


1. DDD(Domain Driven Design)란?

  • 도메인 주도 설계로, 도메인 위주로의 설계 기법
  • Spring Data JDBC에서 DDD의와 밀접한 연관이 있어, 기본 개념을 탑제 필요
  1. 도메인이란?

    • 실제 현실에서 접하는 업무의 한 영역으로, 비지니스적인 업무 영역과 관련이 있다.
      [Ex. 상위 도메인 - 회원, 주문, 음식, 배달, 결제 등][Ex. 하위 도메인 - 회원(회원 정보, 회원 포인트), 음식(음식정보),주문(...), 결제(...)]
  2. 애그리거트(Aggregate)란?

    • 비슷한 업무 도메인들의 묶음
    • 비슷한 범주의 연관된 업무들을 하나로 그룹화 해놓은 그룹
      [Ex. 회원/주문/음식/결제 애그리거트 ]
  3. 애그리거트 루트 란?

    • 각각의 애그리거트 안에 해당 애그리거트를 대표하는 도메인
    • 각 애그리거트안의 모든 도메인들과 직/간접적으로 연관이 되어있는 도메인을 애그리거트 루트 라고함
    • 애그리거트 루트의 기본키 정보를 다른 도메인들이 외래키의 형태로 가지고 있음
    • 특정 집단, 그룹의 대표
      [Ex. 아파트 각 1,2,3동의 각 동 대표]



2. 샘플 애플리케이션 도메인 엔티티 및 테이블 설계

  • ORM기반의 데이터 엑세스 기술로, 각 클래스 간의 연관 관계를 찾기
  • 1:N, N:M 간의 관계를 찾아 매핑 정보 세팅
  • 애그리거트 루트는 Spring data JDBC가 DDD와 밀접한 관련이 있기에, 애그리거트 루트를 잘 찾아야함



Extra

  1. DDD(Domain Driven Design)
  1. 애그리거트(Aggregate)



Spring Data JDBC를 통한 데이터 엑세스 계층 구현(1) - 도메인 엔티티 클래스 정의


1. 엔티티/테이블 설계 확인

  • 엔티티/테이블 설계 확인 [사진]
  • 테이블간에는 기본키-외래키를 통해 연결요소가 직관적
  • 엔티티간에는 객체 간에 참조가 가능하여 객체-객체_List(Set)를 사용해서 기본키-외래키 기능을 대신함

2. Spring Data JDBC에서의 애그리거트 객체 매핑

  • 도메인 엔티티 클래스에서 Spring Data JDBC를 사용하기 위해 설계한
    도메인 엔티티 클래스의 관계를 DDD의 애그리거트 매핑 규칙에 맞게 한번 더 변경이 필요함
    (애그리거트, 애그리거트 루트가 필요)
  • ★1대N 관계의 애그리거트 루트 간 ID참조는 객체 참조가 아닌 ID 참조로 이루어진다.
    [Ex. AggregateReference<Member, Long> memberId, 애그리거트 루트간에는 Id로 참조한다.]
  • ★N대M 관계의 애그리거트 루트 간 참조는, 두 애그리거트 루트를 이어주는 중간 엔티티가 필요함(CoffeeRef)
  1. @애너테이션
    1). @Table("ORDERS")
    - 정의하지 않으면, 기본적으로 클래스 이름이 테이블 이름과 매핑됨
    2). @Id
    - 해당 멤머변수를 식별자로 지정한다는 의미
    3). @MappedCollection(idColumn = "ORDER_ID")
    - 엔티티 클래스 간에 연관 관계를 맺어지게 해주는 정보
    - 즉, 클래스 간의 기본키-외래키 형태로, 외래키 컬럼(idColumn)을 의미함
    - 외래키가 포함된 테이블을 자식테이블이라고함
    - JPA에서는 @OneToMany(mappedBy="")

  2. 애그리거트 객체 매핑 규칙

    • 애그리거트 루트를 통해서만 나머지 엔티티 상태를 변경해야 도메인 규칙에 대한 일관성이 유지된다.
      1). ★모든 엔티티 객체의 상태는 애그리거트 루트를 통해서만 변경할 수 있다.
      2). 하나의 동일한 애그리거트 내에서의 엔티티 객체 참조
      - 동일한 하나의 애그리거트 내에서는 엔티티 간에 객체로 참조한다.
      3). 애그리거트 루트 대 애그리거트 루트간의 엔티티 객체 참조
      - 애그리거트 루트 간의 참조는 객체 참조 대신 ID로 참조한다.
      - 1대1과 1대N 관계일 때는 테이블 간의 외래키 방식과 동일하다.
      - N대M관계일 때는 외래키 방식과 객체 참조 방식이 함께 사용된다.
      4). 1:N 의 관계에서 N쪽, 즉, 외래키를 받는 지점에 애그리거트를 통해 @Id를 참조하도록 애그리거트 객체를 만듬



Extra

  1. AggregateReference

  2. @MappedCollection




Spring Data JDBC를 통한 데이터 엑세스 계층 구현(2) 서비스, 리포지토리 구현


0. @애너테이션

  1. @Query("SELECT * ... =: coffeeId")
  • 사용자가 직접 쿼리문을 작성하여 질의를 할 수 있게 해주는것
  • :coffeeId 는 메소드의 매개변수로 값이 채워지는 동적 쿼리 파라미터이다.
    [Ex. @Query("SELECT FROM MEMBER ORDER BY MEMBER_ID DESC LIMIT (:page-1):size, :size")]



1. 리포지토리(Repository) 인터페이스

  • DDD에서 사용하는 언어로 JPA,JDBC에서 데이터엑세스 계층에서 DB와 상호작용을 하는 역할을 하는 인터페이스

    (1) CrudRepository<엔티티클래스, 기본키 멤버변수>를 상속하여 사용
    (2) 쿼리 메서드 정의를 이용한 데이터 조회 메서드 정의
    ‘find + By + SQL 쿼리문에서 WHERE 절의 컬럼명 + (WHERE 절 컬럼의 조건이 되는 데이터) ’

        WHERE절의 조건 컬럼을 여러개 지정하고  싶다면 'And' 사용'
          IF, EMAIL,NAME 컬럼을 지정하고 싶다면 
        [EX. findByEmailAndName(String email, String name)]처럼 사용하면 된다. 
  • 쿼리 메소드 안에는 엔티티 클래스의 멤버변수명을 적어 주어야한다.

  • 멤버 변수의 경우, 카멜케이스(exampleCase)를 사용해야 한다.

[예제 Code]
import com.codestates.member.entity.Member;
import org.springframework.data.repository.CrudRepository;

import java.util.Optional;

// (1)
public interface MemberRepository extends CrudRepository<Member, Long> {
       // (2)
      Optional<Member> findByEmail(String email);
     
      [SELECT "MEMBER"."NAME" AS "NAME", "MEMBER"."PHONE" AS "PHONE", "MEMBER"."EMAIL" AS
      "EMAIL", "MEMBER"."MEMBER_ID" AS "MEMBER_ID" FROM "MEMBER" **WHERE "MEMBER"."EMAIL"        = ?**]와 같음
}
  • MemberRepository인터페이스의 경우, JDBC내부적으로 Java 리플렉션 기술과 Proxy 기술을 이용해서 MemberRepository인터페이스의 구현 클래스 객체를 생성해준다.
  • 비지니스 로직에서 검증이 필요한 로직은 따로 검증 메소드를 활용해서 검증을 하는것이 직관적으로 보기쉽다.
  • Spring Data JDBC에서는 @Id애너테이션이 붙은 엔티티 클래스의 멤버변수값이 0 또는 Null이면, 신규 데이터로 판단하여 테이블에 insert쿼리를 전송한다.



2. 추가적으로 GitHub의 jdbc 템플릿 코드 확인

Extra_Optional
1. Optional.of(10);
- Optional객체를 만들어 해당 값이 10 인경우, 만일 null값이 들어가면 NullPointerException발생
2. Optional.ofNullable();
- Null이 가능
3. Optional.empty()
- 비어있는 경우로, 주로 Null값일 때 사용
4. Optional.isPresent()
- 내부에 값이 있는지 없는지 확인, 값이 있을시 True
5. Optional.isEmpty()
- 값이 비어있을 경우 True
6. Optional.get()
- 내부 값을 가져온다.
7. Optional.ifPresent(Consumer )
- Optional에 값이 있는 경우 그 값을 Consumer Functional Interface에 전달 후 로직을 수행.
8. Optional.orElse(T)
- 값이 있을 경우 가져오고 없을 경우 인자값으로 선언한 내용을 반환
9. Optional.orElseGet(Supplier)
- 값이 있으면 가져오고 없는 경우에 Supplier Functional Interface로직을 수행한다.
10. Optional.orElseThrow()
- 값이 있으면 가져오고 없으면 에러를 던진다.
11. Optional.filter(Predicate)
- Predicate Functional Interface를 수행 하여 조건에 부합하는 값을 가져온다.
12. Optional map(Function)
- Function Functional Interface를 수행하여 내부 값을 순회하며 변경한 후 반환한다.
13. Optional flatMap(Function)
- Optional 안에 들어있는 인스턴스가 Optional인 경우 내부 원소값을 꺼낼 때 사용한다.




Extra
1. Spring Data JDBC 쿼리 메서드
- https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#jdbc.query-methods
- https://docs.spring.io/spring-data/jdbc/docs/current/reference/html/#repositories.query-methods.details
2. Iterator vs Iterable 차이점
- Iterable은 필드멤버로 Iterator를 가지고있고, Iterable은 Collection(list,set등)의 상위 인터페이스이다.
- Iterable을 다운 캐스팅하여 하위 컬렉션으로 받을 수 있음
- https://devlog-wjdrbs96.tistory.com/84

  1. Java DB 정규화 5단계
    • 3단계 정도만 해서 현업에서 자주사용



Spring Data Spring Data JDBC를 이용한 데이터 엑세스 실습


1. Spring Data JDBC 기반 데이터 엑세스 계층 연동 실습

  • 회원 정보 목록 조회 기능에 페이지네이션 기능을 적용
  • 페이지네이션 : 전체 리소스중 일부분만을 나누어 요청해달라는 것
  • 데이터 추가없이, Controller, Service, Repository, Controller에서 리턴하는 Response용 Dto 클래스 수정

2. 페이징(Paging,Page),페이지네이션 API
0. 사진.

  1. PagingAndSortingRepository 인터페이스

    • Repository.CrudRepository 인터페이스를 상속받아 Pagingxx가 아래 정렬과, 페이징 기능을 제공해준다.
    • org.springframework.data.domain.[Sort,.Pageable]
  2. Page 도메인과 Pageable
    1). Pageable
    - Page의 정보들을 담고있는 객체이다.
    - PageRequest(page,size,[Sort])를 통해 Pageable인터페이스를PageRequest객체로 받는다.
    Sort부분 정렬의 경우 선택적이며, 자세한 사항은 인터페이스 확인(Sort.by("tet").descending()
    - 이를 PagingAndSortingRepository 인터페이스의 findAll(Pageable) 메서드를 통해 Page 제네릭
    객체 형태로 구현화해서 받을 수 있음

    2). Page
    - 실제 리소스를 가지고 있는 객체로, Content안에 domain이 담겨있다.
    - Slice<>인터페이스를 상속하며, PageImpl 구현 클래스를 통해 생성
    - getContent()를 통해 값들을 가져올 수 있으며, 그 밖에 여러 메서드들이 있음
    - https://ithub.tistory.com/28 확인 or 직접확인

    • org.springframework.data.domain.[Page,Pageable]

3. Comparator 구현

  1. Comparator인터페이스를 통해 직접 정렬 기준을 Sorting 할 수 있음
  2. List와 같이 객체 리스트를 특정 객체 멤버변수로 정렬할 수 있음
  3. Implement Comparator<T(실제 구현 클래스)>{}를 통하여 클래스 안에 직접 Comparator메소드를
    @Overriding을 하여 기준을 정렬 할 수 있음
  4. o1 < o2 = 1 일경우 내림차순, 반대의 경우 오름차순으로 정렬 할 수 있다.
[예제 Code]
Collections.sort(list, new Comparator<User>() {
            public int compare(User o1, User o2) {
                if(o1.getNo()>o2.getNo()) {
                    return -1;
                }else if(o1.getNo()<o2.getNo()) {
                    return 1;
                }else {
                    return 0;
                }
            }
        });



  1. 피드백 😮
  • Spring Data JDBC를 이용하여 엔티티, 리포지토리를 설계하고 이를 기반으로 실습을 하였다.

  • 리포지토리의 경우, CrudRepository<Entity, Type>을 상속받아 DB에 접근하는 쿼리문을 자동으로 떤져주는 리포지토리 인터페이스를 만들어 이를 사용하였다.

  1. 앞으로 해야 될 것 😮
  • 매일 꾸준히 할 것
    • 꾸준히 velog 작성
    • Java 언어 및 Algorithm 공부(Coding-Test)
    • 틈틈히 운동 하기

  • 내일 해야 할 것
    • Spring Data JPA 학습
profile
Will be great Backend-developer

0개의 댓글