오늘은 지난번에 알아보았던 JPA를 스프링에서 어떻게 적용하는 지 함께 알아보겠습니다.
저는 Spring Boot를 이용해서 실습을 진행해보겠습니다.
본 실습은 jojoldu님의 블로그를 보면서 진행했습니다. (https://jojoldu.tistory.com/251?category=635883)
먼저 저는 maven을 사용하므로 pom.xml에 관련 dependency를 넣어주겠습니다!
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
data jpa는 JPA관련이고, spring-boot-starter-test는 테스트 관련인데 H2는 무엇인지 들어보셨나요?
H2
는 MySQL, oracle, 등과 같은 평범한 DB이지만, 초경량이라는 점과 In Memory DB 를 지원하는 점이 특징입니다.
그래서 타 DB에 비해 속도가 빠르지만 지속성이 없기때문에?( 컴퓨터 종료 혹은 메모리 종료시 내용이 날라감.) 테스트용으로 매우 좋고, 캐싱용으로도 많이 사용된다고 합니다. 더 자세한 설명은 Google 혹은위키로.....
다음은 lombok이라는 라이브러리도 들어보신 분도 많겠지만, 아직 모르는 분도 계실 거라고 생각합니다. lombok 은 자바에서 클래스를 생성할 때 보통 반복적으로 적게되는 ToString, Getter, Setter, 등을 Annotation 하나로 간단히 처리해줍니다.
@Getter
@Setter
public class User {
private String userName;
private String userAge;
}
이런 식으로 작동한답니다.
lombok은 다른 라이브러리와 다르게 dependency만 추가해준다고 바로 적용되지않고, IDE에 적용을 추가적으로 해줘야하는데, 이 설치법과 lombok을 사용할 때 주의사항은 Hyoj 님의 블로그에 설명이 매우 잘되어있어서 링크로 대체하겠습니다.
이제 드디어 JPA를 테스트해보기위한 Entity클래스를 하나 만들어보겠습니다!
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
@Getter
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 20, nullable = false)
private String name;
@Column(length = 20, nullable = false, unique = true)
private String phone;
@Builder
public Customer(String name, String phone) {
this.name = name;
this.phone = phone;
}
}
Annotation이 너무 많아서 처음엔 조금 헷갈리실 수도 있습니다. 혹시 lombok을 사용하지 않으시는 분들은 코드내의 @ToString, @Getter, @Builder
를 빼고 직접 생성해주시고 나서 진행해주세요!
Customer 에는 총 3개의 필드가 존재합니다.
id
필드는 @id
를 사용하여 기본키(PK
)로 지정합니다. 이때 키를 직접할당하는 방식이 아닌, 자동으로 생성되도록 하기위해 @GeneratedValue
를 사용합니다.
GenerationType.IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 방식이고, 다른 방식들은
정도가 있습니다.
name, phone
필드의 @Column
Annotation은 데이터베이스 컬럼으로 지정해줍니다. 안에 length
와 nullable
또는 unique
와 같은 설정들이 가능합니다. 이외에도 다영한 설정이 들어갈 수 있습니다.
또한, @Column
을 생략할 경우 필드명을 사용하여 컬럼명과 매핑하기 때문에 만약 DB가 대소문자를 구분하는 경우에는 반드시 @Column
Annotation을 사용하는 것이 좋습니다.
JPA에서는 단순히 Repository 인터페이스를 생성한후 JpaRepository<Entity, 기본키 타입> 을 extends하면 기본적인 Create, Read, Update, Delete가 자동으로 생성됩니다! 그래서 저흰 그냥 인터페이스를 만들고, 상속만 잘해주면 기본적인 동작을 테스트해볼 수 있는거죠 :D
@Repositoryd
public interface CustomerRepository extends JpaRepository<Customer, Long>{
//비워있어도 잘 작동함.
// long 이 아니라 Long으로 작성. ex) int => Integer 같이 primitive형식 사용못함
}
이제 JUnit 을 이용하여 테스트를 작성해봅니다. 테스트 코드는
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomerRepositoryTests {
@Autowired
CustomerRepository customerRepository;
@Test
public void testCustomerRepository(){
Customer customer = Customer.builder().name("크리스").phone("010-1224-1225").build();
customerRepository.save(customer);
List<Customer> customerList = customerRepository.findAll();
Customer chris = customerList.get(0);
assertThat(chris.getName(), is("크리스"));
assertThat(chris.getPhone(), is("010-1224-1225"));
}
@After
public void deleteAll() {
customerRepository.deleteAll();
}
}
이런 식으로 작성되어 있습니다.
테스트 클래스를 우클릭 후 Run As 에서 JUnit Test를 눌러서 테스트를 진행하였더니,
테스트에 성공하였습니다! :-)
이번에는 CustomerRepository
에 수정을 가해서 테스트 코드의 findAll()
부분을 findByName 혹은
findByPhone`으로 바꿔보기위해 CustomerRepository
부분을 수정해보겠습니다.
쿼리를 유추할 수 있는 메소드의 이름으로 쿼리를 정의하여주면 자동으로 이에 맞는 쿼리를 실행하여 줍니다.
먼저 CustomerRepository
를 수정합니다.
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long>{
public List<Customer> findByName(String name);
public List<Customer> findByPhone(String phone);
//like검색도 가능
public List<Customer> findByNameLike(String keyword);
}
findBy뒤에 컬럼명을 붙여주면 이를 이용한 검색이 됩니다. 따로 내부 구현을 하지 않아도 알아서 마법처럼 작동합니다!
이외에도 메소드 생성시 다양한 키워드들을 지원하는 데 대표적으로는
이번에는 JUnit 이 아니라 PostMan을 이용해서 작성한 메소드들이 작동하는 지 확인해보겠습니다.
먼저 Controller
를 작성해야겠죠?
@RestController
@EnableAutoConfiguration
@RequestMapping(value = "/customer")
public class CustomerController {
@Autowired
CustomerRepository customerRepository;
@PostMapping("/")
public @ResponseBody List<Customer> createCustomer(@RequestBody Map<String,String> param){
String name = param.get("name");
String phone = param.get("phone");
Customer customer = Customer.builder().name(name).phone(phone).build();
customerRepository.save(customer);
return customerRepository.findAll();
}
}
이렇게 확인을 위해 간단히 만들어줬습니다. 이제 PostMan으로 요청을 보내보겠습니다.
이렇게 응답이 오는 것을 확인할 수 있습니다. 이 상태에서 phone
은 유니크이므로 변경한 후 한번 더 요청을 보내면 id
가 2
인 데이터가 하나 더 생겨있겠죠?
저희가 원하는 결과가 나온 걸 확인할 수 있습니다!
다른 메소드들도 테스트해보면 잘 작동합니다.
직접 사용해보니 JPA가 MyBatis 에 비하여 왜 더 생산속도가 빠른지 느낄 수 있었습니다.
긴 글 읽어주셔서 감사합니다! :D
모두 좋은하루 되세요
샘플코드는 여기서 보실 수 있어요!
https://github.com/junwoochoi/spring-jpa-practice-sample
아직 모르는게 많아 게시글에 잘못된 정보가 있을 수 있습니다. 혹시 잘못된 정보가 있다면, 댓글 혹은 메일로 알려주시면 최대한 빨리 수정하겠습니다!
저는 @Builder
public User(String name, String phone) {
this.name = name;
this.phone = phone;
}
이렇게 builder도 다 만들었는데 .builder()를 사용하려고 하면 없어요 ㅠㅠ 빨간줄이 뜨네요 ㅠㅠㅜ 이유가 뭘까요? 구글 다 뒤져도 저같은 분이 안계시네요 ㅠㅜ