728x90

기존 스프링에선 MyBatis와 같은 SQL Mapper를 사용했다.

그러다보면 개발하는 시간보다 SQL을 다루는 시간이 더 많아진다. 객체 지향이 아닌 테이블 모델링에만 집중하게 되는 기형적인 형태가 된다. 이런 문제의 해결책으로 JPA라는 자바 표준 ORM기술을 사용할 수 있다.

SQL Mapper가 쿼리를 매핑한다면 ORM은 객체를 매핑한다.

 

관계형 데이터베이스와 객체지향 프로그래밍 언어의 패러다임이 서로 다른데, 객체를 데이터베이스에 저장하려고 하면 여러 문제가 생기는데, 이를 패러다임 불일치라고 한다. 기존 SQL Mapper의 문제점이다. JPA는 중간에서 패러다임 일치를 시켜주는 기술이다.

 

즉 개발자는 객체지향적으로 프로그래밍을 하고, JPA가 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성해서 실행한다. 따라서 더는 SQL에 종속적인 개발을 하지 않아도 된다.


JPA를 사용해보자. build.gradle에 의존성을 등록한다.

그 후 도메인을 담을 패키지 domain을 생성하자.

domain 패키지에 posts패키지와 Posts 클래스를 만든다.

Posts는 게시글에 대한 클래스이다.

@Getter
@NoArgsConstructor
@Entity
public class Posts extends BaseTimeEntity {

    @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;
    }

    public void update(String title, String content){
        this.title = title;
        this.content = content;
    }
}

lombok의 어노테이션 @Getter랑 @NoArgsConstructor보다 JPA의 어노테이션인 @Entity를 클래스에 가깝게 쓰자.

@Entity : 데이터베이스의 테이블과 링크될 클래스임을 나타낸다. 보통 클래스의 이름을 언더스코어 네이밍으로 테이블 이름을           매칭한다. ex) SaleManager.java -> sales_manager table

@Id : 테이블의 PK 필드를 나타낸다.

@GeneratedValue : PK의 생성 규칙을 나타낸다. GenerationType.IDENTITY 옵션을 추가해야 auto_increment가 된다.

@Column : 테이블의 칼럼을 나타내며 굳이 선언하지 않아도 클래스의 모든 필드는 칼럼이 된다.  변경이 필요한 옵션이 있을 때     사용한다.

@NoArgsConstructor : 기본 생성자를 자동 추가한다. ( public Posts(){}와 같은 효과)

@Getter : getter 메소드 자동 생성

@Builder : 빌더 패턴 클래스를 생성한다. 생성자에 포함된 필드만 빌더에 포함된다.

 

Entity 클래스에서는 절대 Setter 메소드를 만들지 않는다. 대신 해당 필드의 값 변경이 필요하면, 그 목적과 의도를 나타내는 메소드를 추가해야 한다. 

 

Entity를 생성했다면 해당 클래스로 DB에 접근하게할 JpaRepository를 생성한다.

PostsRepository를 생성하자.

MyBatis에서 Dao라 불리는 DB 접근자를 JPA에선 Repository라고 부르며 인터페이스로 생성한다.

인터페이스를 생성한 후 JpaRepository<Entity 클래스, PK 타입>을 상속하면 기본적인 CRUD 메소드가 자동 생성된다.

@Repository를 추가할 필요도 없으며, 주의할 점은 Entity와 Repository는 함께 위치해야 한다는 점이다.

 

두 클래스를 검사하는 test코드를 작성해보자.

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

    @Autowired
    PostsRepository postsRepository;

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

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

        postsRepository.save(Posts.builder()
                .title(title)
                .content(content)
                .author("jojojo@gmail.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 : 단위 테스트가 끝날 때마다 자동으로 수행되는 메소드를 지정하는 것이다. Posts에 남은 글을 모두 삭제한다.

@postsRepository.svae : 테이블 posts에 insert/update 쿼리를 실행한다. id 값이 있다면 update, 없다면 insert를 실행한다.

@postsRepository.findAll : 테이블 posts에 있는 모든 데이터를 조회해온다.


JPA Auditing으로 생성시간/수정시간 자동화하기

보통 엔티티에는 해당 데이터의 생성시간과 수정시간을 포함한다. 유지보수에 있어서 중요한 정보이기 때문이다.

매번 DB에 삽입,갱신하기 전에 날짜 데이터를 등록,수정하는 코드가 반복해서 들어간다. 이 문제를 해결해보자.

 

domain 패키지에 BaseTimeEntity 클래스를 생성한다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime modifiedDate;
}

이 클래스를 모든 Entity의 상위 클래스로 두어 Entity들의 생성,수정시간을 자동으로 관리할 수 있게 된다.

@MappedSuperClass : Entity 클래스들이 이 클래스를 상속할 경우 두 필드들을 칼럼으로 인식하게 된다.

@EntityListner(AuditingEntityListener.class) : Auditing 기능을 포함한다.

@CreatedDate : Entity가 생성되어 저장될 때 자동으로 생성 시간을 저장한다.

@LastModifiedDate : 조회한 Entity의 값을 변경할 때 시간이 자동 저장된다.

 

그 후 Entity 클래스들이 BaseTimeEntity를 상속받게 하고

Application 클래스에 JPA Auditing을 활성화하는 @EnalbeJpaAuditing 어노테이션을 추가하자.

앞으로 posts.getCreatedDate()나 posts.getModifiedate()를 통해 생성/수정 시간을 구할 수 있다.

728x90

+ Recent posts