온 코딩

[Spring Data JPA] memoTest - IntelliJ 본문

복습 ARCHIVE/모델별 프로젝트

[Spring Data JPA] memoTest - IntelliJ

SummerON 2021. 7. 7. 15:53

IntelliJ 사용

라이브러리 : grdle 사용 중 ~

Spring Boot - 엔티티클래스 / 레포지터리 클래스만 있으면 됨

0. 클래스 구조

1. Entity 만들기 

package com.hhw.ex2.entity;

import lombok.*;

import javax.persistence.*;

@Entity
@Table(name = "tbl_memo")
@ToString
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Memo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long mno;

    @Column(length = 200, nullable = false)
    private String momoText;

    @Transient
    private int t;

}

JPA 어노테이션 클래스 : import javax.persistence.*

@Entity : Spring Data JPA 사용 시 필수

해당 클래스는 엔티티 클래스이고, 해당 클래스의 인스턴스들이 JPA로 관리되는 엔티티 객체라는 것을 의미 

옵션에 따라서 자동으로 테이블을 생성할 수도 있다. 이경우 해당 클래스이 멤버변수 설정에 따라서 자동으로 칼럼까지 생성된다.

 

@Table(name="테이블이름") : @Entity 어노테이션과 함께 사용 가능 

관계형 데이터베이스에서 엔티티 클래스를 어떤 테이블로 생성할 것인지에 대한 정보를 담기위한 어노테이션 

 

@Id : @Entity가 붙은 클래스 내에는 반드시 Primary Key(PK)에 해당하는 특정 멤버변수를 @Id로 설정해야 한다.

만약 @Id가 붙은 멤버변수에 입력되는 값이 사용자 입력이 아니라면 자동으로 생성되는 번호를 사용하기 위해서 @GeneratedValue(strategy=GenerationTyoe.IDENTITY)와 같은 어노테이션을 사용한다.

 

* @GeneratedValue(strategy=)

strategy=GenerationTyoe.AUTO : 기본값으로 JPA 구현체 Hibernate가 생성 방식을 결정

strategy=GenerationTyoe.IDENTITY : 사용하는 데이터베이스가 키 생성을 결정

strategy=GenerationTyoe.SEQUENCE : 데이터베이스 시퀀스를 이용하여 키를 생성 @SequenceGenerator와 같이 사용

strategy=GenerationTyoe.TABLE : 키 생성 전용 테이블을 생성하여 키 생성

 

@Column : 추가적인 필드(컬럼)가 필요할 경우에 사용 

다양한 설정을 위한 옵션 제공

name="컬럼명"

nullable="NULL값 허용여부(true/false)"

length=크기(20)

columnDefinition을 이용하여 기본값을 설정할 수도 있다.

ex) columnDefinition = "varchar(255) default 'YES'"

 

Lombok 어노테이션 클래스 : import lombok.*;

@toString : toString() 메서드 재정의

@Getter : private 변수에 대한 getter 메서드 자동 생성

@Setter : private 변수에 대한 setter 메서드 자동 생성

@Builder : 해당 클래스에 대한 객체 생성 처리 ( 실제 사용시엔 클래스명.builder()를 이용)

             : 단, 이 어노테이션을 사용할 경우 반드시 @AllArgsConstructor 와 @NoArgsContructor를 함께 설정 

 

2. application.properties: 대표 설정 파일

server.port=8080
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/bootex
spring.datasource.username=summer
spring.datasource.password=1234
spring.jpa.open-in-view=false

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

서버 포트번호 변경 

server.port=새로운 포트번호 (새로운 포트번호는 1000이후 번호를 사용)

데이터베이스(DataSource) 설정

spring.datasource.driver-class-name=접속데이터베이스드라이버이름

spring.datasource.url=데이터베이스 접속주소

spring.datasource.name=

spring.datasource.password=

Spring Data JPA를 위한 설정 

spring.jpa.hibernated.ddl-auto= crate/update/create-drop/validate 

:프로젝트 실행 시 자동으로 DDL을 생성할 것 인지 결정

등, persistence.xml에서의 설정과 같음

2021.07.05 - [복습 ARCHIVE/모델별 프로젝트] - [JPA](간단한 게시판) JPA 기본 설명 및 Mavens project 이용 - EntityManager, persistent.xml, JPA API,

 

[JPA](간단한 게시판) JPA 기본 설명 및 Mavens project 이용 - EntityManager, persistent.xml, JPA API,

Maven Project 생성 환경설정 build path - odbc8.jar 임포트 Project Facets - 자바버전확인, JPA 클릭 Dependency 추가(pom.xml) org.hibernate hibernate-entitymanager 5.1.0.Final Mavens 임포트 확인하기 VO..

on-coding.tistory.com

 

3. Repository 인터페이스

  • Spring Data JPA에는 여러 종류의 인터페이스 JPA 관련 작업을 별도의 코드 개발 없이 처리할 수 있도록 지원한다.
  • CRUD작업, 페이징 처리, 정렬, 검색 등의 처리도 인터페이스의 메서드를 호출하는 형태로 처리 되는데, 기능에 따라 상속 구조로 추가적인 기능을 제공

종류

CrudRepository :일반적인 CRUD 작업만 할 경우 사용 

PagingAndSortRepository : 일반적인 CRUD 작업 + 페이징, 정렬 작업을 사용할 경우

JpaRepository : JPA 관련 모든 기능을 사용할 경우 (개발자가 가장 많이 사용하는 인터페이스)

 

JpaRepository 

Spring Data JPA는 이를 상속하는 인터페이스 선언만으로 모든 작업에 대한 개발이 끝난다.

실제 동작 시에는, 스프링이 내부적으로 해당 인터페이스에 맞는 코드를 자동 생성한다.

JpaRepository를 사용할 경우에는 엔티티 타입 정보와 @id 타입 정보를 Map 형태로 지정한다

 

Spring Data JPA 인터페이스 선언만으로도 자동으로 스프링 빈(Bean)으로 등록한다.

즉, 스프링이 내부적으로 인터페이스 타입에 맞는 객체를 생성하여 빈(Bean)으로 등록

package com.hhw.ex2.repository;

import com.hhw.ex2.entity.Memo;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemoRepository extends JpaRepository<Memo, Long> {
}

선언만 하고 내부 작업이 없음..

엔티티클래스, 아이디어노테이션이 붙은 멤버변수 타입을 꼭 적어줘야 함 

 

4. 테스트 코드를 이용하여 CRUD 및 페이징 작업 테스트

1. MemoRepository를 이용하여 작성된 테이블에 SQL없이 CRUD 작업 테스트

MemoRepositoryTests.java // test 폴더에 만들기!!

작업하는 내용 (변경할 가능성이 있는 클래스) 들은 test 쪽에 만들어야함!! test 어노테이션을 통해 각 메서드 마다 실행 확인

package com.hhw.ex2.repository;

import com.hhw.ex2.entity.Memo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;
import java.util.stream.IntStream;

@SpringBootTest
public class MemoRepositoryTests {
    @Autowired
    MemoRepository memoRepository;

    @Test
    public void testClass() {
        System.out.println(memoRepository.getClass().getName());
        //출력결과 :com.sun.proxy.$Proxy92
        // 스프링이 MemoRepository 객체를 자동으로 생성
        //자동으로 생성할 때 프록시 방법으로 생성하기 때문에 위와 같은 이름이 출력됨
    }

    //등록 테스트 메서드 : 99개의 새로운 Memo 객체를 생성
    //MemoRepository인터페이스를 이용하여 insert
    @Test
    public void testInsertDummies(){
        IntStream.range(1,100).forEach(i ->{
            Memo memo = Memo.builder().memoText("Sample "+i).build();
            memoRepository.save(memo);
        });
    }

    //조회 작업 테스트 메서드 : findById() 활용
    //findById()는 반환 타입이 Optical 타입으로 반환
    @Test
    public void testSelect(){
        //데이터베이스에 존재하는 mno 값을 이용
        Long mno = 99L;
        Optional<Memo> result = memoRepository.findById(mno);
        //조회된 결과 : 해당 메서드가 호출되어 실행되는 순간 바로 SQL이 처리

        System.out.println("========================");
        if(result.isPresent()){
            Memo memo = result.get();
            System.out.println(memo);
        }
        System.out.println("========================");

    }

    //조회 작업 테스트2 : getOne()을 이용
    //getOne()은 엔티티 객체로 반환
   @Transactional
    @Test
    public void testSelect2(){
        Long mno = 99L;
        Memo memo = memoRepository.getOne(mno);
        //getOne()를 이용하여 조회된 결과 : 해당 객체를 반환하는데 메서드 실행시 sql이 실행되는 게 아니라 객체가 필요한 시점에 sql 실행
        System.out.println("===========================");

        System.out.println(memo);
        //Memo는 @ToString을 이용하여 toString()가 재정의 되었기 때문에
        //memo 객체를 출력하면 자동으로 toString()메서드가 호출되어 각 변수의 값을 확인할 수 있다.

        System.out.println("===========================");
    }

    //수정 작업 테스트 메서드 : save()메서드 이용
    @Test
    public void testUpdate(){
       //Memo memo = Memo.builder().mno(99L).memoText("Update Text").build(); //업데이트
       Memo memo = Memo.builder().memoText("Update Text").build(); //인서트
        System.out.println(memoRepository.save(memo));

    }

    //삭제 테스트 메서드 : deleteById(id)를 이용
    //만약 해당 데이터가 존재하지 않으면 예외가 발생한다 : EmptyResultDataAccessException
    @Test
    public void testDelete(){
        Long mno = 98L;
        memoRepository.deleteById(mno);
    }

    // 페이지 처리 메서드 : findAll()
    //findAll() : Spring Data JPA에서 페이징 처리와 정렬 작업에서 사용되는 메서드
    //PagingAndSortRepository 에서 정의된 메서드
    //이 메서드에 전달할 수 있는 데이터는 Pageable 객체
    //따라서, 이 메서드를 사용하려면 먼저 Pageable 객체를 만들어야함
    //이 객체는 pageRequest.of(시작, 갯수) 같이 지정하여 얻어낼 수 있다.
    //주의사항 : 스프링 부트 2.0 버전부터 지원
    @Test
    public void testPageDefault(){
        //만약 한 페이지에 10개씩 출력하고 싶을 경우
        Pageable pageable = PageRequest.of(0,10);
        Page<Memo> result = memoRepository.findAll(pageable);

        System.out.println(result);
        System.out.println("----------------------");
        System.out.println("total pages "+result.getTotalPages());
        System.out.println("total count "+result.getTotalElements());
        System.out.println("current page number "+result.getNumber());
        System.out.println("page size "+result.getSize());
        System.out.println("has next page? "+result.hasNext());
        System.out.println("first page? "+result.isFirst()); //시작페이지(0) 존재여부
    }

    //정렬 조건 주가 및 조회된 데이터 출력 : findAll()메서드 이용
    @Test
    public void testSort(){
        Sort sort = Sort.by("mno").descending();

        Pageable pageable = PageRequest.of(0,10,sort);
        Page<Memo> result = memoRepository.findAll(pageable);

        result.get().forEach(memo -> {
            System.out.println(memo);
        });
    }

}

IntelliJ에서는 메서드별로 실행이 가능하다. 코드 추천도 잘된다,,,,

개인사용자 1년 199 달러던데.. 살까,,,, 

CRUD 작업 지원 메서드 (JpaRepository)

insert 작업 : save(엔티티 객체)

select 작업 : findByld(키 타입) /getOne(키 타입) <= getOne()은 현재까지 지원하지만 향후 사라질 예정인 메서드 

update 작업 : save(엔티티 객체) 

delete 작업 : deleteByld(키 타입) / delete(엔티티 객체)

*insert 작업과 update 작업 : save(엔티티 객체) ->JPA의 구현체가 메모리상에서 객체를 비교하고 없으면 insert / 존재하면 update를 동작

 

save()

Memo memo = Memo.builder().mno(100L).memoText("Update Text").build();  //100번 값이 없으므로 인서트

 -> 지금 내가 하고 있는 프로젝트에서는 자동으로 @Id 필드 증가되게 만들었기 때문에 실행 불가능!

Memo memo = Memo.builder().mno(99L).memoText("Update Text").build(); //업데이트  

: 해당하는 값이 존재하는지 확인 후 update또는 insert를 결정해서 실행

 

Pageable 클래스

페이지처리에 필요한 정보를 전달하는 용도로 설계된 인터페이스

따라서 객체를 생성할 때는 import org.springframework.data.domain.Pageable; 을 이용

PageRequest는 생성자가 protected로 되어 있기 때문에 new를 사용핮 ㅣ못하고 

PageRequest의 static of()를 이용하여 Pageable 객체를 반환 받아야 한다.

PageRequest.of(int page, int size) - 0번부터 시작하는 페이지 번화 갯수 // 다양한 다른 파라미터 있음

 

 

Comments