0. 데이터베이스란?
데이터베이스는 데이터를 매우 효율적으로 보관하고 꺼내볼 수 있는 곳이다.
데이터베이스를 사용하면 얻을 수 있는 가장 큰 이점은 굉장히 많은 사람이 안전하게 데이터를 사용하고, 관리할 수 있다는 것이다.
데이터베이스 관리자, DBMS
DBMS : database management system, 데이터베이스를 관리하기 위한 소프트웨어
데이터베이스는 많은 사람이 공유할 수 있어야 하므로 동시 접근을 할 수 있어야 한다.
DBMS 이런 요구 사항을 만족하면서도 효율적으로 데이터베이스를 관리하고 운영한다.
예를 들어, MySQL, Oracle은 DBMS이다.
DBMS는 관리 특징에 따라서 관계형, 객체-관계형, 도큐먼트형, 비관계형 등으로 분류하며,
가장 많이 사용되는 DBMS는 관계형이다.
▶ 관계형 DBMS
관계형 DBMS, RDBMS
DBMS가 관계형 모델을 기반으로 한다.
즉, 테이블 형태로 이루어진 데이터 저장소를 말한다.
예를 들어, 회원 테이블이 있다고 가정하면 각 행은 고유의 키, 즉, 아이디를 가지고 있고,
이메일, 나이와 같은 회원과 관련된 값들이 들어간다.
ID | 이메일 | 나이 |
1 | a@test.com | 10 |
2 | b@test.com | 20 |
3 | c@test.com | 30 |
이때 데이터 1, a@test.com, 10을 묶어서 한 줄을 "행"이라고 하고,
ID, 이메일, 나이와 같은 구분을 "열"이라고 한다.
▶ H2, MySQL
스프링 부트에서 다룰 RDBMS는 H2, MySQL이다.
H2는 자바로 작성되어 있는 RDBMS이며,
스프링 부트가 지원하는 인메모리 관계형 데이터 베이스이다.
H2는 데이터를 다른 공간에 따로 보관하는 것이 아니라 애플리케이션 자체 내부에 데이터를 저장한다.
>> 애플리케이션을 다시 실행하면 데이터가 초기화 됨
하지만, 간편하게 사용하기 좋아서 개발 시에 테스트 용도로 많이 사용한다.
>> 실제 서비스로 올릴 때는 MySQL을 많이 사용한다.
1. ORM이란?
ORM: Object Relational Mapping, 자바의 객체와 데이터베이스를 연결하는 프로그래밍 기법
예를 들어, 데이터베이스에 age, name 컬럼에 20, 홍길동이라는 값이 들어있다고 가정
이러한 데이터를 자바에서 사용하려면 보통은 SQL 언어로 데이터를 꺼냄.
하지만 ORM이 있다면 데이터베이스의 값을 마치 객체처럼, 자바 언어로만 데이터베이스에 접근 가능함
>> 즉, 객체와 데이터베이스를 연결해 자바 언어로만 데이터베이스를 다룰 수 있게 하는 도구임
ORM의 장단점
장점
- SQL을 직접 작성하지 않고 사용하는 언어로 데이터베이스에 접근할 수 있음.
- 객체지향적으로 코드를 작성할 수 있기 때문에 비즈니스 로직에만 집중할 수 있음.
- 데이터베이스 시스템이 추상화되어 있기 때문에, 데이터베이스 시스템에 대한 종속성이 줄어듦.
- 매핑하는 정보가 명확하기 때문에 ERD에 대한 의존도를 낮출 수 있고 유지보수할 때 유리함
단점
- 프로젝트의 복잡성이 커질수록 사용 난이도도 올라감.
- 복잡하고 무거운 쿼리는 ORM으로 해결이 불가능한 경우가 있음.
2. JPA와 하이버네이트?
ORM에는 여러 종류가 있으며, 자바에서는 JPA를 표준으로 사용한다.
JPA : Java Persistence API, 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다.
인터페이스이므로 실제 사용을 위해서는 ORM 프레임 워크를 추가로 선택해야 한다.
>>대표적으로 "하이버네이트"를 많이 사용
하이버네이트: JPA 인터페이스를 구현한 구현체이자 자바용 ORM 프레임워크이다.
내부적으로는 JDBC API를 사용한다.
하이버네이트의 목표는
자바 객체를 통해 데이터베이스 종류에 상관없이 데이터베이스를 자유자재로 사용할 수 있게 하는 데 있다.
JPA와 하이버네이트의 역할
- JPA : 자바 객체와 데이터베이스를 연결해 데이터를 관리한다.
객체 지향 도메인 모델과 데이터베이스의 다리 역할을 한다 - 하이버네이트: JPA의 인터페이스를 구현한다.
내부적으로는 JDBC API를 사용한다.
2-1. 엔티티 매니저란?
엔티티
엔티티는 데이터베이스의 테이블과 매핑되는 객체를 의미한다.
엔티티는 본질적으로는 자바 객체이므로 일반 객체와 다르지 않다.
하지만, 데이터베이스의 테이블과 직접 연결된다는 아주 특별한 특징이 있어 구분 지어 부른다.
즉, 엔티티는 객체이긴 하지만 데이터베이스에 영향을 미치는 쿼리를 실행하는 객체이다.
엔티티 매니저
엔티티 매니저는 엔티티를 관리해 데이터베이스와 애플리케이션 사이에서 객체를 생성, 수정, 삭제하는 등의 역할을 함.
이런 엔티티 매니저를 만드는 곳이 엔티티 매니저 팩토리이다.
예를 들어, 회원 2명이 동시에 회원 가입을 하려는 경우 엔티티 매니저는 다음과 같이 업무를 처리한다.
- 회원 1의 요청에 대해서 가입 처리를 할 엔티티 매니저를 엔티티 매니저 팩토리가 생성
- 이를 통해 가입 처리해 데이터 베이스에 회원 정보 저장
- 회원 2도 같은 방식대로 회원 정보 저장
- 회원 1, 2를 위해 생성된 엔티티 매니저는 필요한 시점에 데이터베이스와 연결한 뒤에 쿼리
스프링 부트는 내부에서 엔티티 매니저 팩토리를 하나만 생성해서 관리하고
@PersistenceContext
@Autowired
애너테이션을 사용해서 엔티티 매니저를 사용한다.
@PersistenceContext
EntityManager em; // 프록시 엔티티 매니저. 필요할 때 진짜 엔티티 매니저 호출
▲스프링 부트가 엔티티 매니저를 사용하는 방법 예
또한, 스프링 부트는 기본적으로 빈을 하나만 생성해서 공유하므로 동시성 문제가 발생할 수 있다.
>> 실제로는 엔티티 매니저가 아닌 실제 엔티티 매니저와 연결하는 프록시(가짜) 엔티티 매니저를 사용한다.
>> 필요할 때 데이터베이스 트랜잭션과 관련된 실제 엔티티 매니저를 호출하는 것이다.
쉽게 말해 엔티티 매니저는 Spring Data JPA에서 관리하므로 우리가 직접 생성하거나 관리할 필요가 없다.
2-2. 영속성 컨텍스트란?
엔티티 매니저는 엔티티를 영속성 컨텍스트에 저장한다는 특성이 있다.
영속성 컨텍스트는 JPA의 중요한 특징 중 하나로, 엔티티를 관리하는 가상의 공간이다.
이를 통해서 데이터베이스에서 효과적으로 데이터를 가져올 수 있고, 엔티티를 편하게 사용할 수 있는 것이다.
영속성 컨텍스트의 특징은 다음과 같다.
1차 캐시
영속성 컨텍스트는 내부에 1차 캐시를 가지고 있다.
이때 캐시의 키는 엔티티의 @Id 애너테이션이 달린 기본키 역할을 하는 식별자이며 값은 엔티티이다.
엔티티를 조회하면 1차 캐시에서 데이터를 조회하고 값이 있으면 반환한다.
값이 없으면 데이터베이스에서 조회해 1차 캐시에 저장한 다음 반환한다.
이를 통해 캐시된 데이터를 조회할 때에는 데이터베이스를 거치지 않아도 되므로 매우 빠르게 데이터를 조회할 수 있다.
쓰기 지연
쓰기 지연은 트랜잭션을 커밋하기 전까지는 데이터베이스에 실제로 질의문을 보내지 않고
쿼리를 모았다가 트랜잭션을 커밋하면 모았던 쿼리를 한 번에 실행하는 것을 의미한다.
예를 들어 데이터 추가 쿼리가 3개라면, 영속성 컨텍스트는 트랜잭션을 커밋하는 시점에 3개의 쿼리를 한꺼번에 쿼리를 전송한다.
이를 통해 적당한 묶음으로 쿼리를 요청할 수 있어 데이터베이스 시스템의 부담을 줄일 수 있다.
변경 감지
트랜잭션을 커밋하면 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서 변경된 값이 있다면,
변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영한다.
이를 통해 쓰기 지연과 마찬가지로 적당한 묶음으로 쿼리를 요청할 수 있고,
데이터베이스 시스템의 부담을 줄일 수 있다.
지연 로딩 (<-> 즉시 로딩)
지연 로딩은 쿼리로 요청한 데이터를 애플리케이션에 바로 로딩하는 것이 아니라
필요할 때 쿼리를 날려 데이터를 조회하는 것을 의미한다.
이 특징들의 갖는 공통점은 모두 데이터베이스의 접근을 최소화해 성능을 높일 수 있다는 것이다.
캐시를 하거나, 자주 쓰지 않게 하거나, 변화를 자동 감지해서 미리 준비하거나 하는 등의 방법을 사용한다.
2-3. 엔티티의 상태
엔티티는 4가지 상태를 가진다
- 분리(detached) 상태
영속성 컨텍스트가 관리하고 있지 않는 상태 - 관리(managed) 상태
영속성 컨텍스트가 관리하는 상태 - 비영속(transient) 상태
영속성 컨텍스트와 전혀 관계가 없는 상태 - 삭제된(removed) 상태
이러한 상태는 특정 메서드를 호출해 변경할 수 있다
필요에 따라 엔티티의 상태를 조절해 데이터를 올바르게 유지하고 관리할 수 있다.
public class EntityManagerTest {
@Autowired
EntityManager em;
public void example() {
// ① 엔티티 매니저가 엔티티를 관리하지 않는 상태(비영속 상태)
Member member = new Member(1L, "홍길동");
// ② 엔티티가 관리되는 상태
em.persist(member);
// ③ 엔티티 객체가 분리된 상태
em.detach(member);
// ④ 엔티티 객체가 삭제된 상태
em.remove(member);
}
}
- 엔티티를 처음 만들면 엔티티는 비영속 상태가 된다
- persists() 메서드를 사용해 엔티티를 관리 상태로 만들 수 있으며,
Member 객체는 영속성 컨텍스트에서 상태가 관리된다 - 만약 영속성 컨텍스트에서 관리하고 싶지 않다면 detach() 메서드를 사용해 분리 상태로 만들 수 있다.
- 더 이상 객체가 필요 없다면 remove() 메서드를 사용해서 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제할 수 있다
3. 스프링 데이터와 스프링 데이터 JPA
지금까지 배운 개념에서는 엔티티의 상태를 직접 관리하고,
필요한 시점에 커밋을 해야 하는 등의 개발자가 신경 써야 하는 부분이 많다.
스프링 데이터(spring data):
비즈니스 로직에 더 집중할 수 있게 데이터베이스 사용 기능을 클래스 레벨에서 추상화함.
스프링 데이터에서 제공하는 인터페이스를 통해서 스프링 데이터를 사용할 수 있다
이 인터페이스에서는
- CRUD를 포함한 여러 메서드가 포함되어 있으며, 알아서 쿼리를 만들어준다.
- 페이징 처리 기능과 메서드 이름으로 자동으로 쿼리를 빌딩하는 기능이 제공되는 등 많은 장점이 있다.
- 각 데이터베이스의 특성에 맞춰 기능을 확장해 제공하는 기술도 제공한다.
3-1. 스프링 데이터 JPA란?
스프링 데이터 JPA는 스프링 데이터의 공통적인 기능에서 JPA의 유용한 기술이 추가된 기술이다.
스프링 데이터 JPA에서는 스프링 데이터의 인터페이스인 PagingAndSortingRepository를 상속받아
JpaRepository 인터페이스를 만들었으며, JPA를 더 편리하게 사용하는 메서드를 제공한다.
@PersistenceContext
EntityManager em;
public void join(){
// 기존의 엔티티 상태를 바꾸는 방법(메서드를 호출해서 상태 변경)
Member member = new Member(1L, "홍길동");
em.persist(member);
}
▲메서드 호출로 엔티티 상태 변경 예시
JPA를 사용하면 리포지터리 역할을 하는 인터페이스를 만들어,
CRUD 작업을 간단히 할 수 있다.
public interface MemberRepository extends JpaRepository<Member, Long>{
}
▲기본 CRUD 메서드를 사용하기 위한 JpaRepository 상속
3-2. JPA에서 제공하는 메서드 사용해보기
memberService.java 클래스파일을 생성한 후 다음과 같이 작성한다.
@Service
public class MemberService {
@Autowired
MemberRepository memberRepository;
public void test(){
// 1. 생성
memberRepository.save(new Member(1L, "A"));
// 2. 조회
Optional<Member> member = memberRepository.findById(1L); // 단건 조회
List<Member> allMembers = memberRepository.findAll(); // 전체 조회
// 3. 삭제
memberRepository.deleteById(1L);
}
}
우선 JpaRepository를 상속받은 MemberRepository를 빈으로 주입한 후
JPA에서 제공하는 CRUD를 사용해 볼 것이다.
- 생성 (Create)
save() 메서드를 호출해 데이터 객체를 저장할 수 있다.
전달 인수로 엔티티 Member를 넘기면 반환값으로 저장한 엔티티를 반환받을 수 있다. - 조회 (Read)
findById() 메서드에 id를 지정해 엔티티 하나를 조회할 수 있다 - 단건 조회
findAll() 메서드를 통해 전체 엔티티를 조회할 수 있다 - 전체 조회 - 삭제 (Delete)
deleteById() 메서드에 id를 지정하면 삭제할 수 있다
혹은 delete() 메서드를 호출해 엔티티를 전달 인수로 넘겨 삭제할 수도 있다.
4. 실습 (예제 코드 살펴보기)
1단계 - Member.java 애너테이션 설명
Member.java
@Getter
@Entity // ① 엔티티로 지정
@NoArgsConstructor(access = AccessLevel.PROTECTED) // ② 기본 생성자
@AllArgsConstructor
public class Member {
@Id // ③ id 필드를 기본키로 지정
@GeneratedValue(strategy = GenerationType.IDENTITY)
// ④ 기본키를 자동으로 1씩 증가
@Column(name = "id", updatable = false)
private Long id;
@Column(name = "name", nullable = false) // ⑤ name이라는 not null 컬럼과 매핑
private String name;
public void changeName(String name) {
this.name = name;
}
}
- @Entity
Member 객체를 JPA가 관리하는 엔티티로 지정한다.
즉, Member 클래스와 실제 데이터베이스의 테이블을 매핑시킨다.
@Entity의 속성 중 name을 사용하면 name의 값을 가진 테이블 이름과 매핑되고, name을 지정하지 않으면 클래스 이름과 같은 이름의 테이블과 매핑된다. - protected 기본 생성자
엔티티는 반드시 기본 생성자가 있어야 하고, 접근 제어자는 public 또는 protected여야 한다.
public보다는 protected가 더 안전하므로 접근 제어자가 protected인 기본 생성자를 생성한다. - @Id
Long 타입의 id 필드를 테이블의 기본키로 지정한다. - @GeneratedValue
기본키의 생성 방식을 결정
자동으로 기본키가 증가되도록 지정하였다.
AUTO 선택한 데이터베이스 방언에 따라 방식을 자동으로 선택 (기본값) IDENTITY 기본키 생성을 데이터베이스에 위임(= AUTO_INCREMENT) SEQUENCE 데이터베이스 시퀀스를 사용해서 기본키를 할당하는 방법
오라클에서 주로 사용TABLE 키 생성 테이블 사용 - @Column
데이터베이스의 컬럼과 필드를 매핑
name 필드와 매핑할 컬럼 이름.
설정하지 않으면 필드 이름으로 지정해 줌nullable 컬럼의 null 허용 여부
설정하지 않으면 true (nullable)unique 컬럼의 유일한 값 여부
설정하지 않으면 false(non unique)columnDefinition 컬럼 정보 설정
default값을 줄 수 있다.
2단계 -MemberRepository.java
MemberRepository.java
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByName(String name);
}
리포지토리는 엔티티에 있는 데이터들을 조회하거나 저장, 변경, 삭제를 할 때 사용하는 인터페이스이며,
스프링 데이터 JPA에서 제공하는 인터페이스인 JpaRepository 클래스를 상속받아 간단하게 구현할 수 있다.
JpaRepository 클래스를 상속받을 때, 엔티티 Member와 엔티티의 기본키 타입 Long을 인수로 넣어준다.
'Java & Kotlin > Spring' 카테고리의 다른 글
[스프링 부트 3] 블로그 만들기 - 2 (글 목록 조회 / 단일 조회 / 삭제 / 수정 API 구현) (0) | 2024.08.08 |
---|---|
[스프링 부트 3] 블로그 만들기 - 1 (블로그 글 작성 API 구현) (4) | 2024.08.07 |
[스프링 부트 3] 테스트 코드란? / 테스트 코드 작성하기 (0) | 2024.07.29 |
[스프링 부트 3] 구조 이해하기 (프레젠테이션, 비즈니스, 퍼시스턴스 계층) (0) | 2024.07.25 |
[스프링 부트 3] 초기 세팅 (IntelliJ) & Hello World 출력하기 (1) | 2024.07.24 |