20230629 공부노트

라영진·2023년 6월 29일
0

Java 학습일지

목록 보기
29/35

스프링부트 2장

JPA란

-스프링부트는 JPA(Java Persistence API)를 사용하여 데이터베이스를 처리한다.
JPA는 자바 진영에서 ORM(Object-Relational Mapping)의 기술 표준으로 사용하는 인터페이스의 모음이다.

※JPA는 인터페이스이다. 따라서 인터페이스를 구현하는 실제 클래스가 필요하다. JPAc를 구현한 대표적인 실제 클래스에는 하이버네이트(Hibernate)가 있다. SBB도 JPA + 하이버네이트 조합을 사용한다.

H2 데이터베이스

-개발용이나 소규모 프로젝트에서 사용되는 파일 기반의 경량 데이터베이스

※runtimeOnly
build.gradle 파일의 runtimeOnly는 해당 라이브러리가 런타임시에만 필요한 경우에 사용한다.
컴파일시에만 필요한 경우에는 runtimeOnly 대신 compileOnly를 사용한다.

application.properties 파일 수정

DATABASE

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/local
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

각 항목에 대해 알아보자

-spring.h2.console.enabled- H2 콘솔의 접속을 허용할지의 여부이다.true로 설정한다.

-spring.h2.console.path - 콘솔 접속을 위한 URL 경로이다.

-spring.datasource.url- 데이터베이스 접속을 위한 경로이다.

-spring.datasource.driverClassName - 데이터베이스 접속시 사용하는 드라이버이다.

-spring.datasource.username - 데이터베이스의 사용자명이다.(사용자명은 기본 값인 sa로 설정한다.)

-spring.datasource.password- 데이터베이스의 패스워드이다.
로컬 개발 용도로만 사용하기 때문에 패스워드를 설정하지 않았다.

spring.datasource.url 경로에 해당하는 데이터베이스 파일을 만들어야한다. jdbc:h2:~/local로 설정했기 떄문에 해당 경로에 파일을 만들어줘야 한다.

JPA 환경설정

※ implementation
-build.gradle 파일의 implementation은 해당 라이브러리 설치를 위해 일반적으로 사용하는 설정이다. implementation은 해당 라이브러리가 변경되더라도 이 라이브러리와 연관된 모든 모듈들을 컴파일하지 않고 직접 관련이 있는 모듈들만 컴파일하기 때문에 rebuild 속도가 빠르다.

-spring.jpa.properties.hibernate.dialect
데이터베이스 엔진 종류를 설정한다.

-spring.hibernate.ddl-auto
엔티티를 기준으로 테이블을 생성하는 규칙을 정의한다.

※spring.jpa.hibernate.ddl-auto

  • 위 설정에서 spring.jpa.hibernate.ddl-auto를 update로 설정했다. update와 같은 설정값에 대해서 간단히 알아보자.

    -none - 엔티티가 변경되더라도 데이터베이스를 변경하지 않는다.
    -update - 엔티티의 변경된 부분만 적용한다.

    -validate - 변경사항이 있는지 검사만 한다.

    -create - 스프링부트 서버가 시작될때 모두 drop하고 다시 생성한다.

    -create-drop -create와 동일하다. 하지만 종료시에도 모두 drop한다.

    개발환경에서는 보통 update 모드를 사용하고 운영환경에서는 none 또는 validate 모드를 사용한다.

엔티티

엔티티는 데이터베이스 테이블과 매핑되는 자바 클래스를 말한다.
SBB는 질문과 답변을 할 수 있는 게시판 서비스이다.
따라서 SBB는 질문과 답변에 해당하는 엔티티가 있어야한다.

엔티티는 모델 또는 도메인 모델이라고 부르기도 한다.
이 책에서는 이것들을 구분하지 않고 테이블과 매핑되는 클래스를 엔티티라 지칭하겠다.

질문(Question) 엔티티의 속성

1.id = 질문의 고유 번호
2.subject = 질문의 제목
3.content = 질문의 내용
4.create_date = 질문을 작성한 일시

답변(Answer) 엔티티의 속성

1.id = 답변의 고유 번호
2.question = 질문(어떤 질문의 답변인지 알아야하므로 질문 속성이 필요하다.)
3.content = 답변의 내용
4.create_date = 답변을 작성한 일시

질문 엔티티 작성하기

@Id

고유 번호 id 속성에 적용한 @Id 애너테이션은 id속성을 기본 키로 지정한다.
기본 키로 지정하면 이제 id속성의 값은 데이터베이스에 저장할 때 동일한 값으로 저장할 수 있다. 고유 번호를 기본 키로 한 이유는 고유 번호는 엔티티에서 각 데이터를 구분하는 윻한 값으로 중복되면 안 되기 때문이다.

※데이터베이스에서는 id와 같은 특징을 가진 속성을 기본 키(primary key)라고 한다.

@GeneratedValue

@GeneratedValue 애너테이션을 적용하면 데이터를 저장할 때 해당 속성에 값을 따로 세팅하지 않아도 1씩 자동으로 증가하여 저장된다. strategy는 고유번호를 생성하는 옵션으로 GenerationType.IDENTITY는 해당 컬럼만의 독립적인 시퀀스를 생성하여 번호를 증가시킬 때 사용한다.

※strategy 옵션을 생략할 경우에 @GeneratedValue 애너테이션이 지정된 컬럼들이 모두 동일한 시퀀스로 번호를 생성하기 때문에 일정한 순서의 고유번호를 가질수 없게 된다. 이러한 이유로 보통 GenerationType.IDENTITY를 많이 사용한다.

@Column

엔티티의 속성은 테이블의 컬럼명과 일치하는데 컬럼의 세부 설정을 위해 @Column 애너테이션을 사용한다. length는 컬럼의 길이를 설정할때 사용하고 columnDefinition은 컬럼의 속성을 정의할 떄 사용한다. columnDefinition = "TEXT"은 "내용"처럼 글자 수를 제한할 수 없는 경우에 사용한다.

※엔티티의 속성은 @Column 애너테이션을 사용하지 않더라도 테이블 컬럼으로 인식한다. 테이블 컬럼으로 인식하고 싶지 않을 경우에만 @Transient 애너테이션을 사용한다.

*테이블의 컬럼명
위의 Question 엔티티에서 작성일시에 해당하는 createDate 속성의 실제 테이블의 컬럼명은 createdate가 된다.
즉, createDate처럼 대소문자 형태의 카멜케이스 이름은 create_date처럼 모두 소문자로 변경되고 언더바(
)로 단어가 구분되어 실제 테이블 컬럼명이 된다.

*엔티티와 Setter
일반적으로 엔티티에는 Setter 메서드를 구현하지 않고 사용하기를 권한다.
왜냐하면 엔티티는 데이터베이스와 바로 연결되어 있으므로 데이터를 자유롭게 변경할 수 있는 Setter 메서드를 허용하는 것이 안전하지 않다고 판단하기 때문이다.

그렇다면 Setter 메서드 없이 어떻게 엔티티에 값을 저장할 수 있을까?

엔티티를 생성할 경우에는 롬복의 @Builder 어노테이션을 통한 빌드패턴을 사용하고, 데이터를 변경해야 할 경우에는 그에 해당되는 메서드를 엔티티에 추가하여 데이터를 변경하면 된다.

답변 엔티티 생성하기

답변은 하나의 질문에 여러개가 달릴 수 있는 구조이다.
따라서 답변은 Many(많은 것)가 되고 질문은 One(하나)이 된다.
따라서 @ManyToOne은 N:1 관계라고 할 수 있다.
이렇게 @ManyToOne 애너테이션을 설정하면 Answer 엔티티의 question 속성과 Question 엔티티가 서로 연결된다.(실제 데이터베이스에서는 ForeignKey 관계가 생성된다.)

※ManyToOne은 부모 자식 관계를 갖는 구조에서 사용한다.
여기서 부모는 Question, 자식은 Answer라고 할 수 있다.

그렇다면 반대방향, 즉 Quesiton 엔티티에서 Answer 엔티티를 참조하려면
1:N 관계이기에 @ManyToOne이 아닌 @OneToMany 애너테이션을 사용해야한다.
Question 하나에 Answer는 여러개이므로 Question 엔티티에 추가할 답변의 속성은 List 형태로 구성해야 한다.

Answer 엔티티 객체로 구성된 answerList를 속성으로 추가하고 @OneToMany 애너테이션을 설정했다. 이제 질문 객체(ex.question)에서 답변을 참조하려면 question.getAnswerList() 를 호출하면 된다.
@OneToMany 애너테이션에 사용된 mappedBy는 참조 엔티티의 속성명을 의미한다.
즉, Answer 엔티티에서 Question 엔티티를 참조한 속성명 question을 mappedBy에 전달해야 한다.

※Cascade Type.REMOVE
질문 하나에는 여러개의 답변이 작성될 수 있다.
이 때 질문을 삭제하면 그에 달린 답변들도 모두 함께 삭제하기 위해서 @OneToMany 의 속성으로 cascade = CascadeType.REMOVE 를 사용했다.

테이블 확인하기

H2콘솔에 접속하여 Question,Answer 엔티티가 정상적으로 생성되었는지 확인

리포지터리

엔티티만으로는 데이터베이스에 데이터를 저장하거나 조회 할 수 없다.
데이터 처리를 위해서는 실제 데이터베이스와 연동하는 JPA 리포지터리가 필요하다.

※리포지터리란?
리포지터리는 엔티티에 의해 생성된 데이터베이스 테이블에 접근하는 메서드들(ex.findAll,save 등)을 사용하기 위한 인터페이스이다.
데이터 처리를 위해서는 테이블에 어떤 값을 넣거나 값을 조회하는 등의 
CRUD(Create, Read, Update, Delete)가 필요하다.
이 때 이러한 CRUD를 어떻게 처리할지 정의하는 계층이 바로 리포지터리이다.

QuestionRepository 인터페이스 생성이유
QuestionRepository는 리포지터리로 만들기 위해 JpaRepository 인터페이스를 상속했다. JpaRepository를 상속할 때는 제네릭스 타입으로 <Question, Integer> 처럼 리포지터리의 대상이 되는 엔티티의 타입(Question)과 해당 엔티티의 PK의 속성 타입(Integer)을 지정해야 한다.
이것은 JpaRepository를 생성하기 위한 규칙이다.

※Question 엔티티의 PK(Primary Key)속성인 id의 타입은 Integer이다.

QuestionRepository와 AnswerRepository를 이용하여 question,answer 테이블에 데이터를 저장하거나 조회할 수 있다.

@SpringBootTest 애너테이션은 ApplicationTests 클래스가 스프링부트 테스트 클래스임을 의미한다.
그리고 @Autowired 애너테이션은 스프링의 DI 기능으로 questionRepository 객체를 스프링이 자동으로 생성해 준다.

※DI(Dependency Injection)- 스프링이 객체를 대신 생성하여 주입한다.

*Autowired
객체를 주입하기 위해 사용하는 스프링의 애너테이션이다. 객체를 주입하는 방식에는 @Autowired 외에 Setter 또는 생성자를 사용하는 방식이 있다.
순환참조 문제와 같은 이유로 @Autowired 보다는 생성자를 통한 객체 주입방식이 권장된다. 하지만 테스트 코드의 경우에는 생성자를 통한 객체의 주입이 불가능하므로 테스트 코드 작성시에만 @Autowired를 사용하고 실제 코드 작성시에는 생성자를 통한 객체 주입방식을 사용하겠다.

@Test 애너테이션은 testJpa 메서드가 테스트 메서드임을 나타낸다.

※JUnit은 테스트코드를 작성하고 작성한 테스트코드를 실행하기 위해 사용하는 자바의 테스트 프레임워크이다.

※테스트를 하기 위해서는 로컬서버를 중지해야 한다

findAll은 데이터를 조회할때 사용하는 메서드이다.

데이터 사이즈가 2인지 확인하기 위해 JUnit의 assertEqulas 메서드를 사용했다.
assertEqulas는 assertEquals(기대값, 실제값)와 같이 사용하고 기대값과 실제값이 동일한지를 조사한다.
만약 기대값과 실제값이 동일하지 않다면 테스트는 실패로 처리된다.
그리고 우리가 저장한 첫번째 데이터의 제목이 "sbb가 무엇인가요?"와 일치하는지도 테스트했다.

※우리는 편의상 testJpa 메서드 하나만을 가지고 JPA의 여러기능을 테스트할 것이다.

findById

findBySubject

findBySubjectAndContent

findBySubjectLike

데이터수정하기

데이터 삭제하기

답변 데이터 생성 후 저장하기

답변 조회하기

답변에 연결된 질문 찾기 vs 질문에 달린 답변 찾기

profile
보더콜리 2마리 키우는 개발자

0개의 댓글