jpa-1일차

박상원·2024년 5월 13일

spring

목록 보기
8/15

관계형 데이터베이스와 ORM

데이터베이스 정의

  • 데이터를 효율적으로 관리하기 위한 일종의 창고
  • 특정 조직의 여러 사용자가 데이터를 공유하여 사용할 수 있도록 통합 저장된 데이터의 집합
  • 행과 열로 구성된 시트에서 사용자가 정의한 형식으로 데이터를 관리하는 엑셀파일과 유사

관계형 데이터베이스

  • 1970년에 E.F.Codd가 제안한 데이터 관계형 모델에 기초하는 디지털 데이터베이스

관계형 모델

  • 데이터를 컬럼과 로우를 이루는 하나 이상의 테이블(또는 관계)로 정리
  • 고유 키가 각 로우를 식별
  • 로우는 레코드나 튜플로 부른다.
  • 관계는 서로 다른 테이블들 사이의 상호작용에 기반을 두고 형성된 논리적 연결이다.
    • 관계는 테이블 간에 둘 다 존재한다.
    • 이 관계들은 일대일, 일대다, 다대다 이렇게 세 가지 형태로 이루어진다.

Maven은 resources 하위의 모든 파일은 classpath로 들어간다.
component scan은 설정된 패키지의 하위 stereotype bean들을 스캔한다.

이때까지 했던 모든 것들은 다 관계형 데이터베이스

  • column
  • row
  • key, primary key, foreign key
  • relationship
  • transaction
  • SQL
  • MySQL, Oracle, ..

하지만 지금 내가 사용하고 있는 프로그래밍 언어는 Java이고 객체지향 프로그래밍 언어이다.

패러다임의 불일치 발생

  • 관계형 데이터베이스 != 객체 지향 프로그래밍 언어

H2 database

  • 자바 기반
  • 오픈소스
  • 관계형 데이터베이스

h2 데이터베이스 실행

java -jar h2/bin/h2-2.1.212.jar

JDBC 정의

  • JDBC(Java Database Connectivity)는 관계형 데이터베이스에 저장된 데이터를 접근 및 조작할 수 있게 하는 자바 API이다.
  • JDBC는 자바 응용프로그램이 다양한 DBMS에 대해 일관된 API로 데이터베이스 연결, 검색, 수정, 관리 등을 할 수 있게 한다.
  • 그러므로 자바 응용프로그램 개발자는 DBMS의 종류에 관계없이 JDBC API만을 이용하면 된다.

JDBC 구조

  • JDBC는 네트워크상에 있는 데이터베이스에 접속할 수 있도록 해주는 데이터베이스 연결기능을 제공한다.
  • JDBC API, JDBC Driver Manager로 구성되어 있다.

구성요소설명역할
Java Application자바 응용프로그램, 자바 웹 애플리케이션 서버(tomcat, weblogic 등)응용 프로그램 개발자, 웹 애플리케이션 서버 개발사
JDBC API자바 응용프로그램에서 데이터베이스를 연결하고 데이터를 제어할 수 있도록 데이터베이스 연결 및 제어를 위한 인터페이스와 클래스들JavaSE 개발사(Sun microsystems, Oracle)
JDBC Driver Manager자바 응용프로그램이 사용하는 데이터베이스에 맞는 JDBC 드라이버를 찾아서 로드한다.JavaSE 개발사(Sun microsystems, Oracle
JDBC Driver각 데이터베이스 개발사에서 만든 데이터베이스 드라이버데이터베이스 개발사(Oracle, MySql, PostgreSql,..

JDBC API

DataSource

  • DriverManager는 데이터베이스의 상세한 정보(호스트, 포트, 사용자이름, 패스워드)를 제공해 주어야 Connection을 받아올 수 있다.
  • DataSource는 Connection Pooling을 제공한다. 따라서 성능에 대한 확장성이 좋다.

Connection Pool

문제

  • Connection 객체는 새롭게 만들어질 때 많은 시스템 자원을 요구한다.

해결안

  • 여러개의 커넥션을 하나의 Pool에 모아 놓고 관리한다.
  • DB Connection을 미리 여러개 생성한 후, Pool에 담아 놓고 요청시 대여하는 형태로 사용한다.
  • 대여할 Connection이 부족하면 반납을 기다린 후 대여해 준다. 그동안 응용 시스템은 대기 상태가 된다.
  • 사용이 끝난 커넥션 객체는 반납을 반드시 해야 한다.

장점

  • 데이터베이스에 Connection을 생성할 때 소요되는 시간 및 자원을 줄일 수 있다.
  • Connection 수를 제한 할 수 있어 과다한 접속으로 인한 서버 자원 고갈을 예방한다.
  • 메모리 영역에서 Connection을 관리하기 때문에 클라이언트가 데이터베이스 작업을 빠르게 진행할 수 있다.

구현체 종류

  • Apache Commons DBCP
  • Tomcat DBCP
  • HikariCP

Java에서의 data access 기술

  • JDBC API
  • Spring Framework의 JdbcTemplate
  • MyBatis - SQL mapper framework
  • JPA (ORM)
  • ...

Spring Framework JdbcTemplate

  • JDBC API 기반
  • JdbcTemplate을 사용하면 지금까지 Jdbc를 사용한 코드에서 많은 부분을 Spring Framework가 대신 처리해 준다.
ActionSpring Framework개발자
Define connection parametersX
Open the connectionX
Specify the SQL statementX
Declare parameters and provide parameter valuesX
Set up the loop to iterate through the result (if any)X
Do the work for each iterationX
Process any exceptionX
Handle transactionsX
Close the connection, the statement, and the resultsetX

jpa는 구현체가 아니라 표준 스펙이다. 구현체는 Hibernate 등이 있다.
jpa는 자바 ORM 기술 표준이다.
servlet 스펙을 구현한 것이 tomcat이다.
Hibernate가 사실상 표준(defecto)이다.

기존에 SQL을 직접 작성한 것에서 데이터를 받는 클래스의 필드를 수정하면

SQL 직접 수정

  • 텍스트 수정이라 오타가 발생해도 런타임에서 확인할 수 있다.

객체와의 맵핑은 별개의 일

  • 쿼리 수행 결과와 객체와의 맵핑은 별도 수작업 필요
  • Repository의 CRUD 메서드와 SQL을 한꺼번에 같이 변경

그 외에도

  • 상속 구조의 표현
  • 연관관계 참조
  • 객체 그래프 탐색 등

ORM(Object-Relational Mapping)

  • ORM 프레임워크가 중간에서 객체와 관계형 데이터베이스를 맵핑
  • ORM을 이용하면 DBMS 벤더마다 다른 SQL에 대한 종속성을 줄이고 호환성 향상이 가능

JPA(Java Persistence API)

  • 자바 ORM 기술 표준
  • 표준 명세
    • JSR 338 - Java Persistence 2.2

JPA(Jakarta Persistence API)

  • Jakarta Persistence 3.1
  • 현재 3.2 개발중

JPA 구현

Hibernate

  • JPA 실제 구현
    • Hibernate, EclipseLink, DataNucleus
  • Hibernate가 사실상 표준

JPA를 사용해야 하는 이유

SQL 중심적인 개발 -> 객체 중심으로 개발

패러다임 불일치 해결

  • JPA는 객체와 관계형 데이터베이스 사이의 패러다임의 불일치로 인해 발생하는 문제(상속, 연관관계, 객체 그래프 탐색 등)를 해결

생산성

  • JPA를 사용하면 지루하고 반복적인 CRUD용 SQL을 개발자가 직접 작성하지 않아도 된다.
  • Spring Data JPA를 사용하면 interface 선언만으로도 쿼리 구현이 가능하기 때문에 관리 도구 등에서 가볍게 사용할 수 있는 CRUD 쿼리를 손쉽게 대처할 수 있다.

maintenance

  • 컬럼 추가/삭제 시 직접 관련된 CRUD 쿼리를 모두 수정하는 대신 JPA가 관리하는 모델(Entity)을 수정하면 된다.

데이터 접근 추상화와 벤더 독립성

  • 데이터베이스 벤더마다 미묘하게 다른 데이터 타입이나 SQL을 JPA를 이용하면 손쉽게 해결이 가능하다.

Java EE

  • Java 언어 플랫폼 중의 하나
  • 대용량, 멀티 티어의 엔터프라이즈 애플리케이션을 실행하고 운영할 수 있는 기술과 환경을 제공
  • 특정 운영체제와 미들웨어에 종속되지 않고 정보 교환 및 애플리케이션 호환이 가능한 플랫폼을 제공하는 것이 목적

Java 언어 플랫폼

  • Java 언어로 작성된 프로그램이 실행되는 특정한 환경
  • JDK 1.2부터 시작

Java 언어 플랫폼의 종류

Java SE(Standard Edition)

  • Java 2 Platform, Standard Edition 줄여서 J2SE라고 불렸음
  • 일반적인 응용 프로그램 개발 용도

Java EE(Enterprise Edition)

  • Java 2 Platform, Enterprise Edition 줄여서 J2EE라고 불렸음
  • Java SE를 확장하여 분산 컴퓨팅, 웹서비스와 같은 엔터프라이즈 환경을 지원

Java ME(Micro Edition)

  • Java 2 Platform, Enterprise Edition 줄여서 J2EE라고 불렸음
  • 임베디드 시스템이나 모바일 디바이스를 위한 개발 환경을 지원

JavaFx

  • 데스크톱 애플리케이션 및 리치 웹 애플리케이션 개발 환경을 지원
  • Fx = special effects

Jakarta EE

  • 오라클이 2017년 Java EE 8 릴리즈를 마지막으로
  • 오픈소스 SW를 지원하는 비영리 단체인 Eclipse 재단에 Java EE 프로젝트를 이관

Spring Framework과 JPA

Spring Data

  • 다양한 데이터 저장소에 대한 접근을 추상화하기 위한 Spring 프로젝트
  • JPA, JDBC, Redis, MongoDB, Elasticsearch 등

Spring Data JPA

  • repository 추상화를 통해 interface 선언만으로도 구현 가능
  • 메서드 이름으로 쿼리 생성
  • Web Support (페이징, 정렬, 도메인 클래스 컨버터 기능)

Transaction

  • 트랜잭션(Transaction)은 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위해 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.

Transaction 속성

트랜젝션은 작업의 안정성과 데이터의 무결성을 유지시키기 위해 다음의 4가지 성질을 가지고 있다.

  • Atomicity(원자성)
  • Consistency(일관성)
  • Isolation(고립성)
  • Durability(지속성)

원자성

  • 트랜잭션 수행 후 데이터베이스에 전부 반영되거나, 전부 반영되지 않아야 한다.
  • 트랜잭션 작업 중 문제가 생기면 전체 작업을 취소 (ROLLBACK) 하는 과정을 거쳐야 한다.

일관성

  • 트랜잭션 수행 후 데이터 모델의 모든 제약조건을 만족해야 한다.

고립성

  • 트랜잭션 수행 시 다른 트랜잭션이 영향을 미치지 않아야 한다.

영속성

  • 트랜잭션의 성공결과는 장애 발생 후에도 변함없이 보관되어야 한다.
  • 트랜잭션이 작업을 정상적으로 완료한 경우에는 디스크(데이터베이스)에 확실히 기록되어야 하며, 부분적으로 수행된 경우에는 작업을 취소해야 한다.
  • 즉, 정상적으로 완료 혹은 부분완료된 데이터는 DBMS가 책임지고 데이터베이스에 기록하는 성질을 트랜잭션의 Durability(영속성)이라고 한다.

Spring Framework의 트랜잭션 추상화

PlatformTransactionManager

  • Spring Framework 트랜잭션 추상화의 핵심 interface
public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) /*..*/;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}

선언적 트랜잭션

@Transaction

Transaction은 보통 service layer에서 묶는다.
jpa를 사용하기 위해서는 jpa dependency와 hibernate dependency가 필요하고 JpaConfig를 만들어서 @EnableJpaRepositories를 사용해야 한다.
내가 직접 쿼리 안 짜고 orm이 짜게 하는 것이 jpa이다.

jpa를 사용하기 위해서는 JpaConfig에 EntityManagerFactoryBean과 TransactionManager를 Bean으로 등록해줘야 한다.

Bean Configuration

LocalContainerEntityManagerFactoryBean Bean Configuration

@Bean 
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
    LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
    emf.setDataSource(dataSource);
    emf.setPackagesToScan("com.nhnacademy.springjpa.entity");
    emf.setJpaVendorAdapter(jpaVendorAdapters());
    emf.setJpaProperties(jpaProperties());

    return emf;
}

private JpaVendorAdapter jpaVendorAdapters() {
    HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
    hibernateJpaVendorAdapter.setDatabase(Database.H2);

    return hibernateJpaVendorAdapter;
}

private Properties jpaProperties() {
    Properties jpaProperties = new Properties();
    jpaProperties.setProperty("hibernate.show_sql", "true");
    jpaProperties.setProperty("hibernate.format_sql", "true");
    jpaProperties.setProperty("hibernate.use_sql_comments", "true");
    jpaProperties.setProperty("hibernate.globally_quoted_identifiers", "true");
    jpaProperties.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");

    return jpaProperties;
}

Transaction Manager

  • DataSourceTransactionManager vs JpaTransactionManager
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory);

    return transactionManager;
}

EntityManager

  • 엔티티의 저장, 수정, 삭제, 조회 등 엔티티와 관련된 모든 일을 처리하는 관리자

EntityManagerFactory

  • Entitymanager를 생성하는 팩토리

JPA/Hibernate Logging

SQL

  • JPA properties
hibernate.show_sql=true
hibernate.format_sql=true
  • logback logger
<logger name="org.hibernate.SQL" level="debug" additivity="false">
    <appender-ref ref="console" />
</logger>
  • binding parameters
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="trace" additivity="false">
    <appender-ref ref="console" />
</logger>

Entity 맵핑

Entity란

  • JPA를 이용해서 데이터베이스를 테이블과 맵핑할 클래스

Entity 맵핑

  • Entity 클래스에 데이터베이스 테이블과 컬럼, 기본 키, 외래 키 등을 설정하는 것

Entity와 database table이 매핑되는 것이 entity mapping이다.
Entity는 database table과 매핑되는 자바 객체이다.
db table 이름과 Entity 객체 이름이 다를 경우 @Table로 어떤 테이블인지 지정을 해줘야 한다.
table의 column은 자바 class의 field로 매핑이 된다.

어노테이션

  • @Entity : JPA가 관리할 객체임을 명시
  • @Table : 맵핑할 DB 테이블 명 지정(테이블 이름과 class이름이 같을 시 생략 가능)
  • @Id : 기본 키 맵핑
  • @Column : 필드와 컬럼 맵핑(컬럼 이름이 같을 시 생략 가능)
@Entity
@Table(name = "Members")
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Column(name = "created_dt")
    private LocalDateTime createdDate;

}

java8 이전 버전에는 날짜 타입을 맵핑하는 어노테이션이 존재함

@Temporal

  • 날짜 타입 맵핑
public enum TemporalType {
    DATE, 
    TIME, 
    TIMESTAMP
}

@Transient

  • 특정 필드를 컬럼에 맵핑하지 않을 경우에 지정

기본 키(Primary Key) 맵핑 전략

자동 생성

  • TABLE 전략: 이 전략은 채번 테이블을 사용하여 기본 키를 생성하는 방법입니다. 채번 테이블은 데이터베이스에 미리 생성된 특별한 테이블로, 기본 키 값들이 저장됩니다. 어플리케이션은 이 테이블에서 기본 키 값을 가져와 사용하고, 가져온 값은 테이블에서 삭제되거나 마킹됩니다. 이 방법은 여러 어플리케이션이 동시에 사용될 때 충돌을 방지할 수 있습니다.

  • SEQUENCE 전략: 시퀀스 전략은 데이터베이스에 시퀀스라는 객체를 사용하여 기본 키를 생성하는 방법입니다. 시퀀스는 일련번호를 자동으로 생성해주는 데이터베이스 객체로, 각 기본 키 값은 시퀀스에서 생성됩니다. 이 방법은 다양한 데이터베이스에서 지원되며, 성능면에서 우수합니다.

  • IDENTITY 전략: 이 전략은 기본 키 생성을 데이터베이스에 위임하는 방법입니다. 대부분의 데이터베이스에서는 자동 증가(auto-increment) 혹은 identity 컬럼을 지원하며, 이를 사용하여 각 레코드가 삽입될 때마다 기본 키 값을 자동으로 생성합니다.

  • AUTO 전략: AUTO 전략은 선택한 데이터베이스 방언(dialect)에 따라 기본 키 맵핑 전략을 자동으로 선택하는 방법입니다. Hibernate에서는 해당 데이터베이스의 특성을 파악하여 가장 적합한 전략을 자동으로 선택합니다.

직접 할당

  • 애플리케이션에서 직접 식별자 값을 할당

복합 Key(Composite key)

  • @IdClass
  • @EmbeddedId / @Embeddable

@IdClass

  • Entity class 레벨에서 지정
@Entity
@Table(name = "OrderItems")
@IdClass(OrderItem.Pk.class)
public class OrderItem {
    @Id
    @Column(name = "order_id")
    private Long orderId;

    @Id
    @Column(name = "line_number")
    private Integer lineNumber;

    @NoArgsConstructor
	@AllArgsConstructor
	@EqualsAndHashCode
	public static class Pk implements Serializable {
    	private Long orderId;

    	private Integer lineNumber;

	}
}

@EmbeddedId / @Embeddable

  • @EmbeddedId - Entity 클래스의 필드에 지정
  • @Embeddable - 복합 Key 식별자 클래스에 지정
@Entity
@Table(name = "OrderItems")
public class OrderItem {
    @EmbeddedId
    private Pk pk;

    @NoArgsConstructor
	@AllArgsConstructor
	@EqualsAndHashCode
	@Embeddable
	public static class Pk implements Serializable {
	    @Column(name = "order_id")
	    private Long orderId;

	    @Column(name = "line_number")
	    private Integer lineNumber;
	}
}

복합 Key Class 제약조건

  • 기본 키 클래스는 반드시 public 이어야 하고 기본 생성자를 가져야 한다.
  • 기본 키 클래스는 반드시 serializable을 구현해야 한다.
  • 기본 키 클래스는 반드시 equals 그리고 hashCode 메서드를 정의해야 한다.

EntityManager / EntityManagerFactory 다시 돌아보기


EntityManagerFactory

  • EntityManager를 생성하는 팩토리
  • 데이터베이스를 하나만 사용하는 애플리케이션은 일반적으로 EntityManager를 하나만 사용
    • EntityManagerFactory를 만드는 비용이 매우 크기 때문에 하나만 만들어서 전체에서 공유
    • thread safe

EntityManager

  • Entity의 저장, 수정, 삭제, 조회 등 Entity와 관련된 모든 일을 처리하는 관리자
  • EntityManagerFactory가 생성 -> 생성 비용이 크지 않다.
  • EntityManager는 thread safe하지 않음
    • 여러 thread 간에 절대 공유하면 안됨
  • 각각의 요청마다 별도의 EntityManager를 생성해서 사용

GMT : Greenwich mean time
GMT+09:00 여기서 뒤에 있는 시간이 offset이다.

영속성 컨텍스트


  • Entity를 영구 저장하는 환경
@PersistenceContext

Entity의 생명주기

비영속(new/transient)

  • 영속성 컨텍스트와 전혀 관계 없는 상태

영속(managed)

  • 영속성 컨텍스트에 저장된 상태

준영속(detached)

  • 영속성 컨텍스트에 저장되었다가 분리된 상태

삭제(removed)

  • 삭제된 상태

영속성 컨텍스트가 Entity를 관리하면 얻을 수 있는 이점

  • 1차 캐시
  • 동일성 보장
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지
  • 지연 로딩

entityManager가 관리하는 entity가 db에 써지기 위해서는 flush() 메서드를 실행해 주어야 한다.

0개의 댓글