본문 바로가기

끄적끄적/Spring Boot

Spring Boot 강의 필기

Spring Boot + JPA_웹 애플리케이션 개발

스프링부트 starter를 이용하여 설치 진행

(https://start.spring.io/)

현재 maven에서 gradel로 넘어가는 추세이므로 gradle 사용

 

vscode

  • Java Extension Pack 설치
  • Spring boot Extension Pack 설치
  • Lombok Annotations Support for VS Code 설치
  •  

Dependancy

  • web
  • thymeleaf
  • JPA
  • 데이터베이스 h2
  • Lombok
  • JUnit4 사용

testImplementation("org.junit.vintage:junit-vintage-engine") {

exclude group: "org.hamcrest", module: "hamcrest-core"

}

  • 입력하지 않을 경우 JUnit5로 실행

 

Import

  • vscode 실행 후 다운받은 스프링 부터 스타터 디렉토리 이동
  • main에 있는 java 실행 후 localhost:8080 접근 가능한지 확인
  • test.java 실행 후 문제 있는지 확인

 

lombok&test

  • main에 Hello.java 생성
  • test/java/*Application.java



라이브러리

gradle w dependencies : 의존 관계 리스트 출력

 = VS code > Gradle > Dependencies > compileClasspath에 위치

 

Tymeleaf

  • 스프링에서 호환이 잘되는 라이브러리
  • jsp와 다르게 기본 마크업 문법을 따름



Controller

public class HelloController {   

   @GetMapping("hello")

   public String hello(Model model) {

       model.addAttribute ("data", "hello!!!!!");

       return "hello";

   }

}

 

model에 Key와 Value를 담아 view에 넘김

  • return “hello” : resource > templates > hello.html 파일로 데이터 전송

 

정적 파일은 resource > static에 위치

랜더링 파일은 resource > template에 위치



수정한 파일을 바로 재시작하는 방법

  • build.gradle > dependencies에 추가

implementation 'org.springframework.boot:spring-boot-devtools'

 

  • application.properties에 옵션 추가

spring.devtools.livereload.enabled=true

spring.freemarker.cache=false

spring.thymeleaf.cache=false

또는

  • application.yml에 옵션 추가

spring:

 devtools:

   livereload:

     enabled: true

 freemarker:

   cache: false

 thymeleaf:

   cache: false



  • 재시작 > 파일 수정 후 브라우저 새로고침




H2데이터베이스 설치

특징

  • 개발 & 테스트 용도로 가벼운 DB
  • 웹 환경, 웹 콘솔 환경 제공

 

환경 설정

  • localhost로 변경후 웹 주소 이동
  • url의 key값 유지
  • JDBC URL은 프로젝트가 있는 루트까지의 위치
  • 이후 JDBC URL 접속 시 jdbc:h2:tcp://localhost/FILE_PATH/jpashop 으로 접속

 

JPA와 DB 설정, 동작확인

  • application.yml 옵션 추가

spring: 

    datasource:

        url: jdbc:h2:tcp://localhost/Documents/basic_java/study_spring_jpa/jpasho;MVCC=TRUE

        username: sa

        password:

        driver-class-name: org.h2.Driver

    jpa:

        hibernate:

            ddl-auto: create

        properties:

            hibernate:

                # show_sql: true

                format_sql: true

logging:

    level:

        org.hibernate.SQL: debug

        # SQL문의 파라미터 값을 알 수 있음

        org.hibernate.type: trace



MVCC=TRUE

  • 접근 할 때 더 빨리 처리

driver-class-name

  • 데이터베이스 커넥션과 관련된 데이터 소스 설정 완료
  • 위와 같이 설정하면 히카리 cpu를 사용하여 커넥션 풀이 스프링 부터에서 세팅을 완료

ddl-auto: create

  • 자동으로 탭을 생성하는 모드

properties

  • hibernate와 관련된 property 사용 가능

show_sql, format_sql

  • 스프링 부트 매뉴얼에서 공부 가능
  • 스프링 부트 > learn > 버전별 Ref.Doc

org.hibernate.SQL: debug

  • hibernate에서 생성하는 SQL의 내용 모두 로깅으로 출력
  • hibernate: show_sql의 차이점은 해당 내용은 시스템 아웃으로 출력



Repository

  • 엔티티를 찾아주는 공간(=DAO)
  • 해당 어노테이션은 Spring이 제공하는 기본 타입으로 컴포넌트 스캔의 대상이 되어 자동으로 스프링 빈에 등록되는 내용

 

@PersistenceContext

    private EntityManager em;

  • JPA를 사용하기 때문에 엔티티 매니저 필요
  • 스프링 컨테이너에서 사용
  • 스프링 부트가 @PersistenceContext 어노테이션을 읽어 주입을 해줌

 

김영한 팁

  • 커맨드와 쿼리를 분류

 

Test코드

  • 테스트 파일 생성(test 디렉토리 아래에 main에서 테스트 할 파일의 같은 위치)

@Autowired MemberRepository memberRepository;

  • MemberRepository 내용 테스트를 위해 Autowired로 멤버 MemberRepository 인젝션
  • 웬뎅 스타일
  • Assertions.assertThat(찾을 내용의 아이디).isEqualTo(생성한 아이디)
  • 엔티티 매니저를 통한 모든 데이터 변경은 항상 트랜잭션 안에서 이루어져야 함

 

Entity Class

  • 실무에서는 가급적 Getter는 열어두고, Setter는 필요한 경우에만 사용

 

@GeneratedValue : 시퀀스 값 사용

@Column(name = "DB 테이블의 사용할 컬럼명")

  • name을 사용 안할 경우 class 내부의 필드에 선언한 이름 그대로 사용

@Embeddable : JPA의 어떤 내장 타입이라는 것을 알려주기 위해 Embeddable 처리

  • 어디에서든 내장이 될 수 있도록 도움

@Embedded : 임베디드 내장 타입을 포함했다는 표시

  • Embeddable나 Embedded 둘 중 하나만 사용해도 되지만 그냥 두 개 다 적어도 무방

@Table(name = "DB 테이블에 사용할 테이블명")

  • name을 사용 안할 경우 class에 선언한 이름 그대로 사용

@JoinColumn(name = "맵핑할 대상 테이블의 컬럼명(id)")

@OneToMany(mappedBy = "맵핑 대상자 테이블의 컬럼명(필드)")

  • 양방향 연관 관계에서 주인을 정해 주어야 함
  • 연관 관계에서 ‘을'일 경우 mappedBy의 누구에 의해 맵핑이 되었는지 작성
  • 해당 annotation은 내가 매핑을 하는게 아닌, 위 대상자에 의해 매핑된 대상자라 표현(읽기 전용)

 

추상 클래스

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

  • 상속관계의 전략을 지정할 때 부모 클래스에서 지정
  • strategy에서 전략 방식을 지정

@DiscriminatorColumn(name = "dtype")

  • 읽을 대상(자식 클래스)를 읽을 때 어떻게 읽을 것인지

@DiscriminatorValue(value = "A")

  • 싱글 테이블에서 저장해둘 때 구분할 수 있도록 값을 지정
  • 기본 값은 class명

@Enumerated(EnumType.타입 선택)

  • ORDINAL : 숫자 형태
  • STRING : 문자열 형태

 

 

엔티티 관계가 다대 다일 경우

@ManyToMany

엔티티와 엔티티 중간 테이블을 만들어 매핑이 필요

@JoinTable(name = "맵핑할 대상 테이블명", 

        joinColumns = @JoinColumn(name = "맵핑할 대상 테이블의 컬럼명"), 

        inverseJoinColumns = @JoinColumn(name = "item_id")

    )

  • inverseJoinColumns : 다대 다의 중간 테이블을 만들 때 상대쪽 엔티티의 매핑 컬럼

 

한쪽에서 위와 같은 작업을 끝낸 후 상대쪽 class에서는 아래와 같이 구성

@ManyToMany(mappedBy=”맵핑 대상자 테이블의 컬럼명(필드)”)

 

계층 트리

 @ManyToOne

 @JoinColumn(name = "parent_id")

    private Category parent;

 

 @OneToMany(mappedBy = "parent")

    private List<Category> child = new ArrayList<>();



엔티티 설계시 주의

가급적 Setter 사용 x

  • 변경 포인트가 많으면 유지보수에 어려움 발생. 리팩토링을 통해서라도 꼭 제거가 필요

모든 연관관계는 지연로딩(LAZY)으로 설정(중요)

즉시 로딩: 테이블을 조회할 때 연관된 내용을 한번에 조회

  • 예측이 어렵다
  • 어떤 SQL이 실행될지 추적이 어렵다
  • 특히 JPQL을 실행할 때 N + 1문제가 자주 발생

 

연관된 엔티티를 함께 DB에서 조회

  • fetch join 또는 엔티티 그래프 기능을 사용

 

@XToOne(OneToOne, ManyToOne) 관계는 기본이 즉시로딩이므로 직접 지연로딩으로 설정

 

컬렉션은 필드에서 초기화

애플리케이션 구현

Repository

@Repository : 컴포넌트 스캔에 의해서 자동으로 Spring Bean으로 관리

@PersistenceContext : 스프링이 EntityManager를 만들어 주입

public void save(Member member) {

       em.persist(member);

   }

  • persist를 하면 영속성 컨텍스트에 member Entity를 넣음
  • 트랜잭션 commit되는 시점에 DB에 반영

 

JPQL

return em.createQuery("select m from Member m", Member.class)

               .getResultList();

 

  • SQL이랑 거의 똑같지만 JOIN 이랑 문법이 약간의 차이가 있다. JPQL은 결국 SQL로 번역됨
  • SQL은 테이블을 대상으로 쿼리를 하지만, JPQL은 Entity 객체를 대상으로 쿼리
  • 기본편 참고



Service

@Service : 컴포넌트 스캔에 의해서 자동으로 Spring Bean으로 관리

@Autowird 

  • 필드에 선언한 repository를 Injection
  • 생성자가 하나일 경우 생략 가능
  • Repository의 EntityManager는 PersistenceContext로만 인젝션이 가능했으나, 스프링부트 이후에는 해당 어노테이션을 사용하여 Repository에서 인젝션을 지원

@Transactional

  • JPA의 모든 데이터 변경이나 로직들은 가급적 트랜잭션 안에서 실행되어야 함
  • LAZY 로딩이 이루어짐

종류

  • JavaX
  • Spring

옵션

  • readOnly = true
  • 옵션은 해당 클래스에서 가장 많이 사용되는 내용을 전체로 묶어서 사용 후, 소수가 사용되는 옵션이 다를 경우에 다른 옵션을 재설정

 

  • 필드(private)의 값을 선언할 경우 Setter로 할 경우

생성자

  • 생성자 주입 방식을 권장
  • 변경 불가능한 안전한 객체 생성 가능

@AllArgsConstructor

  • 필드의 모든걸 가지고 생성자 생성

@RequiredArgsConstructor

  • final 인 필드를 가지고 생성자 생성

 

Order Service의 save를 할 때, 연관되어있는 OrderItem과 Delivery는 CascadeType.ALL 로 묶여있기 때문에 Order가 persist할 경우 같이 persist 됨

숙제

스프링부트 3

JUnit5

Hibernate 6

위 버전에서 SQL Query 확인 불가



동적 쿼리

  • JPQL : parameter 값이 있는지 없는지에 따라 Query를 붙이는 형태(복잡)
  • JPA Criteria : 읽기가 힘들고 유지보수가 어렵기 때문에 사실상 실무에서 사용하기 어렵다.
  • Query DSL


'끄적끄적 > Spring Boot' 카테고리의 다른 글

개인프로젝트_서버 구상  (0) 2024.08.11
Spring Boot Querydsl  (0) 2024.07.26
Spring Boot Data JPA  (0) 2024.07.13
Spring Boot 프로젝트 필기  (0) 2024.06.29