Learn/KH정보교육원

[KH정보교육원 당산] 78일 (Spring Boot - ORM과 JPA)

Dahoon06 2021. 7. 7. 15:31
728x90
반응형

ORM과 JPA

 

ORM : 객체지향 개념을 이용하여 관계형 데이터베이스에 적용(보존)하는 기술

즉, 객체지향 을 관계형으로 매핑하는 개념

 

객체지향 구조와 관계형 데이터베이스와 유사

  객체지향 과계형 데이터베이스
데이터 구조 클래스 테이블
데이터 멤버변수(데이터타입) 컬럼(데이터타입)
데이터 보관 인스턴스 Row(레코드)

 

 

객체지향과 관계형데이터베이스의 차이점

=> 객체지향 (행위)메서드

 

객체지향 : 데이터, 행위

관계형데이터베이스 : 데이터

결론

ORM은 객체지향과 관계형 사이의 변환 기법을 의미

JPA : ORM을 Java 언어에 맞게 사용할 수 있도록 제공되는 스펙

ORM이 상위 개념이고, JPA는 Java언어에 국한된 개념

 

JPA는 단순한 스펙이기 때문에 구현체(Hibernate 등)마다 프레임워크가 다를 수 있다

 


Spring Data JPA 와 JPA

스프링 부트는 JPA 구현체 중 Hibernate를 이용

Hibernate

ORM을 지원하는 오픈소스 프레임워크

단독으로 프로젝트에 적용이 가능한 독립된 프레임워크

 

스프링 부트 프로젝트 생성시 추가한

Spring Data JPA은 Hibernate를 쉽게 사용할 수 있도록 추가적인 API를 제공한다.

 

** Spring Data JPA 를 이용하여 개발 시, 필요한 코드는 두 종류

 

1. 엔티티 클래스 : JPA를 통하여 관리되는 객체(엔티티객체)를 위한 클래스

 

2. Repository : 엔티티 객체들을 처리하는 기능을 보유한 인터페이스

 

** Repository 인터페이스

Spring Data JPA에서 제공하는 인터페이스로 설계

스프링 내부에서 자동으로 객체를 생성하고, 실행하는 구조이기 때문에

단순히 인터페이스를 하나 정의 하기만 하면 된다 (JpaRepository 상속을 받아야한다.!!)

 

Hibernate를 단독으로 사용할 경우 : 모든 코드 직접 작성, 트랜잭션 처리도 직접 처리

 

Spring Data JPA를 이용할 경우 : 모든 코드가 자동으로 생성되기 때문에

CRUD 작업, 페이징 작업 개발을 하지 않아도 된다.


테스트를 위한 예제)

entity 패키지에 Memo 클래스를 만들었다.

package com.jdh.springboot.entity;

import lombok.*;

import javax.persistence.*;

@Entity
@Table(name = "tbl_memo")
@Data
@Builder //해당 클래스에 대한 객체 생성 처리(단, builder 어노테이션을 사용할 경우 아래 2개의 어노테이션 필수)
@AllArgsConstructor // 이 두 어노테이션이 없으면 컴파일 에러
@NoArgsConstructor
public class Memo {

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

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

    @Transient
    private int t;
}

@Entity : Spring Data JPA 사용시 필수 

해당 클래스는 엔티티 클래스이고,

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

 

옵션에 따라서 자동으로 테이블을 생성할 수도 있다

이 경우 해당 클래스의 멤버변수 설정에 따라서 자동으로 컬럼까지 생성된다..

 

@Table : @Entity 어노테이션과 함께 사용 가능

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

 

예) @Table(name = "tbl_memo") 의 경우

테이블 이름이 "tbl_memo"인 테이블을 생성

옵션을 인용하면 인덱스(indexes을 이용) 설정도 가능

 

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

만약 @Id 가 붙은 멤버변수에 입력되는 값이 사용자 입력이 아니라면

자동으로 생성되는 번호를 사용하기 위해서 @GeneratedValue 어노테이션을 사용한다.

 

예) @GeneratedValue(strategy = GeneratoionType.IDENTITY)

     PK를 자동으로 생성할 때 사용(이것을 키 생성전략이라고 부름)

 

@GeneratedValue(strategy = GeneratoionType.IDENTITY)

만약

연결되는 데이터 베이스가 오라클일 경우 : 별도의 번호 저장을 위한 테이블이 자동 생성

연결되는 데이터 베이스가 MySQL, MairaDB일 경우 : auto increment를 이용...

 

strategy 설정 값

GeneratoionType.AUTO : 기본값으로 JPA 구현체(Hibernate)가 생성 방식을 결정

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

GeneratoionType.SEQUENCE : 데이터베이스의 시퀀스를 이용하여 키를 생성. @SequenceGenerator 와 함께 사용

GeneratoionType.TABLE : 키 생성 전용 테이블을 생성하여 키 생성. @TableGenerator 와 함께 사용

 

 

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

이 어노테이션은 다양한 설정을 위한 옵션을 제공

name ="컬럼명"

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

length = 크기(20)

 

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

예) MySQL, MariaDB일 경우 : columnDefinition = "varchar(255) default 'YES'"

    오라클 경우 :  columnDefinition = "varchar2(255) default 'YES'"

 

--------------------------------------------------------------------------------------

 

만약 값을 사용자로부터 입력받지만, 테이블에 저장을 하지 않을 경우에는

해당 멤버변수 위에 @Transient 를 이용하여 제외 시킬 수 있다...

 

그 외에 JPA는 다양한 어노테이션이 있슴..  

 

JPA 어노테이션 : import javax.persistence.*;

 

==============================================

 

Lombok이 지원하는 주요 어노테이션 : import lombok.*;

 

@ToString : toString메서드를 재정의 

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

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

 

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

단, 이 어노테이션을 사용할 경우에는 반드시

@AllArgsConstrutor / @NoArgsConstrutor 를 함게 설정해야한다..

그렇지 않을 경우 컴파일 시 오류 발생!!!!!!!!!!!

 

MemoRepository 인터페이스

public interface MemoRepository extends JpaRepository<Memo, Long> {
                                                  //Entity클래스와 Id의 타입
}

JpaRepository 클래스를 상속 받는다. 이때 타입은 <엔티티클래스 , 해당엔티티의 Id값>

SQL 없이 CRUD 테스트

insert 작업 -> save(엔티티 객체)
select 작업 : findById(키 타입) / getOne(키 타입) ** 향후 사라질 메서드
update 작업 : save(엔티티 객체)
delete 작업 : deleteById(키 타입) / delete(엔티티 객체)

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

 

src/test/java에 새로운 패키지를 만들어서 확인

 

MemoRepositoryTest클래스

@SpringBootTest
public class MemoRepositoryTest {

    @Autowired
    MemoRepository memoRepository;

    @Test
    public void testClass() {
        System.out.println(memoRepository.getClass().getName());
    }

   /** @Test
    public void testInsertDummies() {
        IntStream.range(1, 100).forEach(i -> {
            Memo memo = Memo.builder().memo_Text("Sample TEST : " + i).build();
            memoRepository.save(memo);
        });
    }

    @Test
    public void testSelect(){
        Long mno = 100L;
                Optional<Memo> result = memoRepository.findById(mno);
                Memo memo = result.get();
    }

    public void testSelect2(){
        Long mno = 100L;
        Memo memo = memoRepository.getOne(mno);
    }
**/
    
    //페이지 처리
    @Test
    public void testPageDefault() {
    	Pageable pageable = PageRequest.of(0, 10); //0번 페이지시작 한페이지당 10개의 게시글 출력
    	Page<Memo> result = memoRepository.findAll(pageable);
    	
    	System.out.println(result);
    	System.out.println("----------------------------------------");
    	System.out.println("총 페이지 수 : " + result.getTotalPages());
    	System.out.println("총 게시글 수 : " + result.getTotalElements());
    	System.out.println("현재 페이지 번호 : " + result.getNumber());
    	System.out.println("페이지당 게시글 수 : " + result.getSize());
    	System.out.println("다음 페이지가 존재여부 : " + result.hasNext());
    	System.out.println("첫번째 페이지 존재여부 : " + result.isFirst());
    	
    }
    
    }

** 삭제(delete)와 수정(update) 코드가 빠져있다.

 

페이지 처리 출력값

총 페이지 수 : 10
총 게시글 수 : 99
현제 페이지 번호 : 0
페이지 크기 : 10
다음 페이지가 있는지 확인 : true
첫번째 페이지 : true

 

이용한 데이터 (mariaDB)

 

페이지 처리에서 의 핵심 Pageable (org.springframework.data.domain.Pageable)

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

 

따라서 객체를 생성할 때

org.springframework.data.domain.PageRequest 클래스 사용

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

 

PageRequest.of( int page, int size ) : 0번 부터 시작하는 페이지 번호와 페이지당 출력된 데이터 수

PageRequest.of( int page, int size, Sort.Direction direction, String ..props

: Sort.Direction direction : 정렬방향(방법)

  String ..props : 정렬 기준 필드

 

PageRequest.of(int page, int size, Sort sort)

: Sort sort : 정렬 관련 정보 ( 즉, 정렬 방향과 정렬 기준 필드들을 하나로 묶은 객체)

 

Page<Memo> result = memoRepository.findAll(pageable);

findAll(Pageable pageable)

반환 타입 : Page<엔티티 객체 타입>

=> 2가지의 SQL을 실행

1. limit 를 사용하는 select SQL : 지정한 시작페이지 번호부터 지정한 개수를 select하기 위한 SQL

2. count() 함수를 사용하는 select SQL : 전체 레코드(엔티티) 개수를 확인하기 위한 select SQL

 

728x90
반응형