Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

희디비

[Chapter3] SpringBoot JPA 본문

Clone Coding

[Chapter3] SpringBoot JPA

희디비 2023. 10. 21. 23:17

JPA 사용 이유

- 반복되는 CRUD(Create, Read, Update, Delete)인한 코드반복이 일어납니다.

- 데이터베이스는 어떻게 데이터를 저장할지 초점이 맞춰져 있으나,

  객체지향 프로그래밍 언어는 기능과 속성을 관리하는 기술이므로 페러다임 불일치가 일어납니다.

User user = findUser();
Group group = user.getGroup();

객체지향에서는 User과 Group은 부모 자식 관계임을 알수있습니다.

User user = userDao.findUser();
Group group = groupDao.findGroup(user.getGroupId());

데이터베이스의 경우 User, Group을 따로 조회하게 됩니다.

(Dao = Data Access Object로 데이터에 접근하기위한 객체라는 뜻입니다.)

위경우 User와 Group의 관계를 유추하기 힘듭니다.

 

상속, 1:N등 객체 모델링을 데이터 베이스로 구현이 불가능해 웹 어플리케이션은

데이터 베이스 모델링에 집중됩니다. 이를 해결하기위해 JPA가 등장하게 됩니다.

JPA를 사용할경우 중간에서 페러다임 일치를 시켜주게 되므로, 객체지향적 프로그래밍이 가능하고,

JPA가 관계형 데이터베이스에 맞게 SQL을 대신 생성하여 실행합니다.

 

Spring Data JPA

JPA는 인터페이스로서 자바 표준명세서입니다. 인터페이스인 JPA를 사용하기위해서는 구현체가 필요합니다

Hibernate,EclipseLink 등이 있이 있으나 Spring에서는 구현체를 직접다루지 않고,

구현체를 추상화시킨 Spring Data JPA 모듈을 사용합니다.

JPA <- Hibernate <- Spring Data JPA

구현체를 직접 다루지않고 추상화한 Spring Data JPA를 사용하면 두가지 이점이 있습니다.

 

1. 구현체 교체의 용이성

 

- Spring Data JPA는 내부에서 구현체 매핑을 지원해주기 때문에 구현체를 변경이 간단합니다.

 

2. 저장소 교체의 용이성

 

- Spring Data JPA는 Spring Data의 하위 프로젝트로 CRUD의 인터페이스가 같습니다.

  그러므로 저장소를 Redis, MongoDB로 교체할때 Spring Data Redis, Spring Data MongoDB로만

  의존성을 교체하면 기능은 변경할것이 없습니다.

 

1. compile('org.springframework.boot:spring-boot-starter-data-jpa')
2. compile('com.h2database:h2')

1. Spring Data Jpa 추상화 라이브러리로 스프링 버전에 맞춰 JPA 라이브러리 관리를 해줍니다.

2. h2데이터 베이스로 별도설치 없이 의존성으로 사용 가능히며 메모리에서 실행되기 때문에

    재시작마다 초기화 됩니다.

Entity 클래스

@Getter
@NoArgsConstructor
@Entity
public class Posts {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(length = 500, nullable = false)
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder
    public Posts(String title, String content, String author){
        this.title = title;
        this.content = content;
        this.author = author;
    }

}

@Getter : 롬복 어노테이션으로 속성값의 Getter을 자동생성합니다.

@NoArgsConstructor : 롬복 어노테이션으로 기본생성자를 만들어 줍니다.

@Entity : JPA어노테이션으로 테이블과 링크될 클래스입니다. PostsService > Posts_service table로 매칭됩니다.

@Id, @GenneratedValue : Id는 기본키를 의미하고, Type.IDENTITY를 통해 auto_increment가 됩니다.

@Column : Entity 클래스는 다 칼럼이라 안써도 되지만 속성값을 줄떄 사용됩니다.

@Builder : 빌더 패턴 클래스를 생성하는 것입니다.

 

빌더클래스란?

더보기
@Getter
public class Student {

    private Long id;
    private String name;
    private Integer age;

    private Student(StudentBuilder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
    }

    public static class StudentBuilder {

        private Long id;
        private String name;
        private Integer age;

        //필수값들은 생성자의 파라미터
        public StudentBuilder(Long id) {
            this.id = id;
        }

        public StudentBuilder setName(String name) {
            this.name = name;
            return this;
        }

        public StudentBuilder setAge(Integer age) {
            this.age = age;
            return this;
        }

        public Student build() {
            return new Student(this);
        }

    }
}

이렇게 값을 채워넣고 build()를 통해 인스턴스를 생성하는것을 빌드 패턴 클래스라고 합니다.

public Example(String a, String b){
this.a = a;
this.b = b;
}

위와 같은경우 생성자 사용시 a,b의 순서를 신경써야하지만

Example.builder()
.a(a)
.b(b)
.build();

빌더패턴 클래스를 사용하면 파라메터가 지정되므로 순서와 관계없이 알맞게 파라메터에 값이 들어가고

속성의 값으로 어떤것이 들어가는것이 보이므로 가독성도 좋습니다.

Posts 클래스로 데이터베이스에 접근하기 위해 PostsRepository를 생성합니다.

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostsRepositiory extends JpaRepository<Posts,Long> {}

JpaRepository<Entity 클래스, PK>를 상속받으면 CRUD 메소가 자동 생성됩니다.

Entity 클래스와 Entity Repository클래스는 함꼐 위치 해야 하고 관리됩니다.

 

Entity Repositroy 테스트 코드를 작성합니다.

import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.time.LocalDateTime;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PostRepositioryTest {

    @Autowired
    PostsRepositiory postsRepository;

    @After
    public void cleanup(){
        postsRepository.deleteAll();
    }

    @Test
    public void 게시글저장_불러오기(){
        //given
        String title = "테스트 게시글";
        String content = "테스트 본문";

        postsRepository.save(Posts.builder()
                .title(title)
                .content(content)
                .author("jojoldu@gamil.com")
                .build());
        //when
        List<Posts> postsList = postsRepository.findAll();

        //then
        Posts posts = postsList.get(0);
        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }
 }

@After : Junit 단위 테스트가 끝날때 마다 수행되는 메소드를 지정합니다.

여러 테스트가 진행되면 H2 데이터베이스에 데이터가 그대로 남아있어 다음테스트에 실패 할수있습니다.

 

- postsRepository.save : 테이블 posts에 id가 있다면 update 없다면 insert가 실행됩니다.

- postsRepository.findAll : posts 테이블의 모든데이터를 조회하는 메소드 입니다.