DB를 직접적으로 다룰때 어떤 문제들이 발생할까?
앞선 프로젝트를 통해 DB에 ID, name, content 등 여러 데이터를 한대 묶은 객체형태로 CRUD해왔다. 이때 SQL문을 통해 직접 DB에 접속하여 테이블을 생성하고 애플리케이션에서 SQL을 작성하여 JDBC를 통해 클라이언트로부터 받은 데이터를 DB에 저장, 조회하거나 삭제해왔으며 그 결과를 다시 객체형태로 변환하여 클라이언트에게 반환하였다. 또한 코드 구현이 끝난후 만약 클라이언트의 데이터의 새로운 타입의 데이터가 추가된다면? 다시 일일히 SQL문을 수정하고 반환하는 객체의 형태 또한 수정을 해야한다. 이는 개발자로서 로직 개발보다는 SQL 작성 및 SQL문 수정에 따른 수정요소에 더 많은 노력이 들어간다. 그렇다면 이러한 문제를 어떻게 해결할까?
ORM : Object-Relational Mapping, ORM은 객체와 DB의 관계를 매핑 해주는 도구
ORM의 정의를 통해 알수있듯이 개발자가 일일히 객체와 DB간의 관계를 직접 매핑해왔지만 이제는 ORM을 사용하여 이를 자동적으로 처리하여 개발자가 로직 개발에 좀더 집중할수 있게되었다.
자바에서는 JPA라 불리는 Java PErsistence API(자바 ORM 기술에 대한 표준명세)를 제공하고있다.
JPA는 애플리케이션과 JDBC 사이에서 동작하고 있으며 JPA를 사용하면 DB 연결 과정을 직접 개발하지 않아도 자동적으로 처리해준다. 또한 객체를 통해 간접적으로 DB 데이터를 다룰수 있기때문에 매우 쉽게 DB작업을 처리 할수있다.
더 나아가서 JPA가 표준명세이고 이를 실제로 구현한 프레임워크 중 사실상 표준은 하이버네이트이다. SpringBoot에서는 기본적으로 하이버네이트 구현체를 사용중이다.
사실상 표준 : 기업간의 치열한 경쟁을 통해 시장에서 결정되는 비 공식적 표준
여기서 추가적으로 Entity(엔티티)의 개념이 추가되는데 엔티티는 JPA에서 관리되는 객체들을 뜻한다. Entity클래스는 DB테이블과 매핑되어 JPA에 의해 관리가 되며 클라이언트의 요청으로 받은 데이터(Dto)를 Entity로 변환 후 JPA를 통해 로직에 의해 요청이 DB에 수행이 되고 요청이 끝난 후 Entity타입을 Dto로 변환하여 클라이언트에게 반환한다.
JPA를 사용하기 위해서는 위와 같이 resources 폴더 아래에 META-INF폴더와 그 안에 persistence.xml파일을 생성해준 뒤 아래와 같이 설정을 해줘야한다. SpringBoot의 경우에는 application.properties에 하이버네이트 properties만 넣어주면 자동으로 해당파일을 바탕으로 만들어준다.
겉보기에는 복잡해보이지만 하나하나 차근차근 보면
<persistence-unit name="memo">
<class>com.sparta.entity.Memo</class>
<properties>
<property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jakarta.persistence.jdbc.user" value="root"/>
<property name="jakarta.persistence.jdbc.password" value="1234"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memo"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
</properties>
unit name은 이름을 설정하며 다음으로 class는 해당 엔티티로 관리할 클래스의 경로를 지정하는 코드이며 그 아래로는 JDBC드라이버, DB의 userID와 password 그리고 DB의 해당 데이터베이스의 경로를 나타낸다.
또한 하이버네이트 관련 속성들중 'update'라 작성되있는 부분은 create , create-drop , update, validate 네가지로 작성할수있는데 각 기능은 아래와같다.
create : SessionFactory 시작시 스키마를 삭제하고 다시 생성
create-drop : SessionFactory 종료시 스키마를 삭제
update : SessionFactory 시작시 객체 구성와 스키마를 비교하여 컬럼 추가/삭제 작업을 진행함. 기존의 스키마를 삭제하지 않고 유지.
validate : SessionFactory 시작시 객체구성과 스키마가 다르다면 예외 발생시킴.
이제 엔티티 클래스의 코드구현을 살펴보면 아래와 같다.
package com.sparta.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
public class Memo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// nullable: null 허용 여부
// unique: 중복 허용 여부 (false 일때 중복 허용)
@Column(name = "username", nullable = false, unique = true)
private String username;
// length: 컬럼 길이 지정
@Column(name = "contents", nullable = false, length = 500)
private String contents;
}
처음 보는 어노테이션들이 있는데 이들의 기능은 아래와 같다.