ํฐ์คํ ๋ฆฌ ๋ทฐ
[SPRING]์คํ๋ง ์ ๋ฌธ - ์ฝ๋๋ก ๋ฐฐ์ฐ๋ ์คํ๋ง ๋ถํธ, ์น MVC, DB ์ ๊ทผ ๊ธฐ์ ์น์ 6 (์คํ๋ง DB ์ ๊ทผ ๊ธฐ์ )
chaewonni 2023. 12. 29. 19:59๐กH2 ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์น
> https://www.h2database.com/html/download-archive.html
Archive Downloads
www.h2database.com
์ฌ๊ธฐ์ ๊ฐ์ 1.4.200๋ฒ์ ์ ์ค์นํด์ค๋ค.

create a new database๋ฅผ ํด์ค ๋ค์,

database๋ฅผ ์์ฑํด์ค๋ค.

์์ด์ฝ์ ๋๋ธํด๋ฆญํ์ฌ, ๋ง๊ฒ ์ ์ด์ค ํ ์ฐ๊ฒฐ์ ๋๋ฅด๋ฉด

๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ฐ๊ฒฐ๋๋ค.
๐ป H2 ํ ์ด๋ธ ์์ฑ ๋ฐ ์คํ
member ํ ์ด๋ธ์ ์์ฑํ๋ค.
drop table if exists member CASCADE;
create table member
(
id bigint generated by default as identity,
name varchar(255),
primary key (id)
);
SELECT * FROM MEMBER
select ๋ช ๋ น์ด๋ก memberํ ์ด๋ธ์ ์กฐํํ ์ ์๋ค.

INSERT INTO MEMBER(NAME) VALUES('Spring')
insert ๋ช ๋ น์ด๋ก member ํ ์ด๋ธ์ ๊ฐ์ ์ถ๊ฐํ๋ฉด,

๋ฐ์ดํฐ๊ฐ ์ถ๊ฐ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๐ก์์ JDBC
์ง์ memory๊ฐ ์๋ DB์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ์ฌ ๋ฃ๊ณ ๋นผ๋ ๋ฐฉ๋ฒ (์๋ ์ ํ๋ ๋ฐฉ์)
๐ป ์์ Jdbc ํ๊ฒฝ์ค์
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'com.h2database:h2'
}
build.gradle์ dependencies์ jdbc, h2 ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐํด์ค๋ค.
spring.datasource.url=jdbc:h2:~/jpamember
spring.datasource.driver-class-name=org.h2.Driver
application.properties์ ์คํ๋ง ๋ถํธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ค์ ์ถ๊ฐ
๐ป Jdbc ๋ฆฌํฌ์งํ ๋ฆฌ ์์ฑ
repository์ JdbcMemberRepository๋ผ๋ ์๋ก์ด class๋ฅผ ์์ฑํด์ค ๋ค์,
package hello.hellospring.repository;
import hello.hellospring.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 {
private final 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 {
conn = getConnection();//connection ๊ฐ์ง๊ณ ์ด
pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
pstmt.setString(1, member.getName()); //1->?๋ ๋งค์นญ๋จ
pstmt.executeUpdate();//์ค์ ์ฟผ๋ฆฌ ๋ ๋ผ๊ฐ
rs = pstmt.getGeneratedKeys(); //1๋ฒ์ด๋ฉด 1๋ฒ ๋ฐํ, 2๋ฒ์ด๋ฉด 2๋ฒ ๋ฐํ
if (rs.next()) {
member.setId(rs.getLong(1));
} else {
throw new SQLException("id ์กฐํ ์คํจ");
}
return member;
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
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);
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() {
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);
}
}
์ ์ฝ๋๋ฅผ ์์ฑํด์ค๋ค.
๐ป Spring ์ค์ ๋ณ๊ฒฝ
package spring.study1;
...
@Configuration
public class SpringConfig {
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
...
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
}
์ด๋ ๊ฒ ์ฝ๋๋ฅผ ์์ฑํด์ค ํ์ localhost:8080/members ์ ๋ค์ด๊ฐ๋ฉด H2 DB์์ ์ถ๊ฐํ ๋ฐ์ดํฐ๋ค์ ๋ชฉ๋ก์ด ๋ณด์ธ๋ค.

์ถ๊ฐ์ ์ผ๋ก ์๋กญ๊ฒ ํ์์ ๋ฑ๋กํด์ฃผ๋ฉด ex)123

DB์๋ ์๋กญ๊ฒ ์ถ๊ฐ๋ ๊ฒ๋ ๋ณผ ์ ์๋ค.
๐ป ์๋ฆฌ ์ค๋ช

MemberService๋ MemberRepository๋ฅผ ์์กดํ๊ณ , MemberRepository ๊ตฌํ์ฒด์๋ Memroy์ Jdbc๊ฐ ์๋ค.

์คํ๋ง ์ปจํ
์ด๋์์ ์ค์ ์ ๋ฐ๊ฟ์, ๊ธฐ์กด์ MemoryMemberRepository๋ฅผ spring bean์์ ์ ์ธ์ํค๊ณ JdbcMemberRepository๋ฅผ ์๋ก์ด spring bean์ผ๋ก ๋ฑ๋กํด์ค๋ค.

๐ก์คํ๋ง ํตํฉ ํ ์คํธ
์คํ๋ง ์ปจํ
์ด๋์ DB๊น์ง ์ฐ๊ฒฐํ ํตํฉํ
์คํธ๋ฅผ ์ค์ํ๋ค.
๐ป ํ์ ์๋น์ค ํตํฉ ํ ์คํธ
MemberServiceTest์ ๋ด์ฉ์ ๋ณต๋ถํ์ฌ ์๋ก์ด MemberServiceIntegrationTest๋ฅผ ๋ง๋ค์ด์ค๋ค.
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SpringBootTest
@Transactional //test๊ฐ ๋๋๋ฉด rollback์ ํด์ค์ ๋ฐ์ดํฐ๊ฐ ๋ค ์ง์์ง (testcase์ ๋ถ์์ ๋๋ง, ๊ทธ๋ฅ MemberService์ผ ๋๋ x)
class MemberServiceIntegrationTest {
//MemberService memberService = new MemberService(); // member service
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
void ํ์๊ฐ์
() {
//given
Member member = new Member();
member.setName("spring");
//when
Long saveId = memberService.join(member);
//then
Member findMember = memberService.findOne(saveId).get();
//memberRepository์์ ๊ฐ์ ธ์์ ๋น๊ตํด์ผ๋๋๊น ๊ฐ๋จํ findOne์ผ๋ก ๊ฐ์ฒด ๊ฐ์ ธ์ด
// ํ์๊ฐ์
ํ member์ id๊ฐ ์ ์ฅ์์ ์์ผ๋ฉด, ํด๋น member ๊ฐ์ฒด๋ฅผ findMember๋ก
assertThat(member.getName()).isEqualTo(findMember.getName());
}
@Test
public void ์ค๋ณต_ํ์_์์ธ() {
//given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//when
memberService.join(member1);
// try{
// memberService.join(member2); //validate์์ ๊ฑธ๋ ค์ ์์ธ๊ฐ ํฐ์ง
// fail();
// } catch (IllegalStateException e){
// assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
// }
//try catch๋ฌธ์ ๋ฒ๊ฑฐ๋ก์
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
assertThat(e.getMessage()).isEqualTo("์ด๋ฏธ ์กด์ฌํ๋ ํ์์
๋๋ค.");
//then
}
}
DELETE FROM MEMBER
DB์ ์๋ ๋ด์ฉ์ ๋ชจ๋ ์ง์์ค๋ค.
๐ป ์คํ๋ง ํตํฉ ํ ์คํธ ์ค๋ช
@Transactional ์ ๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉด, ํ
์คํธ ์คํ ์ ํธ๋์ญ์
์ด ์๋์ผ๋ก ๊ด๋ฆฌ๋๋ค. ํ
์คํธ๊ฐ ์์๋๊ธฐ ์ ์ ํธ๋์ญ์
์ด ์์๋๊ณ , ํ
์คํธ๊ฐ ๋๋๋ฉด ๋ชจ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ฌํญ์ ์๋์ผ๋ก ๋กค๋ฐฑ๋๋ค.
์ด๋ ํ
์คํธ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์
ํ๋๋ผ๋, ํ
์คํธ ์ข
๋ฃ ํ์ ์ด๋ฌํ ๋ณ๊ฒฝ์ฌํญ์ด ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์๋์ง ์๋๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก, ๊ฐ ํ
์คํธ ์คํ ํ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์๋์ผ๋ก ์ ๋ฆฌํ ํ์๊ฐ ์์ด์ง๋ค.
๋ฐ๋ฉด์, @Transactional ์ ๋
ธํ
์ด์
์ด ์๋ ๊ฒฝ์ฐ, ํ
์คํธ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉ๋ ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ด ์ค์ ๋ก ๋ฐ์๋๋ค. ์ด๋ ํ
์คํธ๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ์คํํ ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ค๋ณต๋ ๋ฐ์ดํฐ๊ฐ ์์ด๋ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํ ์ ์์ผ๋ฉฐ, ์ด๋ ๋งค๋ฒ ํ
์คํธ๋ฅผ ์คํํ๊ธฐ ์ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์๋์ผ๋ก ์ ๋ฆฌํด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์์ ์ด๋ํ๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก, @Transactional ์ ๋
ธํ
์ด์
์ ํ
์คํธ ์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ผ๊ด์ฑ์ ์ ์งํ๊ณ , ํ
์คํธ ํ ์ ๋ฆฌ ์์
์ ์ค์ฌ์ฃผ๋ ์ค์ํ ์ญํ ์ ํ๋ค.

'Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
- Total
- Today
- Yesterday
- ์คํ๋ง๋ถํธ
- ์๋ฐ ์คํ๋ง
- ์นMVC
- ์ง์ฐ๋ก๋ฉ
- ๋ก๊น
- ์น MVC
- ๋ฐฑ์ค
- ์๋ฐ
- ์คํ๋ง ์ปค๋ฎค๋ํฐ
- ๋น์์
- JPA
- ๋ฐฑ์ค ํ์ด์ฌ
- ์ปค๋ฎค๋ํฐ
- ๋ถ๋งํฌ
- ์คํ๋ง ๋ถ๋งํฌ
- SQL
- ํ์ด์ฌ
- ํ๋ก ํธ์๋
- EnumType.ORDINAL
- SQLD
- ํ์ํํด
- ๋ค์ด๋๋ฏน ํ๋ก๊ทธ๋๋ฐ
- SQL ๋ ๋ฒจ์
- DP
- ์ธํ ๋ฆฌ์ ์ด
- ์คํ๋ง
- ์์
- ์ค์์
- ๋ก๊ทธ์์
- elasticsearch
์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |