스프링 JdbcTemplate
- 순수 Jdbc와 동일한 환경설정을 하면 된다.
- 스프링 JdbcTemplate과 MyBatis 같은 라이브러리는 JDBC API에서 본 반복 코드를 대부분 제거해준다. 하지만 SQL은 직접 작성해야 한다.
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.util.List;
import java.util.Optional;
public class JdbcTemplateMemberRepository implements MemberRepository{
private final JdbcTemplate jdbcTemplate;
@Autowired
public JdbcTemplateMemberRepository(DataSource dataSource){
this.jdbcTemplate =new JdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
return null;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.empty();
}
@Override
public Optional<Member> findByName(String name) {
return Optional.empty();
}
@Override
public List<Member> findAll() {
return null;
}
}
Repository에다가 새로운 클래스를 만들어준다.
생성자가 하나만 있으면 스프링 Bean 으로 등록 할 때 @Autowired를 생략 할 수 있다.
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
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;
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate =newJdbcTemplate(dataSource);
}
@Override
public Member save(Member member) {
SimpleJdbcInsert jdbcInsert =new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
Map<String, Object> parameters =newHashMap<>();
parameters.put("name", member.getName());
Number key = jdbcInsert.executeAndReturnKey(new
MapSqlParameterSource(parameters));
member.setId(key.longValue());
returnmember;
}
@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 List<Member> findAll() {
return jdbcTemplate.query("select * from member", memberRowMapper());
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name);
return result.stream().findAny();
}
private RowMapper<Member> memberRowMapper() {
return(rs, rowNum) -> {
Member member =newMember();
member.setId(rs.getLong("id"));//maping하는 것
member.setName(rs.getString("name"));
return member;
};
}
}
이렇게 변경을 했으니 조립을 해야 합니다.
package hello.hellospring.MemberService;
import hello.hellospring.repository.JdbcMemberRepository;
import hello.hellospring.repository.JdbcTemplateMemberRepository;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
@Configuration
public classSpringConfig {
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource){
this.dataSource = dataSource;
}
@Bean
public MemberService memberService(){
return newMemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
// return new MemoryMemberRepository();
// return new JdbcMemberRepository(dataSource);
return new JdbcTemplateMemberRepository(dataSource);
}
}
JPA
- JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다
- JPA를 사용하면, SQL과 데이터 중심의설계에서 객체 중심의 설계로 패러다임을 전환을 할 수 있다.
- JPA를 사용하면 개발 생산성을 크게 높일 수 있다.
MemoryMemoryRepository 객체를 메모리에 넣듯이 JPA에 넣으면 DB에 알아서 SQL문을 날리기도 하고 DB를 가져오기도 한다.
jpa를 사용하기 위해선 build.grandle 파일에다가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 을 추가한다.
기존의
implementation 'org.springframework.boot:spring-boot-starter-jdbc' 은 사용 하지 않는다.
그 다음 application.properties에 들어가
spring.jpa.show-sql=*true* 추가 한다 이걸 추가하면 쿼리문을 볼 수 있다.
spring.jpa.hibernate.ddl-auto=none 자동으로 테이블 만들어주는 건데 우리는 테이블이 만들어져 있으니깐 none으로 설정 한다. 이걸 create로 바꾸면 테이블을 만들어준다.
우리는 jpa의 인터페이스중 hibernate를 주로 사용할 예정이다. jpa는 자바의 표준 인터페이스고 구현은 여러 업체들이 하는 것이다. ex) 쓰기 편하다.
h2데이터베이스를 보면 우리는 name table에 설정한 이름을 집어 넣는다. 이때
이름만 집어 넣으면 자동으로 id가 설정이 되는데 이걸 IDENTITY라고 한다.
domain에 meber 클래스
package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity //jpa가 Entity로 인식하게 된다.
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) //이 부분
private Long id;
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;
}
}
앞서 application property에 jpa를 다운 받았습니다. 그렇게 다운을 받게 되면 Spring boot 가 자동으로 EntityManger를 생성하고 우리는 인젝션 받으면 된다.
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import javax.persistence.Entity;
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 Optional<Member> findByName(String name) {
List<Member> reuslt = em.createQuery("select m from Member m where m.name = :name",Member.class)
.setParameter("name",name)
.getResultList();
return reuslt.stream().findAny();
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m",Member.class)
.getResultList(); //객체를 대상으로 쿼리를 날린다. 정확히 말하자면 Entity에 날린다.
}
}
@Transactional 을 서비스에 MemberService에 달아준다 .
@Transactional: 이 붙은 메서드는 메서드가 포함하고 있는 작업 중에 하나라도 실패할 경우 전체 작업을 취소한다.
package hello.hellospring.MemberService;
import hello.hellospring.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
@Configuration
public class SpringConfig {
// private DataSource dataSource;
//
// @Autowired
// public SpringConfig(DataSource dataSource){
// this.dataSource = dataSource;
// }
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);
}
}
새로운 방식으로 바꿨으니깐 다시 SpringConfig을 변경 해준다.
다 바꿧으니 Test를 돌려서 확인해보겠습니다.
Could not open JPA EntityManager for transaction; nested exception is org.hibernate.service.spi.ServiceException:
돌리다가 이게 떳는데 알아보니 h2 데이터베이스를 켜야 작동을 합니다.
Test를 돌려보면 잘 작동이 됩니다.
스프링 데이터 JPA
스프링 부트와 JPA만 사용해도 개발 생산성이 정말 많이 증가하고 ,개발해야할 코드도 확연히 줄어듭니다. 여기에 스프링 데이터 JPA를 사용하면 ,기존의 한계를 넘어 마치 마법처럼 ,리포지토리에 구현 클래스 없이 인터페이스 만으로 개발을 완료 할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 스프링 데이터 JPA가 모두 제공합니다.
주의 : 스프링 데이터 JPA는 JPA를 편리하게 사용하도록 도와주는 기술입니다. 따라서 JPA를 먼저 학습한 후에 스프링 데이터 JAP를 학습해야 합니다.
repository에 인터페이스를 만듭니다.
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 {
@Override
Optional<Member> findByName(String name);
}
조립을 하기 위해 SpringConfig도 변경합니다.
package hello.hellospring.MemberService;
import hello.hellospring.repository.*;
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;
}
@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);
// }
}
순수 Jdbc는 쿼리문도 엄청많고 하나하나 다 작성을 해야 했습니다.
JdbcTemplate는 반복되는 코드는 엄청 줄었지만, sql문은 직접 작성을 해야했습니다.
JPA는 직접적인 쿼리문을 작성하지 않아도 됐습니다. 단 select 쿼리 Jsql문은 직접 작성을 해야 했습니다.
스프링 데이터 JPA는 구현클래스가 필요없이 인터페이스로 끝이 났습니다.
'Programming > Spring' 카테고리의 다른 글
Spring Boot JPA를 이용한 수정하기 PUT (0) | 2024.01.08 |
---|---|
AOP #8.1~#8.2 (0) | 2023.01.15 |
스프링 DB 접근 기술 #7.1~#7.3 (0) | 2023.01.14 |
회원 관리 예제 - 웹 MVC 개발 #6.1~#6.3 (0) | 2023.01.12 |
스프링 빈과 의존관계 #5.1~#5.2 (0) | 2023.01.11 |