이전에 작성했던 아래 글을 읽고 오면 이해하는데 더 도움이 된다.
https://hevton.tistory.com/780
@Entity는 테이블이다.
이렇게 하게 되면 자동적으로 Create Table이 된다.
기본적으로 클래스명이 테이블명이 되지만, @Table(name="")으로 테이블명을 따로 지정해 줄 수도 있다.
import com.google.gson.Gson;
import lombok.Builder;
import lombok.Getter;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.UUID;
// @Entity가 붙은 클래스는 JPA가 관리하는 클래스이고, 테이블과 매핑할 테이블에 해당 어노테이션을 붙인다.
@Getter
@Entity
public class User {
// @ID 어노테이션은 Primary key를 뜻함.
@Id
String id;
String name;
String email;
public User() {
this.id = UUID.randomUUID().toString();
}
}
이제 Repository를 만들어준다.
package com.test.admin.repository;
import com.test.admin.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
// JpaRepository<엔티티명, PK 타입>
@Repository
public interface UserRepository extends JpaRepository<User, String> {
//비워있어도 잘 작동한다. 기본적인 CRUD는 자동으로 추가된다.
// findBy뒤에 컬럼명을 붙여주면 이를 이용한 검색이 가능하다.
public List<User> findByName(String name);
// PK에 대한 find는 기본적으로 추가되어 있음.
// public List<MemberVo> findById(String id);
}
extends JpaRepository<엔티티명, PK 타입> 을 extends 하는 interface를 정의만 해주면
기본적인 CRUD가 자동으로 생성되므로, 인터페이스 내부를 비워놓아도 잘 작동한다.
JpaRepository 인터페이스를 상속하는 것만으로도 기본적인 CRUD 기능을 사용할 수 있다.
제네릭 타입으로 내부 기능들이 자동적으로 정의되어 있기 때문이다.
또한, PK로 설정한 Id에 대해서도 findById, deleteById 등 기능들이 기본적으로 추가되지만
그 이외의 필드에 대한 쿼리는 findBy~, deleteBy~ 로 함수의 이름만 interface에 등록 해주면 자동적으로 쿼리가 동작한다.
만약 Entity의 필드 중에 firstname, lastname, name, sal 이라는 필드들이 있다면
And => findByLastnameAndFirstname (EX. where x.lastname = ?1 and x.firstname = ?2)
Or => findByLastnameOrFirstname (EX. where x.lastname = ?1 or x.firstname = ?2)
Is, Equals => findByName,findByNameIs,findByNameEquals (EX. where x.name = 1?)
Between => findBySalBetween(EX. where x.sal between 1? and ?2)
Like => findByFirstnameLike (EX. where x.firstname like ?1)StartingWith => findByFirstnameStartingWith (EX. where x.firstname like ?1)Containing => findByFirstnameContaining (EX. where x.firstname like ?1)OrderBy => findByAgeOrderByLastnameDesc (EX. where x.age =?1 order by x.lastname desc)...이외에도 꽤 많다.
이렇게 메서드 이름을 UserRepository interface에 등록해주면, 쿼리 기능까지 자동으로 등록된다.
하지만 이런 정의된 것들 말고 복잡한 커스텀 쿼리는 직접 작성해주어야 한다.
이 인터페이스를 구현한 구현체를 Spring Data JPA가 제공해준다.
그 시점은, Service에서 Repository를 주입할 때이다.
package com.test.admin.service.logic;
import com.test.admin.entity.User;
import com.test.admin.repository.UserRepository;
import com.test.admin.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
// UserServiceLogic도 bean 객체로 등록하기 위해 @Service
@Service
@RequiredArgsConstructor
public class UserServiceLogic implements UserService {
// DI 적용 방법 1
// UserRepository를 구현한 구현클래스 UserRepositoryLogic을 찾아서 userRepositry 변수에 주입함.
private final UserRepository userRepository; // @RequiredArgsConstructor와 세트
@Override
public String register(User newUser) {
return this.userRepository.save(newUser).getId();
}
@Override
public List<User> findByName(String name) {
return this.userRepository.findByName(name);
}
@Override
public void deleteById(String id) {
userRepository.deleteById(id);
}
// PK인 id 그대로 두고 나머지 수정하면 update
@Override
public User update(User newUser) {
User user = userRepository.findById(newUser.getId()).get();
return userRepository.save(new User(user.getId(), newUser.getName(), newUser.getEmail()));
}
@Override
public List<User> findAll() {
return this.userRepository.findAll();
}
}
save()함수의 경우에는, 새로 저장하는 것 뿐 아니라 update의 기능까지 한다.
우리가 Entity에서 PK로 지정해 놓은 부분이 여기서 중요하다.
우리가 save()를 하게 되면, Spring Data JPA는 해당 PK를 가진 데이터가 테이블에 존재하는지 확인하고
없으면 새로 insert를 하는 것이고, 이미 있다면 update를 하게 된다.
findById의 경우, 리턴되는 데이터가 Optional<User> 이 되는데, 이는 Id가 PK이기 때문에 리턴되는 것이 List와 달리 하나일 경우 리턴되는 타입이다. 값이 있을 수도 없을 수도 있기 때문에 Optional이라는 뜻이다.
위에서는, 제가 테스트하는 경우이기에 데이터가 있음을 보장하기 때문에 바로 .get()으로 Optional<User>에서 User를 받아오는데
데이터가 없을 경우에 Null Pointer Exception이 발생할 수 있기 때문에, 실제 이용에서는 분기할 필요가 있다.
예를 들어
Optioanl<User> user = userRepository.findById(id);
if(!user.isPresent()) { // 또는 user.orElseThrow로 예외처리를 할 수도 있다.
}
이런식으로 분기해주는 것이 안전하다.
'[SpringBoot]' 카테고리의 다른 글
JPA update 할 때 save 안해도 되는 이유 (0) | 2023.03.25 |
---|---|
타임리프 (Thymeleaf) (0) | 2022.12.27 |
JPA 설명 -2 / Spring Data JPA (1) | 2022.12.20 |
JPA - OneToMany (0) | 2022.12.20 |
JPA 설명 -1 / JDBC (0) | 2022.12.15 |