해당 내용의 출처는 김영한 님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술의 강의 내용이고,본 포스트는 수강후 개인적으로 공부한 내용을 필기하고 다시 복습하기 위하여 작성되었고, 기타 내용을 보충하기 위한 부분은 관련링크로서 출처를 밝힌다.
출처 관련 링크는 #1에 기재하였으니 참고하기 바란다.
1. 서론
이전장까지는 순수하게 JDBC를 구현하여 DB를 접근하는 방법을 학습하였고, 그 후 출범한 JDBC Template를 이용하여 DB를 접근하는 방법을 학습하였다면, 이제는 JPA를 통하여 DB에 접근하는 방법에 대해서 학습한다.
참고로, JPA에 관한 내용은 Java/JSP에서 한 번 다룬적이 있으므로 해당 내용을 참고하면 된다.
이전장까지의 내용으로 JDBC의 구조 및 원리를 대략적으로 파악하였으니 본 장의 내용을 이용하여 DB와의 접근을 하면 되겠다.
JDBC Template가 반복적으로 사용되는 코드를 없애주고 SQL의 입력만을 요구하였다면, JPA는 더 나아가 SQL도 직접 JPA가 작성하여 명령을 수행한다.
JPA를 사용한다면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있다. 따라서 생산성이 증대된다.
2 . JPA를 이용한 DB 접근
JPA를 사용하려면 JPA와 관련된 의존성을 주입하여야 한다. 다음과 같다.
// implementation "org.springframework.boot:spring-boot-starter-jdbc" // 제거해도 좋다.
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
본 실습을 같이 진행하였다면, 상기 JDBC와 관련된 의존성은 삭제하여도 무방하다. JPA 의존성에 JDBC 관련 의존성이 같이 따라오기 때문에 data-jpa라는 의존성만 주입하여주면 충분하다.
정상적으로 의존성 주입이 완료되었다면 External Libraries란에 하기의 의존성들이 자동으로 주입되어 있을 것이다.
// External Livraries
Gradle : org.hibernate.common:hibernate-commons-annotaions:"버전"
Gradle : org.hibernate:hibernate-core:"버전"
JPA는, 이전 Java/JSP에서도 정리한 바와 같이, 인터페이스이다. JPA를 구현것이 hibernate와 eclipsLink와 같은 것들이 있다. 해당 예제는 hiberante를 이용하여 진행한다.
JPA는 ORM이라는 기술이다. ORM은 문자 그대로 Object(객체)와 Relational(관계형 데이터베이스)를 Mapping(매핑)하는 기술이다. 이때 매핑을 위하여 어노테이션을 이용하는 것이다.
또한 application.properties에 JPA와 관련된 세팅을 추가하여야 한다. 다음과 같다.
spring.jpa.show.sql=true
spring.jpa.hibernate.ddl-auto=none
//spring.jpa.hibernate.ddl-auto=create
설정의 상세는 다음과 같다.
- 1. spring.jpa.show.sql=true : JPA가 작성한 SQL을 확인할 수 있다.
- 2. spring.jpa.hibernate.ddl-auto=none : JPA를 사용한다면, DB와 매핑할 객체를 확인하여 자동으로 테이블을 생성해 주는 기능이 있다. 따라서 해당 설정을 none으로 해준다면, 자동으로 테이블을 생성해 주는 기능을 사용하지 않을 수 있다.
- none을 create로 변경하여 저장한다면, JPA가 테이블까지 생성하도록 설정할 수 있다.
설정까지 마쳤다면, 다음으로는 객체와 데이터베이스의 테이블을 매핑하기 위하여 어노테이션을 추가하여야 한다.
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
@Entity : 해당 어노테이션은 데이터베이스의 테이블과 일대일로 매핑되는 객체 단위이며, Entitiy 객체의 인스턴스 하나가 테이블 하나의 레코드 값을 의미한다.
따라서 객체의 인스턴스를 구분하기 위하여 유일한 키값(Key Value)을 보유하게 되는데, 이것은 테이블의 Primary Key(PK)와 같은 의미를 지니며, 클래스명 아래의 @Id 어노테이션으로 표기된다.
@Entity
public class Member {
@Id
private Long id;
...
// 일 때,
CREATE TABLE `member`(
`id` BIGINT(20) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
...
// `member` 테이블에서 PRIMARY KEY(PK)는 `id` 컬럼이기 때문에,
// `id`컬럼과 일대일로 매핑되는 Member 클래스의 id필드에 @Id 어노테이션을 부여한 것이다.
@GeneratedValue : PRIMARY KEY(PK)칼럼의 데이터 타입은 정해져 있지는 않으나 구분 가능한 유일한 값을 가지고 있어야 한다. 또한, 데이터 경합으로 인하여 발생되는 데드락* 현상을 방지하기 위하여 대부분 Java의 Long타입을 이용한다.
String형태의 고정된 키값을 직접 생성하여 관리하기도 하지만, 대량의 요청이 유입되더라도 중복과 데드락이 발생되지 않을 만큼 키값이 빨리생성되고 안전하게 관리되어야 한다는 것이 중점이다.
상기와 같은 이유로서, MySQL에 AUTO_INCREMENT 방식으로 컬럼에 속성을 부여하는데, 매핑되는 테이블에 AUTO_INCREMENT 속성을 추가하였다면, EntityManager* 에게 매핑되는 필드가 AUTO_INCREMENT 속성이 부여되었다는 것을 명기해주어야 한다.
그 때 사용하는것이 GenerationType인데, GenerationType.IDENTITY로 지정하여 EntityManager에게 매핑된 필드가 테이블에서는 AUTO_INCREMENT 인 것을 알려주는 것이다.
이 때, 자동으로 생성되는 값을 가지는 PK칼럼(PRIMARY KEY AUTO_INCREMENT)의 이름은 명시적으로 id로 지정하는 것이 관례이다. 다음과 같다.
// 매핑할 객체
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
// 매핑할 테이블
CREATE TABLE `member`(
`id` BIGINT(20) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
...
만일, 매핑할 테이블의 칼럼명과, 엔티티 클래스의 칼럼명이 일치하지 않는다면, @Column 어노테이션과 name속성을 이용하여 수동으로 매핑할 수 있다.
// 매핑할 테이블
CREATE TABLE `member`(
...
`member_name` VARCHAR(100) NOT NULL
);
// 매핑할 엔티티 클래스
@Entity
public class Member {
@Column(name="member_name")
private String name;
기타 어노테이션은 API의 어노테이션 장에서 정리하였으니 참고하기 바란다.
상기와 같이, 엔티티 클래스에 어노테이션을 추가하여 설정을 완료한다면, JPA가 SQL까지 자동으로 작성하여 INSERT, UPDATE, DELETE 등을 수행하기 때문에 DB와의 통신이 상당히 편리해진다.
*EntityManager : JPA에서는 EntityManager라는 모듈이 동작한다. 이전 장의 의존성 주입부분에서, spring-boot-starter-data-jpa를 추가하였을 때, 스프링 부트에서 자동으로 EntityManager라는 것을 생성하여준다. 따라서 스프링부트를 이용하였다면, 생성자로서 상태주입을 받기만 하면 된다.
public class JpaMemberRepository implements MemberRepository {
// 스프링 부트에서 자동으로 생성하는 엔티티 매니저
private final EntityManager em;
// 생성자로서 상태주입을 받는다.
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
이렇게 엔티티 매니저까지 상태주입을 받았다면 설정은 완료된 것이다. 이제 실제 DB와의 통신은 다음과 같이 이루어지게 된다.
// 1. INSERT
@Override
public Member save(Member member) {
em.persist(member);
return member;
}
// 2. SELECT * FROM `MEMBER` WHERE = ID
@Override
public Optional<Member> findById(Long id) {
// 엔티티매니저의 find 메소드 사용
// 엔티티클래스와 식별자(PK)를 인자로 넘겨서 사ㅏ용한다.
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
// 3. SELECT * FROM `MEMBER`
@Override
public List<Member> findAll() {
return em.createQuery("SELECT m FROM MEMBER as m", Member.class).getResultList();
}
// 4. SELECT * FROM `MEMBER` WHERE `name` = name
@Override
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name",name)
.getResultList();
return result.stream().findAny();
}
3번 예제에서는 JPQL이라는 것을 알고 있어야 한다. JPQL이란, 테이블이 아닌 엔티티객체를 대상으로 데이터를 조회하는 객체지향 쿼리이다. SQL을 추상화하여 특정 DB의 SQL에 의존하지 않도록 하기 위하여 사용된다.
이때, JPA는 JPQL을 분석한 후 적절한 SQL을 만들어 데이터베이스를 조회한다. 예제는 다음과 같다.
// 엔티티 클래스
@Entity(name = "Article")
public class Article {
@Column(name= "title")
private String title;
}
// JPQL
String jpql = "SELECT a FROM Article as a WHERE a.title = 'title'"
기본 분법은 SQL과 유사하다. SELECT문을 예시로 들었을 때,
# JPQL
SELECT a FROM Article AS a
## SELECT a FROM Articel a
## AS는 생략이 가능하다
# Article은 TABLE명이 아니라 ENTITY 클래스명이다.
JPQL은 별칭을 필수로 명시해주어야 한다, AS는 생략이 가능하다.
JPA를 사용함에 있어서 주의해야할점은, @Transactional 어노테이션을 사용해주어야 하는 것이다.
@Transactional
@Service
public class MemberService {
private final MemberRepository memberRepository;
...
JPA는 join이 실행될 때 모든 데이터 변경이 트랜잭션의 단위 안에서 이루어져야만 하기 떄문에 상기 예제와 같이 Transactional 이라는 어노테이션이 필요하다.
3. 결론
여기까지 JPA를 이용하여 DB에 접근하고 값을 CURD하는 예제를 작성하고 살펴보았다. 학습한 내용을 정리하기 위하여 작성한 글이라 내용이 산발적이지만, 후일 다시 읽었을 때 도움이 되었으면 한다.
'Framework > Spring' 카테고리의 다른 글
Spring 공부내용 정리 : Spring Bean 과 의존관계 (0) | 2021.08.05 |
---|---|
Spring 오류 정리 1. Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. (0) | 2021.08.05 |
Spring 공부내용 정리 : 3. Spring 에서의 JDBC #2 (0) | 2021.08.01 |
Spring 공부내용 정리 : 3. Spring 에서의 JDBC #1 (0) | 2021.08.01 |
Spring 공부내용 정리 : 2. Bean과 의존관계 (0) | 2021.07.29 |