[스프링 입문]-4

김영한님의 스프링 입문 강의 학습 내용입니다.

스프링 DB 접근 기술

  • cmd -> h2 -> bin -> h2.bat -> jdbc:h2:tcp://localhost/~/test

application.properties

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

JDBC

SpringConfig

@Configuration
public class SpringConfig {

    private DataSource dataSource;

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

    @Bean
    public MemberRepository memberRepository(){
//        return new MemoryMemberRepository();
        return new JdbcMemberRepository(dataSource);
    }
}
  • MemoryMemberRepository -> JdbcMemberRepository : 직접 스프링 빈을 등록한 경우 이와 같이 변경에 용이하다. (스프링의 DI 사용)

스프링 통합 테스트

@SpringBootTest 
@Transactional 
class MemberServiceIntegrationTest {

    @Autowired MemberService memberService;
    @Autowired MemberRepository memberRepository;

//...

}
  • @SpringBootTest : 스프링 컨테이너와 테스트를 함께 실행, 단위 테스트가 아닌 통합테스트이기에 좋은 테스트는 아니다.
  • @Transactional : 테스트가 끝나면 ROLL BACK, DB에 실제 데이터가 반영X, 테스트 반복이 가능하다.
  • @Commit : DB에 반영
  • @Autowired : 테스트 케이스는 필드 기반으로 Autowired 받는게 편하다.

스프링 JdbcTemplate

  • Jdbc와 동일 환경설정
  • JDBC API에서 본 반복 코드를 대부분 제거한다. SQL은 직접 작성해야 한다.
public class JdbcTemplateMemberRepository implements MemberRepository{

    private final JdbcTemplate jdbcTemplate;

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

    @Override
    public Member save(Member 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(){
    //...
    }
}

JdbcTemplate 기본 스타일

private final JdbcTemplate jdbcTemplate;
@Autowired
public JdbcTemplateMemberRepository(DataSource dataSource) {
    jdbcTemplate = new JdbcTemplate(dataSource);
}

Alt + Enter -> 람다스타일로 변경 가능

private RowMapper<Member> memberRowMapper(){
    return new RowMapper<Member>() {
        @Override
        public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        }
    };
}
  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도 JPA가 직접 만들어 실행
  • SQL, 데이터 중심 설계에서 객체 중심의 설계로 전환
  • 개발 생산성을 크게 높일 수 있다.
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

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
spring.jpa.hibernate.ddl-auto=none
  • spring.jpa.hibernate.ddl-auto=create -> 테이블 자동 생성
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

//    @Column(name = "username")
    private String name;

  //...
  
  }
  • @Entity : JPA가 관리하는 Entity
  • @Id :PK
  • @GeneratedValue(strategy = GenerationType.IDENTITY) : IDENTITY strategy (DB에 값을 넣으면 DB가 id를 자동생성)
  • @Column(name = "username") : 만약 컬럼명이 username이라면 이와 같이 매핑

JpaMemberRepository

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

    @Override
    public List<Member> findAll() {
        List<Member> result = em.createQuery("select m from Member m", Member.class)
                .getResultList();
        return result;
    }
}
  • JPA 사용을 위해서 EntityManager를 주입 받아야한다.
  • em.persist(member); : JPA가 INSERT 쿼리 만들어 DB에 넣고 id set까지 다 완료해준다.
  • 저장, 조회, 갱신, 삭제 SQL작성할 필요가 없다. (자동완료)
  • 스프링 데이터 JPA를 사용하면 findByName, findAll역시 SQL작성이 필요없다.

inline -> Ctrl + Alt + N

  List<Member> result = em.createQuery("select m from Member m", Member.class)
          .getResultList();
  return result;
  return em.createQuery("select m from Member m", Member.class)
          .getResultList();

JPA 사용 시 항상 @Transactional이 존재해야한다.

@Transactional
public class MemberService {
//...
}

SpringConfig

@Configuration
public class SpringConfig {

  private EntityManager em;

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

  @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

SpringDataJpaMemberRepository

//JpaRepository Interface 받기
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {

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

SpringConfig

@Configuration
public class SpringConfig {

  private final MemberRepository memberRepository;

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

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

}
  • 스프링 데이터 JPA가 SpringDataJpaMemberRepository를 스프링 빈으로 자동 등록
  • Interface를 통한 기본 CRUD
  • findByName(); 등 메서드 이름 만으로 조회

좋은 웹페이지 즐겨찾기