[spring 입문] 스프링 DB 접근 기술-2

스프링 JDBCTemplate


아까 한 jdbc 설정이랑 동일한 환경설정
스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분제거해줌. 하지만 SQL은 직접 작성해야함.

반복코드만 제거해줘도 행복~
JDBCTemplate은 실무에서도 많이 씀

repository 폴더 하위에 JdbcTemplateMemberRepository 클래스 생성

**참고로 생성자 하나면 스프링 빈 등록시 Autowired 생략가능

JdbcTemplate가 순수 jdbc 코드에서 많이 줄여줌

SimpleJdbcInsert는 쿼리 짤 필요 없이 withTableName, usingGeneratedKeyColumns 같은걸로 편하게 사용가능

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class JdbcTemplateMemberRepository implements MemberRepository{

    private final JdbcTemplate jdbcTemplate;

    //@Autowired
    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());
        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();
    }

    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
        return result.stream().findAny();
    }

    @Override
    public List<Member> findAll() {
        return jdbcTemplate.query("select * from member", memberRowMapper());
    }

    private RowMapper<Member> memberRowMapper() {
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }
}

SpringConfig로 가서 조립해주기

MemberServiceIntegrationTest 돌려주면 오류 없이 작성 된다.
db까지 연동한 테스트가 성공 (JDBC템플릿 버전으로 바꾼거)

다음은 쿼리까지 없애버릴 수 있는 jpa대한 강의

JPA


sql은 결국 개발자가 직접 해야했던 JDBC템플릿에서 sql을 만들어주는
jpa에 대한 내용

JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환 가능하고 개발 생산성을 크게 높일 수 있다.

build.gralde에 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 추가

resources/application.properties에
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none 추가

-> show-sql : JPA가 생성하는 SQL을 출력한다.
-> ddl-auto : JPA는 테이블을 자동으로 생성하는 기능을 제공하는데 none 를 사용하면 해당 기능을 끈다.

domain폴더 하위에 위치한 Member클래스에 와서 @Entity 로 JPA가 관리하는 엔티티라고 어노테이션 준다.
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
넣어서
db가 자동으로 id 생성해주는 identity를 넣어준다.
(name도 @Column(name="username") 이런 식으로 가능함)

이렇게 어노테이션을 가지고 db랑 매핑하는 것이다.

repository 폴더 하위에 JPA 회원 리포지토리인 JpaMemberRepository클래스 생성
JPA를 쓰려면 EntityManager를 주입 받아야한다. EntityManager(=em)

저장은 em.persist(member); return member; 로만 해주면 jpa가 insert 쿼리 만들어서 집어넣고 id까지 member에 setId해준다.

조회(findById)는 Member member = em.find(Member.class, id);
return Optional.ofNullable(member);넣어주면
조회 타입이랑 식별자(PK)넣어주면 끝..

findAll도 동일하게 매핑이 다 되어있기 때문에 m을 이용해서
객체를 대상으로 (엔티티 대상) 쿼리 날린다.

package hello.hellospring.repository;

import hello.hellospring.domain.Member;

import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;

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);
    }

    @Override
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }
    @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
                .setParameter("name", name)
                .getResultList();
        return result.stream().findAny();
    }



}

돌려보기 위해서
SpringConfig에 설정해주고

통합 테스트에서 테스트 진행
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not prepare statement 에러가 떳음
아까 예시로 넣은 Member 클래스의 어노테이션을 안지워서 그랬음
@Column(name="username")
private String name; 에서
private String name; 만으로 변경하고 하니 잘됨.

select 쿼리와 insert 쿼리가 나가고 있는거 보임
(select 쿼리는 validation때문)
(insert 에서 id 는 null나가서 자동생성)

전체 테스트 돌려도 다 성공함.

스프링 데이터 JPA


스프링 부트랑 jpa만 해도 개발생산성이 높아진다.
스프링 데이터 JPA 사용하면 인터페이스만으로 개발완료 가능

반복 개발해온 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공합니다.

주의: 스프링 데이터 JPA는 JPA를 편리하게 사용하도록 도와주는 기술입니다. 따라서 JPA를 먼저 학습한 후에 스프링 데이터 JPA를 학습해야 합니다.
넹,, 열심히 jpa 공부해보겠습니다. .. 다음 JAP1 강의도 열심히 들을게요,,,

앞의 jpa 설정 그대로 이용
스프링 데이터 JPA 회원 리포지토리 생성

repository 폴더 하위에 SpringDataJpaMemberRepository 인터페이스

인터페이스는 다중상속, extends

package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface SpringDataJpaMemberRepository extends JpaRepository<Member,
Long>, MemberRepository {
Optional<Member> findByName(String name);
}

하면 끝이다.. 오잉
스프링 데이터 JPA가 Data레포지토리 받고있으면 구현체 자동으로 만들어서 자동으로 스프링빈 등록해준다.
우리는 그거 가져다 쓰면 된다.

스프링 데이터 JPA가 SpringDataJpaMemberRepository 를 스프링 빈으로 자동 등록해준다.

SpringConfig에서

package hello.hellospring;

import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {
    private final MemberRepository memberRepository;

    @Autowired
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }


    /*EntityManager em;

    @Autowired
    public SpringConfig(EntityManager em) {
        this.em = em;
    }*/

    /*private DataSource dataSource;

    @Autowired
    public SpringConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }*/

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository);

    }

    /*@Bean
    public MemberRepository memberRepository() {
        //return new MemoryMemberRepository();
        //return new JdbcMemberRepository(dataSource);
        //return new JdbcTemplateMemberRepository(dataSource);
        //return new JpaMemberRepository(em);
    }*/
}

통합 테스트도 다 완성된다.

  • 스프링 데이터 JPA 제공 기능
    -> 인터페이스를 통한 기본적인 CRUD
    -> findByName() , findByEmail() 처럼 메서드 이름 만으로 조회 기능 제공
    -> 페이징 기능 자동 제공

findByName은 제공하지 않기 때문에 (공통 제공 아님, 여기서나 name이지 다른 곳은 username이나 다른 이름일 수 있어서 공통화 불가)

좋은 웹페이지 즐겨찾기