(Spring) Spring DB 접근 기술
1. H2 데이터베이스 설치
– 교육에 좋은
– 다운로드(1.4.200) -> 모든 플랫폼)
– cd h2 -> cd bin -> chmod 755 h2.sh(권한) -> ./h2.sh(실행)
– 실행되고 IP가 캡처되지 않은 경우 (IP:8082 -> localhost:8082)
– JDBC URL -> jdbc:h2~/test -> 연결
– ll -> ls -all -> test.mv.db 파일이 존재해야 함
– 파일에 직접 접근하지 말고 여러 곳에서 접근 -> JDBC URL -> jdbc:h2:tcp://localhost/~/test
– 테이블 생성
– DDL 관리 -> src 외부에 sql 폴더를 생성하여 ddl.sql(command) 생성 및 관리 ex) drop table, create table~
2. 순수한 Jdbc
– 우선 사항
-JdbcMemberRepository
public class JdbcMemberRepository implements MemberRepository {
@Override
public Member save(Member member) {
return null;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.empty();
}
}
* 지침
-> DataSourceUtils를 통한 getConnection 및 releaseConnection(연결, 해제)
-> 이렇게 하면 데이터베이스 트랜잭션이 활성 상태로 유지됩니다.
SpringConfig
-> @Bean 저장소는 부분적으로 수정된 반환 -> MemoryMemberRepo -> JdbcMemberRepo(dataSource)
데이터 소스
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
-> Spring 자체를 사용하여 데이터 소스 생성 및 주입
컨트롤러는 서비스에 따라 달라지고 서비스는 Repo에 따라 달라집니다.
-> Jdbc로 쉽게 변경 가능
** 개방 폐쇄 원칙(OCP)
= 확장에는 개방, 수정/변경에는 폐쇄.
= 연산 코드(기존 코드)는 기능을 변경해도 수정 불가
= 스프링 DI
3. 스프링 통합 테스트
@SpringBootTest 주석 + @Transactional 주석
@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("이미 존재하는 회원입니다.
");
}
}
@SpringBootTest : Spring 컨테이너와 테스트를 함께 실행.
@트랜잭션 : 테스트 사례에 이 주석이 있는 경우, 테스트가 시작되기 전에 거래를 시작하십시오., 테스트 완료 후 항상 롤백.
이 방법 DB데이터가 남아 있지 않아 다음 테스트에 영향을 미치지 않습니다.
.
-> aftereach, beforeeach가 필요 없습니다!
* 더 나은 순수 단위 테스트(통합 테스트보다)
* 스프링 컨테이너 없이 테스트하는 것이 가장 좋습니다.
4. 스프링 JdbcTemplate(Mybatis와 유사)
– 라이브러리(실무에서 자주 사용!
)
– JDBC API에서 대부분의 중복 코드 제거
– SQL은 손으로 작성해야 합니다.
– 스프링 Jdbc 템플릿
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
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 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 = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
* 생성자가 1개인 경우 @Autowired 생략 가능
4. 공동 행동 계획
– 기존 반복 코드 + 기본 sql 직접 생성 및 실행
– SQL 및 데이터 중심 설계에서 객체 중심 설계로 패러다임 전환 가능
– 개발 효율성 향상