์คํ๋ง DB ์ ๊ทผ ๊ธฐ์
2. ์์ JDBC
์ด๋ฒ ๊ธ์ `์์ฃผ ์ค๋์ ์๋ ์ด๋ ๊ฒ JDBC API๋ฅผ ์ง์ ์ฝ๋ฉํ๊ตฌ๋` ์ ๋๋ก๋ง ์ฐธ๊ณ ๋ก ์์๋์
1) ํ๊ฒฝ ์ค์
- build.gradle ํ์ผ์ jdbc, h2 ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
- ์คํ๋ง ๋ถํธ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ค์ ์ถ๊ฐ
- url์ ์ด์ ๊ธ์ ์ค์นํ h2์ url์ ์ ๋ ฅํ๋ฉด ๋๋ค.
- ๋ง์ฝ driver class name ์ ๋ ฅ ์ ๋นจ๊ฐ ๊ธ์จ๋ก ๋ ธ์ถ๋๋ค๋ฉด, build.gradle๋ฅผ ๋ค์ ๋ก๋ํ๋ฉด ๋๋ค.
2) Jdbc ๋ฆฌํฌ์งํ ๋ฆฌ ๊ตฌํ
package hello.hello.spring.repository;
import hello.hello.spring.domain.Member;
import org.springframework.jdbc.datasource.DataSourceUtils;
import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class JdbcMemberRepository implements MemberRepository {
// DB์ ์ ๊ทผํ๊ธฐ ์ํด ํ์ํ๋ค.
private final DataSource dataSource;
// application.properties์์ DataSource ์ค์ ํ๋ฉด
// ์คํ๋ง์ด ๊ด๋ จ DataSource๋ฅผ ์์ฑํ๊ณ ์ฃผ์
ํด์ค๋ค.
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Member save(Member member) {
String sql = "insert into member(name) values(?)";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ค.
try {
// getConnection(): DB์ Connection์ ๊ฐ์ ธ์จ๋ค.
conn = getConnection();
// RETURN_GENERATED_KEYS: DB์ id ๊ฐ์ ์ป์ด์ฌ ๋ ์ฌ์ฉํ๋ค.
pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// ์ธ๋ฑ์ค 1์ด (?)์ ๋งค์นญ์ด ๋๋ฉด์ member.getName()์ (?)์ ๋ฃ๋๋ค.
pstmt.setString(1, member.getName());
// executeUpdate(): DB์ ์ฟผ๋ฆฌ๋ฅผ ๋๊ธด๋ค.
pstmt.executeUpdate();
// getGeneratedKeys(): DB์ id ๊ฐ์ ๋ฐํํ๋ค.
rs = pstmt.getGeneratedKeys();
if (rs.next()) { // DB์ id ๊ฐ์ด ์๋ค๋ฉด
member.setId(rs.getLong(1));
} else {
throw new SQLException("id ์กฐํ ์คํจ");
}
return member;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
// ์ฌ์ฉํ ์์๋ค ๋ฐ๋ก release ํด์ผ ํ๋ค.
close(conn, pstmt, rs);
}
}
@Override
public Optional<Member> findById(Long id) {
String sql = "select * from member where id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
// executeQuery(): DB์ ์ฟผ๋ฆฌ๋ฅผ ๋๊ธด๋ค. (์กฐํ)
rs = pstmt.executeQuery();
if (rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return Optional.of(member);
} else {
return Optional.empty();
}
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public List<Member> findAll() {
String sql = "select * from member";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
List<Member> members = new ArrayList<>();
while (rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
members.add(member);
}
return members;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
@Override
public Optional<Member> findByName(String name) {
String sql = "select * from member where name = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
rs = pstmt.executeQuery();
if (rs.next()) {
Member member = new Member();
member.setId(rs.getLong("id"));
member.setName(rs.getString("name"));
return Optional.of(member);
}
return Optional.empty();
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
close(conn, pstmt, rs);
}
}
private Connection getConnection() {
// dataSource๋ฅผ ํตํด ์ง์ getConnection()๋ฅผ ํ ์๋ ์์ง๋ง,
// ๊ทธ๋ ๊ฒ ์ฌ์ฉํ๋ค๋ฉด ์๋ก์ด Connection์ ๊ณ์ํด์ ์์ฑํ๊ฒ ๋๋ค.
return DataSourceUtils.getConnection(dataSource);
}
private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
close(conn);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private void close(Connection conn) throws SQLException {
DataSourceUtils.releaseConnection(conn, dataSource);
}
}
3) ์๋ก์ด ๋ฆฌํฌ์งํ ๋ฆฌ ์ฐ๊ฒฐ
- MemberRepository ์ธํฐํ์ด์ค์ ์๋ก์ด ๊ตฌํ์ฒด๋ฅผ ์์ฑํ๋ค.
- ๋ฐ์ดํฐ๋ฅผ DB์ ์ ์ฅํ๋ฏ๋ก ์คํ๋ง ์๋ฒ๋ฅผ ๋ค์ ์คํํด๋ ๋ฐ์ดํฐ๊ฐ ์์ ํ๊ฒ ์ ์ฅ๋๋ค.
- ๊ฐ์ฒด ์งํฅ์ ๋คํ์ฑ ํน์ฑ
- ๊ฐ๋ฐฉ-ํ์ ์์น(OCP, Open-Closed Principle): ํ์ฅ์๋ ์ด๋ ค์๊ณ ์์ , ๋ณ๊ฒฝ์๋ ๋ซํ์๋ค.
- ์คํ๋ง DI์ ์ฌ์ฉํ๋ฉด ๊ธฐ์กด ์ฝ๋๋ฅผ ์ ํ ์๋์ง ์๊ณ ์ค์ ๋ง์ผ๋ก ๊ตฌํ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
- DataSource๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปค๋ฅ์ ์ ํ๋ํ ๋ ์ฌ์ฉํ๋ ๊ฐ์ฒด๋ค.
- ์คํ๋ง ๋ถํธ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปค๋ฅ์ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก DataSource๋ฅผ ์์ฑํ๊ณ ์คํ๋ง ๋น์ผ๋ก ๋ง๋ค์ด๋๋ค.
- ๊ทธ๋์ DataSource๋ DI๋ฅผ ๋ฐ์ ์ ์๋ค.
package hello.hello.spring;
import hello.hello.spring.repository.JdbcMemberRepository;
import hello.hello.spring.repository.MemberRepository;
import hello.hello.spring.repository.MemoryMemberRepository;
import hello.hello.spring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private 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);
}
}
4) ๊ฒฐ๊ณผ
- ์น ํ์ด์ง๋ฅผ ํตํด ๊ฐ์ ํ๋ค.
- DB ๋ฐ์ดํฐ๋ฅผ ํ์ธํ๋ฉด ๊ฐ์ ์ด๋ฆ์ ๊ฐ์ง member๊ฐ ์กฐํ๋๋ค.