스프링 DB 접근 기술
H2 데이터베이스 설치
http://www.h2database.com
링크에 접속하여 Windows Installer 설치(맥북은 따로 강의에 나와있다)
IntelliJ 터미널에서 설치한 파일 경로로 이동하고 ./h2.bat 명령어 입력
JDBC URL을 jdbc:h2~/test로 바꾸고 연결 클릭
이후부터 jdbc:h2:tcp://localhost/~/test로 바꿔서 접속
만약 오류가 뜰 경우 웹 브라우저 주소창에 x.x.x.x를 localhost로 변경하여 접속하자
./h2.bat을 실행시켜두고 DB에 접근해야한다.
순수 jdbc
20년전에 개발된 데이터베이스 접근 기술이다. 사용하지않으므로 글은 생략한다. 궁금하면 직접 찾아보자…
jdbcTemplate
JDBC API에서 반복 코드를 대부분 제거한 기술이다.
build.gradle에 아래 코드를 추가
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
resource/application.properties에 코드 추가
spring.datasource.url = jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
repository 패키지에 jdbcTemplateMemberRepository java 파일을 생성하자.
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) {
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 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(){
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return member;
};
}
}
각 메소드를 보면 sql 쿼리를 보내는걸 확인할 수 있다. rowMapper 메소드는 템플릿으로부터 ResultSet을 전달받고, 필요한 정보를 추출해서 리턴하는 방식으로 동작 ResultSet의 로우 하나만을 매핑하기 위해 사용된다. 여기서는 row에 해당되는 member 객체를 return하여 다른 메소드에서 활용한다.
jdbcTemplate를 사용하기 위해서 스프링 설정을 변경하자.
@Configuration
public class SpringConfig {
private final DataSource dataSource;
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new JdbcTemplateMemberRepository(dataSource);
}
}
JPA
JPA는 기존의 반복 코드와 기본적은 SQL도 자동으로 만들어서 실행해준다.
build.gradle 파일에 아래 코드를 추가하자.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
resource/application.properties에 코드 추가
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
엔티티를 설정해줘야 되기 때문에 Member 클래스에 @Entity 어노테이션을 추가하자
@Entity
public class Member {
...
}
JPA 레포지토리 추가
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() {
return em.createQuery("select m from Member m",Member.class)
.getResultList();
}
}
추가로 MemberService 클래스에 @Transactional(org.sprinFramwork.trasaction.annotation.Trasactional)를 추가해서 사용하는게 좋다.
JPA를 사용하도록 스프링 설정을 변경하자.
@Configuration
public class SpringConfig {
private final EntityManager em;
private final DataSource dataSource;
public SpringConfig(EntityManager em, DataSource dataSource) {
this.em = em;
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new JpaMemberRepository(em);
}
}
스프링 데이터 JPA
스프링 데이터 JPA를 사용하면 레포지토리에 구현 클래스를 만들어주지 않고 인터페이스 만으로 개발을 할 수 있는 장점이 있다. 또한 CRUD 기능도 제공하기 때문에 반복적인 코드들이 확연하게 줄어들면서 핵짐 비즈니스 로직에만 집중할 수 있게 해준다. 관계형 데이터베이스를 사용한다면 스프링 데이터 JPA는 필수로 사용해야한다.
스프링 데이터 JPA를 사용하기 위한 레포지토리를 생성하고 스프링 설정을 변경하자.
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
//JPQL select m from Member m where m.name = ?
@Override
Optional<Member> findByName(String name);
// 메서드 이름으로 재정의할 수 있다.
}
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
public SpringConfig(MemberRepository memberRepository) {
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}