[Spring] 6.์คํ๋ง DB ์ ๊ทผ ๊ธฐ์
1. H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์น
- ๊ต์ก์ฉ์ผ๋ก์ข์
- download (1.4.200) -> all platforms)
- cd h2 -> cd bin -> chmod 755 h2.sh (๊ถํ) -> ./h2.sh (์คํ)
- ์คํ๋๊ณ ์์ดํผ ์์กํ๋ (์์ดํผ:8082 -> localhost:8082)
- JDBC URL -> jdbc:h2~/test -> ์ฐ๊ฒฐ
- ll -> ls -all -> test.mv.db ํ์ผ์ด ์์ด์ผํจ
- ํ์ผ ์ง์ ์ ๊ทผ ์ํ๊ณ ์ฌ๋ฌ๊ตฐ๋ฐ์ ์ ๊ทผํ๋๋ก -> JDBC URL -> jdbc:h2:tcp://localhost/~/test
- create table
- DDL ๊ด๋ฆฌ ->src๋ฐ์ sql ํด๋ ๋ง๋ค์ด์ ddl.sql ๋ง๋ค์ด์ ๊ด๋ฆฌ (๋ช ๋ น์ด) 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 (connect, release ํด์ค๋)
-> ์ด๋ ๊ฒ ํด์ผ database ํธ๋์ญ์ ์ ์ง์์ผ์ค
SpringConfig
-> @Bean ๋ ํฌ์งํ ๋ฆฌ return ๋ถ๋ถ ์์ -> MemoryMemberRepo -> JdbcMemberRepo(dataSource)
datasource
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
-> ์คํ๋ง ์์ฒด๋ก ๋ฐ์ดํฐ์์ค ๋ง๋ค์ด์ฃผ๊ณ ์ฃผ์ ํด์ค
Controller๊ฐ Service ์์กดํ๊ณ , Service๊ฐ Repo ์์กด
-> Jdbc๋ก ์ฝ๊ฒ ๋ฐ๊ฟ์ค์์์
** ๊ฐ๋ฐฉํ์์์น (OCP, open closed principle)
= ํ์ฅ์ ์ด๋ ค์๊ณ ์์ /๋ณ๊ฒฝ์๋ ๋ซํ์๋ค.
= ๊ธฐ๋ฅ ๋ณ๊ฒฝํด๋ ๋์์ฝ๋ ์์ ์ํ ์์์ (๊ธฐ์กด์ฝ๋)
= ์คํ๋ง์ 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 : ์คํ๋ง ์ปจํ ์ด๋์ ํ ์คํธ๋ฅผ ํจ๊ป ์คํํ๋ค.
@Transactional : ํ ์คํธ ์ผ์ด์ค์ ์ด ์ ๋ ธํ ์ด์ ์ด ์์ผ๋ฉด, ํ ์คํธ ์์ ์ ์ ํธ๋์ญ์ ์ ์์ํ๊ณ , ํ ์คํธ ์๋ฃ ํ์ ํญ์ ๋กค๋ฐฑํ๋ค.
์ด๋ ๊ฒ ํ๋ฉด DB์ ๋ฐ์ดํฐ๊ฐ ๋จ์ง ์์ผ๋ฏ๋ก ๋ค์ ํ ์คํธ์ ์ํฅ์ ์ฃผ์ง ์๋๋ค.
-> aftereach, beforeeach ํ์์์!
* ์์ ๋จ์ ํ ์คํธ๊ฐ ์ข์ (ํตํฉํ ์คํธ๋ณด๋ค)
* ์คํ๋ง ์ปจํ ์ด๋ ์์ด ํ ์คํธํ๋๊ฒ ์ข์
4. ์คํ๋ง JdbcTemplate (Mybatis ๋น์ท)
- ๋ผ์ด๋ธ๋ฌ๋ฆฌ (์ค๋ฌด์์ ๋ง์ด ์!)
- JDBC API์์ ๋ฐ๋ณต์ฝ๋๋ฅผ ๋๋ถ๋ถ ์ ๊ฑฐํด์ค
- SQL์ ์ง์ ์์ฑํด์ผํจ
- ์คํ๋ง JdbcTemplate
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;
};
}
}
* ์์ฑ์ ํ๋๋ฉด @Autowired ์๋ต๊ฐ๋ฅ
5. JPA
- ๊ธฐ์กด์ ๋ฐ๋ณต์ฝ๋ + ๊ธฐ๋ณธ sql ์ง์ ๋ง๋ค์ด์ ์คํ
- sql๊ณผ ๋ฐ์ดํฐ ์ค์ฌ ์ค๊ณ -> ๊ฐ์ฒด ์ค์ฌ์ ์ค๊ณ๋ก ํจ๋ฌ๋ค์ ์ ํ ๊ฐ๋ฅ
- ๊ฐ๋ฐ ์์ฐ์ฑ ํฅ์
์ค์
dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
//implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'com.h2database:h2' testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
resources/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 // jpa๊ฐ ๋ ๋ฆฐ sql์ ๋ณผ์์์
spring.jpa.hibernate.ddl-auto=none // jpa ์ฌ์ฉํ๋ฉด ํ์๊ฐ์ฒด(ํ
์ด๋ธ๋ก) ์๋์ผ๋ก ์์ฑํด์ฃผ๋๋ฐ ์ฌ๊ธฐ์๋ ์๊ด์์
- JPA ์ฐ๋ ค๋ฉด ์ํฐํฐ๋ ๋งตํํด์ผ๋จ
- JPA = ์ธํฐํ์ด์ค(์๋ฐ์ง์ ํ์ค ์ธํฐํ์ด์ค) / ํ์ด๋ฒ๋ค์ดํธ = ๊ตฌํ์ฒด
- ORM(Object Relational Mapping) ๊ธฐ์ , ์ด๋ป๊ฒ? ์ด๋ ธํ ์ด์
Member
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username")
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
* @Id / @GenerateValue -> identity ์ ๋ต & id ์๋ ์์ฑ
* @Column -> db ์นผ๋ผ๋ช ์ด๋ ๋งค์นญ
JPA repo ์์ฑ
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
public Member save(Member member) {
em.persist(member);
return member;
}
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class).getResultList();
}
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();
}
}
- ์คํ๋ง๋ถํธ๊ฐ ๋ด๋ถ์ ์ผ๋ก(์๋) Entity Manager ๋ง๋ค์ด์ค
- ๊ทธ๋์ Entity Manager ์ฃผ์ ๋ฐ์์ผํจ
์๋น์ค ๊ณ์ธต์ ํธ๋์ญ์ ์ถ๊ฐ
import org.springframework.transaction.annotation.Transactional
@Transactional
public class MemberService {}
Configuration
@Configuration
public class SpringConfig {
private final DataSource dataSource;
private final EntityManager em;
public SpringConfig(DataSource dataSource, EntityManager em) {
this.dataSource = dataSource;
this.em = em;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
// return new JdbcMemberRepository(dataSource);
// return new JdbcTemplateMemberRepository(dataSource);
return new JpaMemberRepository(em);
}
}
6. ์คํ๋ง ๋ฐ์ดํฐ JPA
- JPA ๋ฐฐ์ฐ๊ณ ์คํ๋ง ๋ฐ์ดํฐ JPA ๋ฐฐ์ฐ๋ ๊ฒ์ด ์ข์!
์คํ๋ง ๋ฐ์ดํฐ JPA ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ
public interface SpringDataJpaMemberRepository extends JpaRepository<Member,Long>, MemberRepository {
Optional<Member> findByName(String name);
}
- <Member, Long> : ๋ฉค๋ฒ์ pk
- ์ธํฐํ์ด์ค ๋ค์ค์์ ๊ฐ๋ฅ
์คํ๋ง ๋ฐ์ดํฐ JPA ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๋ก ์คํ๋ง ์ค์ ๋ณ๊ฒฝ
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}
- ์คํ๋ง ๋ฐ์ดํฐ JPA๊ฐ SpringDataJpaMemberRepository ๋ฅผ ์คํ๋ง ๋น์ผ๋ก ์๋ ๋ฑ๋กํด์ค๋ค.
[์๋ฆฌ]
์คํ๋ง ๋ฐ์ดํฐ JPA ์ ๊ณต ํด๋์ค
- jpa + ์คํ๋ง ๋ฐ์ดํฐ jpa ๊ธฐ๋ณธ๋ด์ฅ๋๊ฑฐ ๊ฐ์ ธ๋ค ์ฐ๋๊ฒ
- ํต์ฉ๋์ง ์๋ ๊ฒ๋ค (name์ผ๋ก ์ฐพ๋ ๊ฒ ๋ฑ๋ฑ)
-> ์ธํฐํ์ด์ค์ ์ถ๊ฐ
public interface SpringDataJpaMemberRepository extends JpaReopository<Member, Long>, MemberRepository {
@Override
Optional<Member> findByName(String name):
}
-> ์ค๋ฌด์์๋ ๋์ ์ฟผ๋ฆฌ์ ๊ฒฝ์ฐ Querydsl ํ์ฉ (JPA + Spring Data JPA + Querydsl)
-> ์ ์ธ์กฐํฉ์ด ์ํตํ๋ฉด JPA ์์ ๊ธฐ์ ๋ ์ ๊ณตํ๊ธดํจ (JPA ๋ค์ดํฐ๋ธ ์ฟผ๋ฆฌ) or JdbcTemplate
'๐จโ๐ป Web Development > Spring - Intro' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] 7.AOP (0) | 2023.02.16 |
---|---|
[Spring] 5.ํ์ ๊ด๋ฆฌ ์์ - ์น MVC ๊ฐ๋ฐ (0) | 2023.02.13 |
[Spring] 4.์คํ๋ง ๋น๊ณผ ์์กด๊ด๊ณ (0) | 2023.02.11 |
[Spring] 3.ํ์๊ด๋ฆฌ ์์ - ๋ฐฑ์๋ ๊ฐ๋ฐ (0) | 2022.07.03 |
[Spring] 2.์คํ๋ง ์น ๊ฐ๋ฐ ๊ธฐ์ด (1) | 2022.07.01 |
์ต๊ทผ๋๊ธ