김영한님의 스프링 입문 강의 학습 내용입니다.
스프링 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 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;
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 JpaMemberRepository(em);
}
}
스프링 데이터 JPA
SpringDataJpaMemberRepository
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(); 등 메서드 이름 만으로 조회