[Spring] 순수 JDBC → JdbcTemplate → JPA → 스프링 데이터 JPA

728x90

 

순수 JDBC

- 아래와 같이 초기 설정을 해준다.

- JDBC 표준 인터페이스를 통해 표준화하여 여러 개의 데이터베이스에 동일하게 적용할 수 있다.

- DataSource 를 주입받아, `getConnection( )`을 통해 데이터베이스와 연결된 소켓을 얻을 수 있다.

- 반복 코드가 굉장히 길고 많다. 

  (Connection, PreparedStatement, ResultSet 을 항상 열고 닫는 try-finally 구조가 필요)

- SQL 문을 직접 작성해야 한다.

 

> 참고 <

- 스프링 부트가 자동으로 데이터베이스 접속 정보로 DataSource을 생성하여 스프링 빈으로 등록한다.

 

초기 설정

- build.gradle 파일에 jdbc, h2 데이터베이스 관련 라이브러리 추가

implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'

 

- 스프링 부트 데이터베이스 연결 설정 추가

resources/application.properties

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

 

이때, 스프링 부트 2.4 이상은 `spring.datasource.username=sa`를 추가해야 한다.

 

예시 코드

public class JdbcMemberRepository implements MemberRepository {
    private final DataSource dataSource;
 	
    public JdbcMemberRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Member save(Member member) {
        String sql = "insert into member(name) values(?)";
        
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;	// 결과 저장
        
        try {
            conn = getConnection();
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            // 1 > sql의 ? 와 매칭되어 member.getName()으로 값 삽입
            pstmt.setString(1, member.getName());
            
            pstmt.executeUpdate();          // DB의 실제 sql 문이 전달
            rs = pstmt.getGeneratedKeys();  // RETURN_GENERATED_KEYS와 매칭되어 rs에 저장
            
            if (rs.next()) {
                member.setId(rs.getLong(1));
            } else {
                throw new SQLException("id 조회 실패");
            }
            return member;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        } finally {
            close(conn, pstmt, rs);        // 리소스 반환
        }
    }
 }

 

JDBC 표준 인터페이스, JDBC 드라이버

`java.sql.Connection` - 연결

`java.sql.Statement` - SQL 을 담은 내용

`java.sql.ResultSet` - SQL 요청 응답

JDBC 표준 인터페이스는 위의 3가지를 정의하여 제공한다.

 

이를 각자의 DB 에 맞도록 구현하여 제공하는 라이브러리를 JDBC 드라이버라고 한다.

(예를 들어, MySQL DB 라면 MySQL JDBC 드라이버 / Oracle DB 라면 Oracle JDBC 드라이버)

 

DriverManager 

라이브러리에 등록된 DB 드라이버들의 목록을 관리하고, 커넥션을 획득하는 기능을 제공한다.

매번 새로운 커넥션을 생성하여 성능에 비효율적이다.

 

DataSource

커넥션을 획득하는 방법을 추상화 하는 인터페이스이다.

- `DBCP2 커넥션 풀` / `HIKARICP 커넥션 풀` 로 커넥션 풀을 사용

- `DriverMaganerDataSource` 를 사용하여 매번 새로운 커넥션을 생성하여 사용

  (DriverManager 는 DataSource 를 구현하지 않기 때문에, DataSource 를 구현한 DriverManagerDataSource 클래스이다. )

 

어느 방법을 사용해도 애플리케이션 로직이 DataSource 인터페이스에만 의존하여 사용할 수 있다.

 

JDBC 를 편리하게 사용하는 기술

데이터베이스마다 SQL, 데이터타입 등 일부 사용법이 다르므로 해당 기술과 함께 편리하게 사용한다.

- SQL Mapper

- ORM 기술

 

SQL Mapper

- SQL 응답 결과를 객체로 편리하게 반환해 준다.

- JDBC 반복 코드를 제거해 준다.

- SQL 문을 직접 작성해야 하지만, 객체 매핑을 도와주는 유틸리티가 있다.

- `스프링 Jdbc Template`, `MyBatis`

 

ORM 기술

- 객체를 관계형 데이터베이스 테이블과 매핑해주는 기술

- SQL 문을 직접 작성하지 않고, 동적으로 만들어 실행해 준다.

- `JPA`, `하이버네이트`, `이클립스 링크`

( JPA 는 자바의 ORM 표준 인터페이스, 이를 구현한 것이 하이버네이트 / 이클립스 링크 등)

 

스프링 JdbcTemplate

- 순수 Jdbc 설정과 같게 한다. (위에 있음.)

- Jdbc API에서 본 반복 코드 대부분을 제거한다.

- DataSource 를 주입받아 JdbcTemplate를 생성하여 사용한다.

- SQL 문을 직접 작성해야 한다. 

 

> 참고<

- Insert 문은 데이터 추가라는 단순한 작업이므로, 테이블 이름과 칼럼-값 정보로 SQL을 생성하기 쉽다.

따라서 Spring 이 자동화하기 위해 SimpleJdbcInsert 같은 유틸리티를 제공한다.

 

그렇지만 Select 는 조건, 그룹화, 정렬, 조인 등 형태가 다양하므로 자동화하기 어려워서 직접 작성한다.

Insert 문도 직접 작성해도 된다.

 

예시 코드

public class JdbcTemplateMemberRepository implements MemberRepository {

    private final JdbcTemplate jdbcTemplate;

    public JdbcTemplateMemberRepository(DataSource dataSource) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public Member save(Member member) {
        SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
        jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", member.getName());  // 여기까지 table 이름, pk, name 으로 insert 문 생성

        Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
        member.setId(key.longValue());
        return member;
    }
    
    @Override
    public Optional<Member> findById(Long id) {
        List<Member> result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id);
        return result.stream().findAny();
    }
    
    private RowMapper<Member> memberRowMapper() {
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }
}

 

JPA

- 아래와 같이 초기 설정을 해준다.

- SQL, 데이터 중심의 설계에서 객체 중심의 설계로 전환할 수 있다.

- 개발 생산성이 크게 향상된다.

- 기존의 반복 코드, 기본적인 SQL 문도 JPA 가 직접 만들어 실행한다.

- PK 기반이 아닌 SQL 문은 직접 작성해야 한다.

 

- 도메인에 @Entity 를 추가한다.

- PK에 @Id와 @GeneratedValue 를 추가한다.

- EntityManger 을 주입받아 동작한다.

 

- JPA 를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.

 

@Transactional 을 서비스 계층에 추가해야 함

@GenerationType(strategy = GenerationType.IDENTITY) 에서 IDENTITY 는 DB가 알아서 생성해 주는 것을 말한다.

 

@Column : 칼럼명을 명시할 때 사용(ex. Column(name = "칼럼명"))

 

> 참고 <

- 스프링 부트가 JPA 라이브러리를 다운로드할 때, EntityManger를 생성하여 스프링 빈으로 만든다.

 

초기 설정

- build.gradle 파일에 JPA, h2 데이터베이스 관련 라이브러리 추가

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'

 

- 스프링 부트에 JPA 설정 추가

resources/application.properties

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true              // JPA가 생성하는 SQL 출력
spring.jpa.hibernate.ddl-auto=none    // JPA의 테이블을 자동으로 생성하는 기능을 none


이때, 스프링 부트 2.4 이상은 `spring.datasource.username=sa`를 추가해야 한다.

 

예시 코드

@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    // @Column(name = "칼럼명")
    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class JpaMemberRepository implements  MemberRepository {

    private final EntityManager em;

    public JpaMemberRepository(EntityManager em) {
        this.em = em;
    }

    @Override
    public Member save(Member member) {
        em.persist(member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id);
        return Optional.ofNullable(member);
    }
}

 

스프링 데이터 JPA

- JPA 설정과 같게 한다. (위에 있음.)

- JPA 를 편리하게 사용하도록 도와주는 프레임워크이다.

- 리파지토리의 구현 클래스 없이 인터페이스만으로 개발이 가능하다.

- 기본 CRUD 기능도 제공된다.

- 페이징 기능도 자동 제공된다.

 

save, count, findByName, exists 등 공통 메서드가 제공된다.

 

> 참고 <

- 스프링 데이터 JPA 의 JpaRepository 가 인터페이스에 구현체를 자동으로 만들어 스프링 빈으로 등록한다. 

 

예시 코드

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {

    @Override
    Optional<Member> findByName(String name);
}

 

 

 

 

 

 

 

 

 

 

출처 | 스프링 입문(김영한) - 인프런

728x90