0+ 스프링/0 + 스프링 ORM(JPA)

[JPA] JPA 기초

힘들면힘을내는쿼카 2023. 2. 8. 15:35
728x90
반응형

[JPA] JPA 기초

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
이 글은 인프런에서 제공하는 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 참고했고
강의 내용을 다시 복습하면서 정리하려는 목적으로 작성합니다.

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

 

 

과거에는 객체를 DB에 저장하려면 👨🏼‍💻개발자는 JDBC API를 이용하여 SQL을 직접 작성해야 했습니다.

public class MemberDAOV1 {

    // 회원 저장
    public Long save(Connection connection, Member member) throws SQLException {
        PreparedStatement pstmt = null;
        String sql = "INSERT INTO MEMBER(USERNAME, AGE) VALUES (?, ?)";

        try {
            pstmt = connection.prepareStatement(sql);
            pstmt.setString(1, member.getUsername());
            pstmt.setInt(2, member.getAge());

            pstmt.executeUpdate();

            ResultSet generatedKeys = pstmt.getGeneratedKeys();

            if(generatedKeys.next()) {
                return generatedKeys.getLong(1);
            }

            return null;

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            pstmt.close();
        }
    }

    // 회원 조회
    public Member findMember(Connection connection, Long id) throws SQLException {
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        String sql = "SELECT * FROM MEMBER WHERE ID = ?";

        try {
            pstmt = connection.prepareStatement(sql);
            pstmt.setLong(1, id);

            rs = pstmt.executeQuery();

            if(rs.next()) {
                return Member.builder()
                        .id(rs.getLong("MEMBER_ID"))
                        .username(rs.getString("USERNAME"))
                        .age(rs.getInt("AGE"))
                        .build();
            }

            return null;

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            pstmt.close();
        }
    }
}

 

그런데 JDBC TemplateMybatis 같은 SQL Mapper가 등장합니다…!!

 

public class MemberDAOV2 {
    private JdbcTemplate jdbcTemplate;
    private SimpleJdbcInsert insertActor;

    // 회원 저장
    public Long save(final Member member) {
        SqlParameterSource parameterSource = new MapSqlParameterSource()
                .addValue("USERNAME", member.getUsername())
                .addValue("AGE", member.getAge());

        Number newId = insertActor.executeAndReturnKey(parameterSource);
        return newId.longValue();
    }

    // 회원 조회
    public Member findMember(Long id) {
        String sql = "SELECT MEMBER_ID, USERNAME, AGE FROM MEMBER WHERE ID = ?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(), id);
    }
}

코드는 많이 줄었지만 개발자가 여전히 직접 SQL을 작성해야 했습니다. 😿

그러다가 JPA가 등장합니다.

 

public class MemberDAOV3 {

    @PersistenceContext
    EntityManager em;

      // 회원 저장
    public void save(Member member) {
        em.persist(member);
    }

      // 회원 조회
    public Member findMember(Long id) {
        return em.find(Member.class, id);
    }
}

JPA가 엄청나다는 것을 바로 느낄 수 있습니다…… ㄷ ㄷ
JPA를 도입하면 개발 속도가 엄청나게 증가할것 같습니다.
이러한 이점으로 인해 개발자는 핵심적인 비지니스 로직에 대해서 더 고민할 수 있는 시간이 생기겠죠? 🤗

 

 

그런데 JPA를 막상 사용하려고 하면 개발자들은 난관에 부딪힙니다.
그 이유는 다음과 같습니다.

  • 객체와 테이블을 올바르게 매핑하는 방법을 모른다. 🤷‍♀️
    • 실무는 수십 개 이상의 복잡한 객체와 테이블을 사용합니다.
  • JPA 내부 동작 방식이 어떻게 돌아가는지 모른다. 🤷🏻‍♂️
    • JPA가 어떤 SQL을 만들어 내는지 정확하게 이해하고 있어야 합니다.
    • 제대로 알지 못하면 장애 상황에 대처하지 못합니다.

그래서 이 포스팅 시리즈에서는 객체와 테이블 설계 매핑에 관해서 집중적으로 말씀드릴려고 합니다.

목표

  • 객체와 테이블을 제대로 설계하고 매핑하는 방법
    • 기본 키(PK)와 외래 키(FK) 매핑
    • 일대다(1:N), 다대일(N:1), 일대일(1:1), 다대다(M:N) 매핑
  • JPA 내부 동작 방식
    • JPA 내부 동작 방식 이해
    • JPA가 어떤 SQL을 만들어 내는지 이해
    • JPA가 언제 SQL을 실행하는지 이해

JPA

JPAJava Persistence API의 약자 이고, JAVA 진영의 ORM 기술 표준 입니다.
그리고 인터페이스의 모음 입니다.

그렇다면 ORM이란 무엇일까요?
Object-Relational Mapping의 약자 입니다.
한국어로 객체 관계 매핑 정도로 해석할 수 있습니다.

JPA 장점

JAVA진영의 ORM표준인 JPA를 사용하면 다음과 같은 장점이 있습니다.

객체는 객체대로 설계, 관계형 데이터베이스는 관계형 데이터베이스대로 설계

  • 객체 중심으로 애플리케이션 개발

생산성

  • 저장: jpa.persist(member);
  • 조회: Member member = jpa.find(memberId);
  • 수정: member.setName(“변경할 이름”);
  • 삭제: jpa.remove(member);

유지보수

  • Entity에 필드를 추가하거나 삭제하면 SQLJPA가 처리

패러다임 불일치 해결

  • JPA와 상속(ITEM은 하나 이하의 ALBUM으로 구성)
    • jpa.persist(album);
    • INSERT INTO ITEM …
    • INSERT INTO ALBUM …
  • JPA와 연관관계
    • member.setTeam(team);
    • jpa.persist(member);
  • JPA와 객체 그래프 탐색
    • Member member = jpa.find(Member.class, memberId);
    • Team team = member.getTeam();
  • JPA와 비교하기
    • Member member1 = jpa.find(Member.class, memberId);
    • Member member2 = jpa.find(Member.class, memberId);
    • member1 == member2; // 같다.
    • 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장함

성능

  • 1차 캐시와 동일성(identity)보장
    • 같은 트랜잭션 안에서는 같은 엔티티 반환(약간의 조회 성능 향상)
    • DB Isolation LevelRead Commited이어도 애플리케이션에서 Repeatable Read 보장
    • 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)

INSERT

transaction.begin(); // [트랜잭션] 시작 

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.

//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다. 
transaction.commit(); // [트랜잭션] 커밋         
* 트랜잭션을 커밋할 때까지 `INSERT SQL`을 모음
* `JDBC BATCH SQL` 기능을 사용해서 한번 `SQL` 전송

UPDATE

transaction.begin(); // [트랜잭션] 시작 

changeMember(memberA);
deleteMember(memberB);
비즈니스_로직_수행(); //비즈니스 로직 수행 동안 DB 로우 락이 걸리지 않는다.

//커밋하는 순간 데이터베이스에 UPDATE, DELETE SQL을 보낸다. 
transaction.commit(); // [트랜잭션] 커밋 
* `UPDATE`, `DELETE`로 인한 로우(`ROW`)락 시간 최소화
* 트랜잭션 커밋 시 `UPDATE`, `DELETE` `SQL` 실행하고, 바로 커밋

* 지연 로딩(`Lazy Loading`)
    * 지연 로딩: **객체가 실제 사용될 때 로딩**
    * 즉시 로딩: `JOIN` `SQL`로 한번에 연관된 객체까지 미리 조회

Lazy Loading

Member member = memberDAO.find(memberId); // SELECT * FROM MEMBER
Team team = member.getTeam(); 
String teamName = team.getName(); // 객체가 실제로 사용됨. SELECT * FROM TEAM

Eager Loading

Member member = memberDAO.find(memberId); // SELECT M.*, T.* FROM MEMBER JOIN TEAM ... 
Team team = member.getTeam();
String teamName = team.getName();
  • 데이터 접근 추상화와 벤더(DB회사(MySQL, ORACLE …)) 독립성
    • JPA의 구현체인 Hibernate는 여러 데이터베이스에 대해 각각의 기능을 구현
  • 표준
    • JPAJAVA진영의 ORM표준

 

JPA 구성도

JPA는 애플리케이션과 JDBC 사이에서 동작합니다.

 

JPA 동작 -삽입

JPAINSERT 쿼리를 생성하고 전달하는 과정을 그림으로 나타내면 다음과 같습니다.
ObjectJPA에게 전달하면 저장됩니다.!

 

JPA 동작 -조회

조회하는 과정도 살펴볼까요?
find()메소드에 id를 파라미터로 입력하면 Object를 반환해 줍니다.

 

728x90
반응형