본문 바로가기
Backend boot camp/Session3

[Spring MVC] JDBC DB Access Layer

by orioncsy 2022. 10. 29.

JDBC

JDBC 개념

  • Java DataBase Connectivity
    • 자바 기반 애플리케이션 코드에서 데이터를 db에 저장하거나 가져오는 Java에서 제공하는 표준 사양(specification)
  • JDBS API를 사용하여 oracle, MS SQL, MySQL 등의 db와 연동 가능
  • Spring Data JDBC, Spring Data JPA 모두 내부적으로 JDBC를 사용한다.

JDBC 동작 흐름

Java 애플리케이션 → JDBC API → JDBC 드라이버 → 데이터베이스

  • Java 애플리케이션 내 JDBC API를 사용하여 db에 접근하는 구조
  • JDBC API는 드라이버를 로딩 후에 db에 연결

JDBC 드라이버(JDBC Driver)

  • JDBC 드라이버는 다양한 벤더에 맞는 JDBC 드라이버를 구현해서 제공하고 구현체를 이용해 벤터의 db에 접근 가능

JDBC API 사용 흐름

  • JDBC 드라이버 로딩
    • DriverManager 클래스를 통해 로딩
  • Connection 객체 생성
    • DriverManager를 통해 db와 연결되는 Connection 객체 생성
  • Statement 객체 생성
    • SQL 쿼리문을 실행하기 위한 객체, SQL 쿼리 문자열을 입력으로 가진다
  • Query 실행
    • SQL 쿼리 실행
  • ResultSet 객체로부터 데이터 조회
    • 결과 데이터셋
  • ResultSet 객체 close → Statement 객체 close → Connection 객체 close
    • 사용된 객체들을 사용 역순으로 close

Connection Pool

  • Connection 객체를 애플리케이션 로딩 시점에 미리 생성해두고 미리 만들어 둔 객체를 사용하여 성능 향상
  • 미리 Connection을 만들어서 보관하고 제공해주는 Connection 관리자를 Connection Pool

Spring Data JDBC

데이터 액세스 기술 유형

  • mybatis, Spring JDBC, Spring Data JDBC, JPA, Spring Data JPA

SQL 중심 기술

  • 대표적 SQL 중심 기술 : mybatis, Spring JDBC
  • SQL문을 애플리케이션 내부에 직접 작성하는 방식

객체(Object) 중심 기술

  • 자바 객체를 이용해 SQL 쿼리문으로 자동 변환 후 데이터베이스에 접근
  • ORM(Object-Relational Mapping)
  • 대표적 object 중심 기술 : JPA, Spring data JDBC

Spring Data JDBC

  • Spring Data JDBC - 최근에 릴리즈 되었지만 규모가 작고 복잡하지 않은 경우 사용
  • JPA - 실무에서 가장 많이 사용하는 기술
  • Spring Data JPA - Spring에서 JPA를 사용하기 위한 기술

JDBC 사용

의존 라이브러리 추가

  • implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
    • Spring Data JDBC 사용 라이브러리
  • runtimeOnly 'com.h2database:h2'
    • 인메모리 DB인 H2 사용 라이브러리
    • 인메모리 사용은 로컬 개발 환경에서 테스트를 진행하고 저장된 내용이 초기화되는 것에 유리하여 자주 사용

H2 Browser 활성화

  • src/main/resources 디렉터리에 application/properties 파일
  • application.yml(혹은 application.yaml)로 수정하여 저장(shift + F6)
spring:
	h2:
		console:
			enabled: true
  • 애플리케이션을 실행하면 로그에서 db 정보 확인 가능
  • H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:9d6af270-4aab-4742-b3d5-cb82d1276124’
    • /h2-console로 웹 접속 가능(localhost:8080/h2-console)
    • JDBC URL에 jdbc:h2:mem:9d6af270-4aab-4742-b3d5-cb82d1276124로 넣고 connect
  • 디폴트 설정의 문제점
    • JDBC URL이 애플리케이션 시작할 때마다 변경된다
    • 고정시키기 위해서 application.yml 변경
    spring:
    	h2:
    		console:
    			enabled: true
    			path: /h2
    	datasource:
    		url: jdbc:h2:mem:test 
    
    • h2콘솔 접속 URL을 /h2로 변경
    • JDBC URL 항목을 jdbc:h2:mem:test로 변경

간단한 메세지 전송 코드 구현

  • Dto, entity, controller, mapper, service, repository를 구현
  • Repository 구현
    • public interface MessageRepository extends CrudRepository<Message, Long>{}
    • CrudRepository를 선언하고 제너릭 타입을 해당 객체, long으로 선언
    • 해당 엔티티 클래스 객체에 담긴 데이터를 db 테이블에 가공한 데이터를 엔티티 클래스로 변환 가능
    • long은 entity 클래스에서 식별자를 의미하는 @Id라는 애너테이션이 붙은 멤버 변수 타입
  • Service에 repository를 final로 선언
    • repository를 이용하여 save 메서드를 사용하여 객체를 저장 및 다양한 메서드 활용하여 crud 작업 가능
  • Entity
    • entity 명은 db에서 테이블 명에 해당
    • @Id를 추가한 멤버 변수는 고유 식별자로 PK로 지정한 칼럼
  • application.yml에서 테이블 생성 경로 추가
  • src/main/resources/db/h2에 schema.sql 파일을 읽어서 테이블 생성
spring:
	h2:
		console:
			enabled: true
			path: /h2
	datasource:
		url: jdbc:h2:mem:test
	sql:
		init:
			schema-locations: classpath*:db/h2/schema.sql
  • schema.sql
    • MESSAGE라는 테이블이 없으면 생성
    • message_id는 insert 될 때마다 자동 증가
    • message_id는 PK로 등록
    CREATE TABLE IF NOT EXISTS MESSAGE (
        message_id bigint NOT NULL AUTO_INCREMENT,
        message varchar(100) NOT NULL,
        PRIMARY KEY (message_id)
    );
    
    • ORM에서는 객체 멤버 변수와 데이터베이스 칼럼이 1대 1 매핑
  • 실행해서 postman으로 값을 post 하면 H2콘솔로 확인해보면 저장된 것을 확인 가능

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

DDD(Domain Driven Design)

  • 도메인 주도 설계

Domain

  • 비즈니스적인 업무 영역
  • 도메인 지식을 서비스 계층에서 비즈니스 로직으로 구현해야 한다
  • 상위 수준의 도메인은 큰 범주의 기능들(회원, 주문, 음식, 결제)
  • 하위 수준의 도메인은 상위 수준 도메인 아래에 세부 기능

Aggregate

  • 비슷한 업무 도메인들의 묶음을 의미

Aggreate Root

  • 각 aggregate 안에 대표적인 도메인
  • 선정 기준
    • aggregate 내에 다른 도메인과 직간접으로 연관되어 있는 도메인으로 선정
  • DB에서는 aggregate root가 부모 테이블이 되고 다른 도메인이 자식 테이블이 된다
    • 자식 테이블은 부모 테이블의 기본키를 가지고 있고 외래 키라 부른다.

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

  • 도메인에서 aggregate root를 찾기
    • 회원정보, 주문정보, 커피 정보
  • 회원 정보 - (주문 정보 - 주문 커피 정보) - 커피정보
    • 각 aggregate 간의 관계는 1대 N으로 표시
    • 회원 정보와 주문 정보는 1대 N
    • 주문 정보와 커피 정보는 N대 N
    • 주문 커피 정보를 매개로 1대 N, N대 1로 재구성

엔티티 클래스 간 관계

  • 객체 참조를 통해서 관계 형성
  • Member entity class
    • Member 클래스와 Order 클래스는 1대 N
    • Member 클래스에 List <Order> 멤버 변수 추가
  • Order entity class
    • Order class와 Coffee class는 N대 N 관계를 가지기 때문에 1대 N을 가지도록 List<OrderCoffee>를 맴버 변수로 추가
  • Coffee entity class
    • Coffee class 와 Order class는 N대 N 관계를 가지기 때문에 1대 N을 가지도록 List <OrderCoffee>를 멤버 변수로 추가
  • OrderCoffee table
    • 주문하는 커피가 한잔 이상일 수 있어 quantity 추가

데이터베이스 테이블 설계

  • 테이블 간의 관계를 외래 키 참조로 형성
  • 주문 테이블은 ORDERS로 하여 ORDER BY와 혼돈 방지
  • 1대 N의 관계를 가질 때 1에 해당하는 테이블 pk를 N에 해당하는 테이블에서 외래 키로 참조

Spring Data JDBC 데이터 액세스 계층 구현

엔티티 설계 확인

  • 회원, 주문, 커피를 엔티티 클래스로 각각 객체를 참조하여 관계를 설정한 설계

테이블 설계 확인

  • 회원, 주문, 커피를 엔티티 클래스로 각각 외래 키를 참조하여 설계

Aggregate 객체 매핑

  • 데이터베이스 테이블은 스키마를 사용해서 테이블 생성
  • 엔티티 클래스는 DDD의 aggregate 매핑 규칙에 맞게 변경

Aggregate 객체 매핑 규칙

  • 모든 엔티티 객체 상태는 aggregate root를 통해서만 변경 가능
  • 동일한 하나의 aggregate 내에서는 엔티티 간 객체로 참조
  • aggregate root 간 엔티티 객체 참조
    • 객체 참조 대신 ID로 참조
    • 1대 1과 1대 N 관계일 때 테이블 간 외래 키 방식과 동일
    • N대 N일 때 외래 키 방식인 ID참조와 객체 참조 방식이 함께 사용

Entity 구현

  • Member 클래스와 Order 클래스의 aggregate root 매핑
    • Member 클래스에서 memberId를 @Id로 지정
    • Order 클래스에서는 @Table(”ORDERS”)로 이름 변경
    • Order 클래스의 orderId를 @Id로 지정
    • Order 클래스에 AggregateReference <Member, Long> memberId로 멤버 변수 생성하여 외래키 처럼 참조
  • Order 클래스와 Coffee 클래스의 aggregate root 매핑(M:N 관계)
    • Coffee 클래스에서 coffeeId를 @Id로 지정
    • Coffee 클래스에서 String coffeeCode를 맴버 변수로 생성(중복 등록 체크를 위한 변수)
    • M:N 관계를 풀어줄 CoffeeRef 클래스를 생성
    • Order 클래스
      • @MappedCollection(idColumn = “ORDER_ID”)
      • private Set <CoffeeRef> orderCoffees=new LinkedHashSet <>();
      • @MappedCollection 사용법
        • CoffeeRef entity는 order와 동일한 aggregate에 속하고 이에 따라 객체 참조 방식 사용
        • MappedCollection 애너테이션은 ORDERS와 ORDER_COFFEE 테이블을 관계를 맺어주고 ORDER_ID(ORDER의 기본키)가 ORDER_COFFEE의 외래 키가 되어 idColumn에 배정해준다.(자식 테이블에 추가되는 외래키 칼럼명)
        • idColumn이 아닌 keyColumn은 외래 키를 포함하는 테이블의 기본키 칼럼명
        • 필드가 List인 경우 keyColumn 값 입력이 필수, Set인 경우 필수가 아니다.
    • CoffeeRef 클래스
      • @Table(”ORDER_COFFEE”)로 테이블 이름 변경(이름을 변경하지 않으면 클래스 이름 그대로 설정)
      • coffeeId, quantity를 필드 멤버로 설정
      • coffeeId를 AggregateReference로 감쌀 필요가 없는 이뉴는 CoffeeRef는 Order Aggregate 내에 있는 클래스이고 Coffee 클래스는 Aggregate root에 해당하기 때문이다.
  • Order 클래스 맴버 변수 추가
    • OrderStatus라는 enum을 생성하여 number와 description을 필드로 가지는 주문 단계를 만들어 Order 클래스 필드 맴버로 추가
    • LocalDateTime 클래스를 활용하여 createdAt을 선언하고 현재 시간으로 초기화
  • 해당 클래스 관계 설정에 맞게 schema.sql에 정의

Spring Data JDBC 서비스, 레포지토리 구현

레포지토리 인터페이스 정의

  • CrudRepository <Object, Long>를 extends 하여 각 정보의 레포지토리 인터페이스 생성
  • 제너릭에서 Object는 해당 정보의 객체를 의미하고 Long은 엔티티 클래스에서 @Id가 붙은 멤버 변수 타입을 의미
  • 바디에는 쿼리 메서드 작성
    • findBy [엔티티 명](조건 데이터) 형태로 반환형은 Optional로 감싼 객체를 반환
    • 조건이 두 개일 경우 메서드명을 And로 붙이고 파라미터를 두 개로 받는다.
  • @Query 애너테이션을 사용한 쿼리 매서드
    • 괄호 안에 쿼리문 직접 작성 가능 :[칼럼명]은 매개변수로 받은 변수 값이 채워지는 동적 쿼리 파라미터
  • findById(ID id)를 사용하면 기본키를 조회하는 쿼리 메서드 사용 가능

서비스 클래스 구현

  • 레포지터리를 필드 멤버로 선언하고 DI를 통해 생성자 작성
  • verifyExists 메서드 생성 엔티티의 특정 멤버로 이미 존재하는지 확인하는 메서드를 생성
    • Optional로 선언하여 repository에서 findBy 메서드로 할당
    • isPresent()를 사용하여 존재하면 MEMBER_EXISTS로 예외 발생
  • findVerifiedMember 메서드 선언
    • memberId를 받아서 Optional로 선언하여 findById를 통해 할당
    • orElseThrow(()→…))를 통해 null이 아니라면 객체를 반환하고 null이면 예외를 던진다.
  • create 서비스나 update 서비스
    • create는 repository.save를 통해 저장
    • update도 repository.save를 통해 저장하면 @Id가 0이거나 null이 아니면 update 형식으로 저장 가능
    • update 할 때 Optional.ofNullable로 감싸고 ifPresent()를 사용하여 값이 null이 아니면 람다로 값을 넣어주는 방식 사용
  • findMember 서비스, findMembers 서비스
    • findMember는 findVerifiedMember메서드 사용
    • findMembers는 findAll() 메서드 사용하고 반환형이 Iterable <T>이기 때문에 해당 타입으로 캐스팅 필요
  • deleteMember 서비스
    • delete() 메서드 사용

'Backend boot camp > Session3' 카테고리의 다른 글

[Spring MVC] 트랜잭션  (0) 2022.11.07
[Spring MVC] JPA 데이터 액세스 계층  (0) 2022.11.07
[Spring MVC] Exception Handle  (0) 2022.10.29
[Spring MVC] Service layer  (0) 2022.10.29
[Spring MVC] API Layer  (0) 2022.10.29