JpaRepository 인터페이스는 뭘까? 그리고 Optional<T>(제너럴)는 어디에 사용되지?

·2023년 7월 30일
0

프로젝트 공부

목록 보기
25/33

JpaRepository = Repository 인터페이스

iBatis나 MyBatis 등에서 Dao라고 불리는 DB Layer 접근자로, JPA에서는 Repository라고 부르며 인터페이스로 생성한다

인터페이스 생성 후, JpaRepository<Entity 클래스, PK 타입>을 상속하면
기본적인 CRUD 메소드가 자동으로 생성된다.

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostsRepository extends JpaRepository<Users, Long> {
}

JpaRepository 설명

  • JpaRepository는 PagingAndSortingRepository, QueryByExampleExecutor 인터페이스를 상속받고 있음
  • PagingAndSqortingRepository는 CrudRepository 인터페이스를 상속받고 있음

기본적으로 제공하는 메소드

  • CrudRepository 인터페이스에는 기본적인 CRUD 메소드 제공
    save(), findById(), existsById(), count(), deleteById(), delete(), deleteAll()
  • QueryByExampleExecutor 인터페이스에는 더 다양한 CRUD 메소드 제공
    findOne(), findAll(), count(), exists()

이 외에도 Optional<Users> findById(Long id);와 같은 형태로 메소드를 커스텀할 수 있다

참고 - 🔗JPARepository - 메소드 커스텀하기

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostsRepository extends JpaRepository<Users, Long> {
	Optional<Users> findById(Long id);
}

Optional<T>란?

Java8에서부터 나타났으며 null이 올 수 있는 값을 감싸는 Wrapper 클래스다.
Optional을 사용하면 참조하더라도 NullPointException이 발생하지 않도록 도와주기 때문에 null검사를 위한 코드를 줄여, 코드를 간결하게 만들 수 있다.

public final class Optional<T> {

  // If non-null, the value; if null, indicates no value is present
  private final T value;
   
  ...
}

올바른 Optional 사용법

  • Optional 변수에 Null을 할당하지 말아라
  • 값이 없을 때 Optional.orElseX()로 기본 값을 반환하라
  • 단순히 값을 얻으려는 목적으로만 Optional을 사용하지 마라
  • 생성자, 수정자, 메소드 파라미터 등으로 Optional을 넘기지 마라
  • Collection의 경우 Optional이 아닌 빈 Collection을 사용하라
  • 반환 타입으로만 사용하라

링크 참고 - [Java] 언제 Optional을 사용해야 하는가? 올바른 Optional 사용법 가이드 - (2/2)

값이 Null인 경우

Optional은 Wrapper 클래스이기 때문에 값이 없을 수도 있는데, 이때는 Optional.empty()로 생성할 수 있다.

Optional<String> optional = Optional.empty();

System.out.println(optional); // Optional.empty
System.out.println(optional.isPresent()); // false

Optional 클래스는 내부에서 static 변수로 EMPTY 객체를 미리 생성해서 가지고 있다. 이러한 이유로 빈 객체를 여러 번 생성해줘야 하는 경우에도 1개의 EMPTY 객체를 공유함으로써 메모리를 절약하고 있다.

public final class Optional<T> {

    private static final Optional<?> EMPTY = new Optional<>();
    private final T value;
    
    private Optional() {
        this.value = null;
    }

    ...

}

값이 Null이 아닌 경우

어떤 데이터가 절대 Null이 아니라면 Optional.of()로 생성할 수 있다.
만약 Optional.of()로 Null을 저장하려고 하면 NullPointerException이 발생한다.

// Optional의 value는 절대 null이 아니다.
Optional<String> optional = Optional.of("MyName");

값이 Null일수도, 아닐수도 있는 경우

만약 어떤 데이터가 null이 올 수도 있고 아닐 수도 있는 경우에는 Optional.ofNullable()로 생성할 수 있다.
그리고 이후에 orElse 또는 orElseGet 메소드를 이용해서 값이 없는 경우라도 안전하게 값을 가져올 수 있다.

String isNull;
String name;
        
isNull = "loose";
name = Optional.ofNullable(isNull).orElse("test");

System.out.println(name); //isNull값이 null이 아니므로 "loose" 출력

isNull = null;
name = Optional.ofNullable(isNull).orElse("test");

System.out.println(name); //isNull값이 null이므로 "test" 출력

orElse~의 차이와 사용법

기본적으로 orElse~는 Optional에 올 값이 null인 경우 orElse~ 안에 있는 내용을 실행시킨다
=> orElse~는 if문을 이용해 처리하는 명령어를 짧게 람다식처럼 처리 가능

orElse~는 Spring Data JPA에서 가장 잘 사용된다.

Optional<User> user = userRepository.findById(id); //정상
User user = userRepository.findById(id); //에러
Spring Data JPA 메소드는 return 값이 Optional 로 이루어져있다.

그러므로 위처럼 Optional 타입으로 받아야 정상 처리된다.

String name1 = Optional.ofNullable("test1").orElse("test2"); // 맞음
Optional name1 = Optional.ofNullable("test1").orElse("test2"); // 틀림

하지만 orElse~를 사용하는 경우 null이 아닐 시 Optional이 아닌 Optional의 인자가 반환된다

❗orElse와 orElseGet의 차이

  • orElse
    • 파라미터로 값을 필요로 한다
    • 값이 미리 존재하는 경우에 사용한다.
    • 매개변수에 값을 넘긴다
  • orElseGet
    • 파라미터로 함수(함수형 인터페이스)를 필요로 한다
    • 값이 미리 존재하지 않는 거의 대부분의 경우에 orElseGet을 사용하면 된다.
    • 매개변수에 람다식을 넘긴다

orElse와 orElseGet의 예시

String name1 = Optional.ofNullable("loose").orElse("test");
String name2 = Optional.ofNullable("loose").orElseGet(() -> "test");

System.out.println("name1 = " + name1);
System.out.println("name2 = " + name2);

//출력 결과
//name1 = loose
//name2 = loose

orElse와 orElseGet의 정리

orElse는 매개변수가 고정 값일 때 사용하고 orElseGet은 매개변수가 메소드일 때 사용하는 것이 좋다.

orElse가 orElseGet에 비해 좋은 장점

  • orElseGet보다 짧아 가독성이 좋다.
  • orElse는 클린 코드 측면에서 일반 변수를 전달하기 때문에 람다식에 비해서 직관적이다.
  • Supplier 객체 만들지 않기 때문에 공간의 낭비가 없다.

위의 장점처럼 orElse는 orElseGet에 비해 장점이 크지 않아서 대부분의 상황에선 orElseGet을 사용하기도 한다.

❗orElseThrow의 사용법

User user = userRepository.findById(id)
        .orElseThrow(() -> new IllegalArgumentException("user doesn't exist");

orElseThrow는 Optional의 인자가 null일 경우 예외처리를 시킨다.

Optional 예시코드

1

//lambda 이용
List<String> nameList = Optional.ofNullable(getNames())
    .orElseGet(() -> new ArrayList<>());

getNames()로 값을 가져왔을 때, null인 경우 .orElseGet()가 실행되고, 람다 함수를 통해 nameList가 새로 만들어진다.

2

예를 들어 아래와 같은 우편번호를 꺼내는 null 검사 코드가 있다고 하자. 이 코드는 null 검사 때문에 상당히 복잡하다.

public String findPostCode() {
    UserVO userVO = getUser();
    if (userVO != null) {
        Address address = user.getAddress();
        if (address != null) {
            String postCode = address.getPostCode();
            if (postCode != null) {
                return postCode;
            }
        }
    }
    return "우편번호 없음";
}

하지만 Optional을 사용하면 이러한 코드를 아래와 같이 표현할 수 있다.

public String findPostCode() {
    // 위의 코드를 Optional로 펼쳐놓으면 아래와 같다.
    Optional<UserVO> userVO = Optional.ofNullable(getUser());
    Optional<Address> address = userVO.map(UserVO::getAddress);
    Optional<String> postCode = address.map(Address::getPostCode);
    String result = postCode.orElse("우편번호 없음");

    // 그리고 위의 코드를 다음과 같이 축약해서 쓸 수 있다.
    String result = user.map(UserVO::getAddress)
        .map(Address::getPostCode)
        .orElse("우편번호 없음");
}

3

예를 들어 아래와 같이 이름을 대문자로 변경하는 코드에서 NPE 처리를 해준다고 하자.

String name = getName();
String result = "";

try {
    result = name.toUpperCase();
} catch (NullPointerException e) {
    throw new CustomUpperCaseException();
}

위의 코드는 다소 번잡하고 가독성이 떨어지는데 이를 Optional를 활용하면 아래와 같이 표현할 수 있다.

Optional<String> nameOpt = Optional.ofNullable(getName());
String result = nameOpt.orElseThrow(CustomUpperCaseExcpetion::new)
                  .toUpperCase();

참고
[Spring JPA]JpaRepository 기본 사용법
[Java] Optional이란? Optional 개념 및 사용법 - (1/2)
Optional의 orElse, orElseGet, orElseThrow 사용법

profile
개발자가 되고싶은 낭랑 24세

0개의 댓글

관련 채용 정보