반응형
작성된 모든 소스는 github에 있습니다.
이번글에서는 JPA 설정부터 테스트코드(Junit5) 까지 작성하였습니다.
다음글에서 JPA 테스트 코드 기준으로 작동 원리에 대해서 정리하겠습니다.
프로젝트 생성
gradle
dependencies를 제외한 설정은 default값 그대로입니다.
application.yaml
spring:
profiles:
active: local
--- #local
spring:
profiles: local
datasource:
url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
username: root
password: vnfmsqka0727!
driver-class-name: com.mysql.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
database: mysql
generate-ddl: false
open-in-view: false
show-sql: true
hibernate:
ddl-auto: create # 항상 조심!!
properties:
hibernate:
show_sql: true
format_sql: true
use_sql_comments: true
logging:
level:
org:
hibernate:
SQL: DEBUG
type:
trace
- datasource : database 설정값을 세팅한다.
- jpa.database-platform : platform 설정
- jpa.open-in-view : 영속성을 어느 범위까지 설정할지 결정. 참조
- jpa.show-sql : 실행하는 쿼리 show
- jpa.hibernate.ddl-auto: 톰캣 기동할 때 어떤 동작을 할지 결정
- 해당 설정을 잘못하면 테이블이 drop될수 있다.
- 한 번 설정이 끝났다면 none, validate 로 설정하는 것을 추천한다.
- jpa.properties.hibernate.format_sql : 쿼리를 이쁘게 보여준다.
- logging.level.org.hibernate.SQL : 로그 레벨 설정
Configuration
package com.example.queyrdsl.configuration;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Configuration
public class DataBaseConfiguration {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
- EntityManagerFactory
- 여기선 사용하지 않았다.
- request가 올 때 EntityManager를 생성한다.
- JPAQueryFactory
- JPAQuery를 만들어서 사용하는 방식
- EntityManager를 통해서 질의가 처리된다.
- 사용하는 쿼리문은 JPQL
- 최종 목표는 JPA와 QueryDSL이기 때문에 사용
- EntityManager
- Transaction 단위로 생성된다.
- DB connection pool을 사용한다.
- Transaction이 끝나면 버린다. 다른 Thread와 공유하면 안된다.
- PersistenceContext
- persistence 뜻 : 지속하다.
- 위 소스코드 기준으로는 EntityManager를 영구 저장하겠다. 로 쓰인다.
Database schema 생성
github 소스의 testdb-schema.sql 참조
-- auto-generated definition
create table store
(
id bigint auto_increment
primary key,
name varchar(20) null,
address varchar(100) null
);
-- auto-generated definition
create table staff
(
id bigint auto_increment
primary key,
store_id bigint null,
name varchar(10) null,
age int null
);
Entity 생성
package com.example.queyrdsl.entity;
import lombok.*;
import javax.persistence.*;
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Staff {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
@Builder
public Staff(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
}
package com.example.queyrdsl.entity;
import lombok.*;
import javax.persistence.*;
import java.util.Collection;
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Store {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "store_id")
private Collection<Staff> staff;
@Builder
public Store(Long id, String name, String address, Collection<Staff> staff) {
this.id = id;
this.name = name;
this.address = address;
this.staff = staff;
}
}
- Database Table마다 Entity를 생성한다.
- Entity에는 Setter를 사용하지 않는다.
- 객체의 일관성을 보장할 수 없다.
- 객체를 생성할 때는 Builder를 사용하자.
Setter는 되도록이면 사용하지 않는 것이 좋다. 객체의 일관성을 유지하는데 문제가 생길 수 있다.
- @NoArgsConstructor(access = AccessLevel.PROTECTED)
- 생성자를 protected로 변경하면 new Store() 사용을 막을 수 있으므로 일관성을 유지하기 용이하다.
- @Id
- table column의 private key 설정
- GeneratedValue(strategy = GenerationType.IDENTITY
- 기본키 생성 방법
- IDENTITY : 기본키 생성을 데이터베이스에 위임
- SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본키 할당
- TABLE : 키 생성 테이블을 사용
- AUTO : 데이터베이스에 따라서 IDENTITY, SEQUENCE, TABLE 방법 중 하나를 자동으로 선택
- @OneToMany
- 일 대 다 관계 설정
- @JoinColumn 기준으로 관계 설정
mappedBy를 사용해도 무관하다.
Repository 생성
package com.example.queyrdsl.repository;
import com.example.queyrdsl.entity.Staff;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StaffRepository extends JpaRepository<Staff, Long> {
}
package com.example.queyrdsl.repository;
import com.example.queyrdsl.entity.Store;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StoreRepository extends JpaRepository<Store, Long> {
Store findByName(String name);
}
- Database table마다 생성 ( Entity와 1:1 )
- JpaRepository를 상속받아서 사용한다.
- <Entity, ENTITY_ID>
Test
package com.example.queyrdsl.jpa;
import com.example.queyrdsl.entity.Staff;
import com.example.queyrdsl.entity.Store;
import com.example.queyrdsl.repository.StoreRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@ActiveProfiles("local")
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class JpaTest {
@Autowired
private StoreRepository storeRepository;
@Test
void entity저장후조회() {
//given
final Long id = 1L;
final String storeName = "스토어1";
final String storeAddress = "주소1";
Store store = Store.builder()
.id(id)
.name(storeName)
.address(storeAddress)
.build();
storeRepository.save(store);
//when
Store resultStore = storeRepository.findByName(storeName);
//then
Assertions.assertEquals(resultStore.getName(), storeName);
}
@Test
@DisplayName("초기 테스트 시 entity저장후조회 테스트 먼저 진행 후 테스트.")
void entity수정() {
//given
final Long id = 1L;
final String storeName = "스토어2";
final String storeAddress = "주소2";
Store store = Store.builder()
.id(id) // id 가 같으면 수정처리함
.name(storeName)
.address(storeAddress)
.build();
//when
Store udpateStore = storeRepository.save(store);
//then
Assertions.assertEquals(udpateStore.getName(), storeName);
}
@Test
@DisplayName("Store, Staff entity 저장")
void entity저장() {
//given
final Long storeId = 2L;
final String storeName = "store1234";
final String storeAddress = "storeAddress";
final Long staffId = 1L;
final String staffName = "staff1234";
final Integer staffAge = 30;
Staff staff = Staff.builder()
.id(staffId)
.name(staffName)
.age(staffAge)
.build();
Store store = Store.builder()
.id(storeId)
.name(storeName)
.address(storeAddress)
.staff(Arrays.asList(staff))
.build();
//when
Store saveStore = storeRepository.save(store);
//then
Assertions.assertEquals(saveStore.getName(), storeName);
Collection<Staff> staff1 = saveStore.getStaff();
Iterator<Staff> iterator = staff1.iterator();
Staff next = iterator.next();
Assertions.assertEquals(next.getName(), staffName);
}
}
다음글에서 JPA 테스트 코드 기준으로 작동 원리에 대해서 정리하겠습니다.
반응형
'Develop > spring-data' 카테고리의 다른 글
Spring Data JPA 기반 Querydsl plugin 실행방법 (0) | 2020.11.07 |
---|---|
Spring Data JPA 기반 Querydsl 사용해보자. ( 설정편 ) (0) | 2020.11.07 |
Spring Data JPA 기반 Querydsl란? (0) | 2020.11.06 |
JPA 테스트 코드로 알아보자! (0) | 2020.11.05 |
Spring Data JPA란? (0) | 2020.11.05 |