Home [Spring] JPA 개념, 사용법
Post
Cancel

[Spring] JPA 개념, 사용법

JPA 개념

  • Java Persistence API 의 약자
    • JAVA에서 제공하는 API이다.
  • RDBMS와 OOP 객체 사이의 불일치에서 오는 패러다임을 해결하기 위해 만들어졌다.
  • ORM(Object-Relational Mapping) 기술이다.
    • 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스다.
    • JPA를 사용하기 위해서는 JPA를 구현한 ORM 프레임워크를 사용해야 한다. (Hibername, EclipseLink, DataNucleus 등)

springmvc

ORM

  • ORM이란 객체와 DB의 테이블이 매핑을 이루는 것 (객체가 테이블이 되도록 매핑시켜주는 것)

  • ORM을 이용하면 SQL Query가 아닌 직관적인 코드(메소드)로 데이터를 조작할 수 있다.

    • MySQLORM
      SELECT * FROM user;user.findAll()
  • 장점

    • 생산성이 높아진다. (간단한 메소드로 CRUD가 가능하다.)
    • 유지보수가 쉽다. (필드 변경 시 모든 SQL을 수정하지 않고 필드만 추가하면 된다.)
  • 단점

    • Query가 복잡해질 경우 ORM으로 표현하는 데 한계가 있다.
    • 성능이 raw query에 비해 느리다. (한 프로젝트 내에서Mybatis와 같이 사용하기도 한다.)

Spring Data JPA

  • Spring Data JPA는 JPA를 쉽게 사용하기 위해 스프링에서 제공하고 있는 프레임워크이다.

  • JPA를 한 단계 추상화시킨 Repository라는 인터페이스를 제공하면서 JPA를 더 쉽고 편하게 사용할 수 있도록 도와준다.

    • Repository 인터페이스에 정해진 규칙대로 메소드를 입력하면, Spring이 알아서 해당 메소드 이름에 적합한 쿼리를 날리는 구현체를 만들어서 Bean으로 등록해준다.

    • 추상화했다는 말은, Spring Data JPA로 Repository를 구현할 때 JPA를 사용하고 있다는 것이다.

      • Repository 인터페이스 기본 구현체인 SimpleJpaRepository의 코드에서 내부적으로 EntityManager를 사용하고 있다.

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        
        public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
        
            private final EntityManager em;
        
            public Optional<T> findById(ID id) {
        
                Assert.notNull(id, ID_MUST_NOT_BE_NULL);
                Class<T> domainType = getDomainClass();
                if (metadata == null) {
                    return Optional.ofNullable(em.find(domainType, id));
                }
                      
                LockModeType type = metadata.getLockModeType();
                Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
        
                return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
            }
        
            // Other methods...
        }
        

springmvc

  • JPA, Hibernate, Spring Data JPA의 전반적인 개념
  • 추상화 정도는 Spring Data JPA > Hibernate > JPA 이다.
    • Hibernate를 쓰는 것과 Spring Data JPA를 쓰는 것 사이에는 큰 차이가 없지만 구현체 교체의 용이성, 저장소 교체의 용이성 때문에 Spring Data JPA를 사용하는 것이 좋다.

스프링에서 JPA 사용하기

JPA 개발 환경 구성

  1. 라이브러리 설정

MAVEN 프로젝트 : pom.xml 에 의존성 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 <!-- H2 database -->
 <dependency>
     <groupId>com.h2database</groupId>
     <artifactId>h2</artifactId>
     <scope>runtime</scope>
 </dependency>
    
 <!-- 스프링부트용 Spring Data JPA 추상화 라이브러리 -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-jpa</artifactId>
 </dependency>
   
 <!-- Lombok 라이브러리 -->
 <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>1.18.10</version>
     <scope>provided</scope>
 </dependency>
  • h2
    • 인메모리 관계형 데이터베이스
    • 별도의 설치없이 프로젝트 의존성만으로 관리할 수 있다.
    • 메모리에서 실행되기 때문에 애플리케이션을 재시작할 때마다 초기화된다.
  • spring-boot-starter-data-jpa
    • 스프링 부트용 Spring Data JPA 추상화 라이브러리
    • 스프링 부트 버전에 맞춰 자동으로 JPA 관련 라이브러리들의 버전을 관리해준다.
  • lombok
    • 반복되는 getter, setter, tostring 등의 메서드 작성코드를 줄여주는 라이브러리

Gradle 프로젝트 : build.gradle에 의존성 추가

1
2
3
4
5
6
7
dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.projectlombok:lombok')
    compile('org.springframework.boot:spring-boot-stater-data-jpa')
    compile('com.h2database:h2')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}
  1. 환경 설정

application.properties 에 설정

1
2
3
4
5
6
7
8
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.username=sa
spring.datasource.password=1234
spring.datasource.driver-class-name=org.h2.Driver
  • spring.jpa.hibernate.ddl-auto
    • 애플리케이션 구동 시(실행 시) JPA의 데이터베이스 초기화 전략
      • none : 사용하지 않음
      • create : 기존 테이블 삭제 후 테이블 생성
      • create-drop : 기존 테이블 삭제 후 테이블 생성, 종료 시점에 테이블 삭제
      • update : 변경된 스키마 적용
      • validate : 엔티티와 테이블 정상 매핑 확인
  • spring.jpa.generate-ddl
    • true로 설정 시 @Entity가 명시된 클래스를 찾아서 DDL을 생성하고 실행
  • spring.jpa.show-sql
    • 콘솔창에 출력되는 쿼리를 가독성 좋게 포맷팅
  • spring.datasource
    • h2 데이터베이스 설정

Spring Data JPA 사용

  1. 엔티티 설계
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
@Table(name="member")
@Getter @Setter
@ToString
public class Member {

    @Id
    @Column(name="member_id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    
    @Column(unique = true)
    private String email;
}
  • @Entity
    • JPA 엔티티 클래스라는 것을 알려준다.
    • 속성
      • name : JPA에서 사용할 엔티티의 이름을 지정한다. 같은 클래스 이름이 없으면 가급적 기본값을 사용 (기본값 : 클래스이름)
    • Entity 클래스는 반드시 기본키를 가져야 한다. (@Id 어노테이션 사용)
  • @Table
    • 엔티티와 매핑할 테이블을 지정한다.
    • 생략 시 엔티티 이름을 테이블 이름으로 사용한다.
  • @Column
    • 필드와 컬럼 매핑
    • 속성
      • name : 필드를 매핑할 컬럼의 이름 설정
      • unique : 유니트 제약 조건 설정
  • @GeneratedValue
    • 기본키를 생성하는 전략(방법)
      • GenerationType_AUTO(default) : JPA 구현체가 자동으로 생성 전략 결정 (IDENTITY, SEQUENCE, TABLE 중 하나)
      • GenerationType.IDENTITY : 기본키 생성을 데이터베이스에 위임
      • GenerationType.SEQUENCE : 데이터베이스 시퀀스 오브젝트를 이용한 기본키 생성 (@SequenceGenerator를 사용하여 시퀀스 등록 필요-순차적으로 증가하는 값을 반환하는 데이터베이스 객체)
      • GenerationType.TABLE : 키 생성용 테이블 사용 (@TableGenerator 필요)
  1. Repository 설계
1
2
public interface MemberRepository extends JpaRepository<Member, Long>{
}
  • Repository는 JPARepository를 상속받는다.

    • JpaRepository<T, ID> : 제네릭은 사용하고자 하는 엔티티와 엔티티의 식별자 타입을 입력한다.
  • JPARepository는 기본적인 CRUD 및 페이징 처리를 위한 메소드가 정의되어 있다.

    메소드설명
    <S extends T> save(S entity)엔티티 저장 및 수정
    void delete(T entity)엔티티 삭제
    count()엔티티 총 개수 반환
    Iterable<T> findAll()모든 엔티티 조회
  1. 메인 어플리케이션 작성
  • 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  @Slf4j
  @SpringBootApplication
  public class JpaExam8Application {
        
      public static void main(String[] args) {
          SpringApplication.run(JpaExam8Application.class, args);
      }
            
      @Bean
      public CommandLineRunner Member(MemberRepository repository) {
          return (args) -> {
              //엔티티 생성
              Member member1 = new Member("홍길동");
              Member member2 = new Member("이순신");
              Member member3 = new Member("김철수");
                    
              //엔티티 저장
              repository.save(member1);
              repository.save(member2);
              repository.save(member3);
                    
              //엔티티 조회
              for(Member m : repository.findAll()) {
                  log.info("회원 명 : {}", m.getName());
              }
      
              //엔티티 삭제
              repository.delete(member2);
                    
              for(Member m : repository.findAll()) {
                  log.info("회원 명 : {}", m.getName());
              }
          };
      }
  }
  • Service Class 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
@Transactional
@RequiredArgsConstructor
public class MemberService implements UserDetailsService{

    private final MemberRepository memberRepository;

    public Member saveMember(Member member){
        validationDuplicateMember(member);
        return memberRepository.save(member);
    }

    private void validationDuplicateMember(Member member){
        Member findMember = memberRepository.findByEmail(member.getEmail());
        if(findMember != null){
            throw new IllegalStateException("이미 가입된 회원입니다.");
        }
    }
    
}    

JPA 기능

  1. 쿼리 메소드
  • 스프링 데이터 JPA에서 제공하는 핵심 기능 중 하나

  • 문법 예시 : find + (엔티티 이름) + By + 변수이름

    • 엔티티 이름 생략 가능
    • 호출 시 해당 변수를 조건으로하는 select 쿼리문 실행
  • Repository 인터페이스에 메소드를 추가하면 원하는 쿼리를 실행할 수 있다.

    1
    2
    3
    
    public interface MemberRepository extends JpaRepository<Member, Long>{
        Member findByEmail(String email);
    }
    
  1. @Query 어노테이션
  • JPQL(Java Persistence Query Language) 라는 객체지향 쿼리 언어를 통해 복잡한 쿼리도 처리가 가능하다.

    • SQL과 문법이 유사하기 때문에 쉽게 배울 수 있다.
    • 엔티티 객체를 대상으로 쿼리를 수행한다.
    • 테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리이다.
    • SQL을 추상화해서 사용하기 때문에 특정 데이터베이스 SQL에 의존하지 않는다. (데이터베이스가 변경되어도 영향받지 않는다.)
    1
    2
    3
    4
    
    public interface MemberRepository extends JpaRepository<Member, Long>{
        @Query("select m from Member m where m.email = :email")
        Member findByEmail(@Param("email") String email);
    }
    
  • @Param 어노테이션 : 파라미터로 넘어온 값을 JPQL에 들어갈 변수로 지정한다.

  • nativeQuery 속성 : 기존 데이터베이스에서 사용하던 쿼리를 그대로 사용해야할 때 사용한다.

    • 특정 DB에 종속되는 쿼리를 사용하게 되므로 DB에 대해 독립적이라는 장점을 잃는다.
  1. @NamedQuery 어노테이션
  • 엔티티

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    @Entity
    @Table(name="member")
    
    @NamedQuery(
    	name = "member.findByEmail"
    	query = "select m from Member m where m.email = :email"
    )
    
    @Getter @Setter
    @ToString
    public class Member {
    
        @Id
        @Column(name="member_id")
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        private String name;
          
        @Column(unique = true)
        private String email;
    }
    
  • Repository

    1
    2
    3
    
    public interface MemberRepository extends JpaRepository<Member, Long>{
        Member findByEmail(@Param("email") String email);
    }
    
  1. Querydsl
  1. 페이징 처리
  • Pageable 이용

출처📎

This post is licensed under CC BY 4.0 by the author.