데이터 액세스 기술 유형
1. SQL 중심 기술
- 애플리케이션에서 데이터 베이스에 접근하기 위해 쿼리문을 애플리케이션 내부에 직접적으로 작성하는 것이 중심인 기술
- mybatis
- SQL Mapper 설정 파일에 쿼리문을 직접적으로 작성
- 작성된 쿼리문을 기반으로 데이터베이스에서 데이터 조회 후 Java 객체로 변환
- Spring JDBC
2. ORM(Object-Relational Mapping)
- 데이터를 쿼리문이 아닌 객체 관점에서 다룸
- 데이터베이스와 상호작용을 할 시 Java 객체를 이용해 쿼리문으로 자동 변환 후 데이터베이스에 접근
- Spring Data JDBC
- JPA(Java Persistence API)
DDD
- 도메인 주도 설계(Domain Driven Design)
1. 도메인 : 비즈니스적인 어떤 업무의 영역, 분야
ex) 배달 주문앱의 도메인
2. 애그리거트(Aggregate)
- 도메인들을 세분화하여 비슷한 업무 도메인들의 묶음
3. 애그리거트 루트(Aggregate Root)
- 해당 애그리거트를 대표하는 도메인
- 다른 도메인들과 직간접적으로 연관
- DB와 비교했을때 애그리거트 루트는 부모 테이블, 다른 도메인들은 자식 테이블
- 애그리거트 루트의 기본키 정보를 다른 도메인들이 외래키로 가지고 있는 형태
테이블 설계
JDBC
- JDBC(Java Database Connectivity)
- java 기반 애플리케이션의ㅏ 코드 레벨에서 사용하는 데이터를 데이터베이스에 저장, 업데이트 하거나 데이터베이스에 저장된 데이터를 코드 레벨에서 사용할 수 있게 해주는 java에서 제공하는 표준 사양
- JDBC를 이용해 다양한 데이터베이스와 연동 가능
1. JDBC 동작 흐름
Java 애플리케이션에서 JDBC API를 이용해 데이터베이스 드라이버를 로딩 후 데이터베이스와 상호작용
1) Java 애플리케이션
2) JDBC API
3) JDBC 드라이버
4) 데이터베이스
2. JDBC API 사용 흐름
1) JDBC 드라이버 로딩
2) Connection 객체 생성
- JDBC 드라이버가 로딩되면 DriverManager를 통해 데이터베이스와 연결되는 세션인 Connection 객체 생성
- Connection Pool
- Connection 객체를 미리 만들어 보관하고 애플리케이션이 필요할때 Connection 객체를 제공해주는 Connection 관리자
- 기본 DBCP : HikariCP
3) Statement 객체 생성
- 작성된 SQL 쿼리문을 실행하기 위한 객체
- 생성 후 정적 SQL 쿼리 문자열을 입력으로 가짐
4) Query 실행
- 생성된 Statement 객체를 이용해 입력한 SQL 쿼리를 실행
5) ResultSet 객체로부터 데이터 조회
6) ResultSet, Statement, Connection 순으로 close
Spring Data JDBC
1. 기술 적용 순서
- build.gradle에 사용할 데이터베이스를 위한 의존 라이브러리를 추가
- application.yml 파일에 사용할 데이터베이스에 대한 설정
- ‘schema.sql’ 파일에 필요한 테이블 스크립트를 작성
- application.yml 파일에서 ‘schema.sql’ 파일을 읽어서 테이블을 생성할 수 있도록 초기화 설정을 추가
- 데이터베이스의 테이블과 매핑할 엔티티(Entity) 클래스를 작성
- 작성한 엔티티 클래스를 기반으로 데이터베이스의 작업을 처리할 Repository 인터페이스를 작성
- 작성된 Repository 인터페이스를 서비스 클래스에서 사용할 수 있도록 DI
- DI 된 Repository의 메서드를 사용해서 서비스 클래스에서 데이터베이스에 CRUD 작업을 수행
2. In-Memory DB
- 연습을 위해 인메모리 DB H2 사용
- build.gradle의 dependencies에 h2 의존 라이브러리 추가
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
runtimeOnly 'com.h2database:h2'
}
- application.properties 파일을 설정 정보를 depth 별로 입력할 수 있는 application.yml로 변경
- h2 설정 추가
spring:
h2:
console:
enabled: true
- http://localhost:8080/h2-console 접속
- JDBC URL 입력창에 프로젝트 실행 후 출력되는 로그
H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem: ...'
'jdbc:h2:mem: ...' 부분을 입력
- h2 콘솔 접속 URL 바꾸기
- application.yml 파일에 설정 추가
spring:
h2:
console:
enabled: true
path: /h2 # (1) Context path 변경
datasource:
url: jdbc:h2:mem:test # (2) JDBC URL 변경, 해당 URL로 접속 가능
3. 애그리거트 객체 매핑
- 설계한 도메인 엔티티 클래스 관계를 DDD의 애그리거트 매핑 규칙에 맞게 변경 필요
- 애그리거트 객체 매핑 규칙
- 1) 모든 엔티티 객체는 애그리거트 루트를 통해서만 변경할 수 있다.
- 2) 하나의 동일한 애그리거트 내에서의 엔티티 간에는 객체로 참조한다.
- 3) 애그리거트 루트 간의 참조는 객체 참조 대신 ID로 참조한다.
- 1:1 관계, 1:N 관계일 때 테이블 간 외래키 방식과 동일
- N:N 관계일 때 외래키 방식인 ID 참조와 객체 참조방식 함께 사용
4. 엔티티 구현
1) Member 클래스와 Order 클래스 매핑(1 : N 관계)
- @Table 애너테이션이 없으면 클래스명이 테이블 이름과 매핑
- @Id 애너테이션으로 식별자 지정
2) Order 클래스와 Coffee 클래스 매핑(N : N 관계)
- Order 클래스와 Coffee 클래스는 N:N 관계로 중간에서 1:N, N:1로 풀어줄 OrderCoffee 엔티티는 Order 클래스와 동일한 애그리거트
- Order 클래스에서 Set< OrderCoffee>를 사용해 1:N관계 설정
- @MappedCollection 애너테이션
- 엔티티 클래스 간에 연관 관계를 맺어주는 정보
- idColumn : 자식 테이블에 추가되는 외래키에 해당되는 열명을 지정
- 예시 코드의 경우 ORDER_COFFEE 테이블은 ORDERS 테이블의 기본키인 ORDER_ID를 외래키로 가짐
- keyColumn : 외래키를 포함하고 있는 테이블의 기본키 열명을 지정
@MappedCollection(idColumn = "ORDER_ID", keyColumn = "ORDER_COFFEE_ID")
- Set 자료구조의 경우 keyColunm은 생략 가능하지만 List, Map 일 경우 생략 불가능
3) 테이블 생성 스크립트
- 로컬환경에서 인메모리 DB를 사용해 테스트 할 경우 테이블을 DROP 했다가 다시 생성할 필요 없이 테스트 가능
CREATE TABLE IF NOT EXISTS ORDERS (
ORDER_ID bigint NOT NULL AUTO_INCREMENT,
MEMBER_ID bigint NOT NULL,
ORDER_STATUS varchar(20) NOT NULL,
CREATED_AT datetime NOT NULL,
PRIMARY KEY (ORDER_ID),
FOREIGN KEY (MEMBER_ID) REFERENCES MEMBER(MEMBER_ID)
);
5. Repository 인터페이스 정의
- Repository : 데이터 엑세스 계층에서 데이터베이스와 상호작용하는 역할을 하는 인터페이스
public interface MemberRepository extends CrudRepository<Member, Long> {
Optional<Member> findByEmail(String email);
}
@Query("SELECT * FROM MEMBER WHERE MEMBER_ID = :memberId")
Optional<Member> findByMember(Long memberId);
- (1) CrudRepository : crud 메서드를 제공하는 인터페이스
- <엔티티 클래스, @Id 애너테이션이 붙은 멤버 변수의 타입>
- (2) Spring Data JDBC에서 지원하는 쿼리 메서드
‘find + By + SQL 쿼리문에서 WHERE 절의 열명 + (WHERE 절 열의 조건이 되는 데이터) ’ 형식으로 쿼리 메서드(Query Method)를 정의하면 조건에 맞는 데이터를 테이블에서 조회
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" = ?
로 변환되어 테이블에 질의
-
Spring Data JDBC에서는 Optional을 지원하기 때문에 리턴값을 Optional로 래핑하여 처리 가능
-
(3) @Query 애너테이션으로 직접 쿼리문 작성 가능
- :memberId는 findByMember(Long memberId)의 memberId 변수 값으로 채워지는 동적 쿼리 파라미터
6. 서비스 클래스 구현 및 기타 코드 수정