순수 JDBC
hellospring.SpringConfig - 스프링 설정 변경
@Configuration //코드로 직접 스프링 빈 등록하기. 코드 수정이 유리하다.
public class SpringConfig {
private final 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);
}
}
DataSource는 데이터베이스 커넥션을 획득할 때 사용하는 객체다. 스프링 부트는 데이터베이스 커넥션 정보를 바탕으로 DataSource를 생성하고 스프링 빈으로 만들어둔다.


개방-폐쇄 원칙(OCP, Open-Closed Principle)
확장에는 열려있고, 수정, 변경에는 닫혀있다.
스프링의 DI (Dependencies Injection)을 사용하면 기존 코드를 전혀 손대지 않고, 설정만으로 구현 클래스를 변경할 수 있다.
스프링 통합 테스트
service.MemberServiceIntegrationTest
@SpringBootTest //스프링 컨테이너와 테스트를 함께 실행한다
@Transactional //테스트 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 롤백한다(테스트에서만). 다음 테스트에 영향을 주지 않는다
class MemberServiceIntegrationTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("hello");
//When
Long saveId = memberService.join(member); //Then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복_회원_예외() throws Exception {
//Given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//When
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class,
() -> memberService.join(member2));//예외가 발생해야 한다.
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
통합테스트 보다 순수한 단위 테스트가 더 좋은 테스트이다.
스프링 JDBC Template
순수 Jdbc와 동일한 환경설정.
스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해주지만 SQL은 직접 작성해야 한다.
hellospring.SpringConfig - 스프링 설정 변경
@Configuration //코드로 직접 스프링 빈 등록하기. 코드 수정이 유리하다.
public class SpringConfig {
private final DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource){
this.dataSource=dataSource;
}
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository();
//return new JdbcMemberRepository(dataSource);
return new jdbcTemplateMemberRepository(dataSource);
}
}
JPA
repository.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); //insert 쿼리 수행. setter도 알아서 수행
return member;
}
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id); //pk를 통해 조회하는 쿼리 날림.
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() { //jpql. 객체 대상으로 쿼리를 날림. m:객체 자체를 선택.
List<Member> result = em.createQuery("select m from Member m", Member.class)
.getResultList();
return result;
}
}
hellospring.SpringConfig
@Configuration //코드로 직접 스프링 빈 등록하기. 코드 수정이 유리하다.
public class SpringConfig {
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {this.em=em;}
private final MemberRepository memberRepository;
@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를 통한 모든 데이터 변경은 트랜잭션 안에서 실행해야 한다.
스프링 데이터 JPA


기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공.
개발자는 핵심 비즈니스로직을 개발하는데, 집중할 수 있다.
repository.SpringDataJpaMemberRepository
JpaRepository가 해당 인터페이스의 구현체를 자동으로 생성한다.
@Primary //해당 빈을 우선적으로 주입함.
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
//JPQL: select m from Member m where m.id = ? 쿼리를 날림. 메서드 이름 규칙을 지켜야 한다.
@Override
Optional<Member> findByName(String name);
}
hellospring.SpringConfig
@Configuration //코드로 직접 스프링 빈 등록하기. 코드 수정이 유리하다.
public class SpringConfig {
private final MemberRepository memberRepository;
@Autowired //SpringDataJpaMemberRepository의 구현체가 주입됨.
public SpringConfig(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}
스프링 데이터 JPA가 자동으로 SpringDataJpaMemberRepository의 구현체를 생성해서 스프링 빈에 등록됨.