- RDBMS, 줄여서 RDB는 관계형 데이터베이스를 말합니다. 정보 저장소라고 생각하셔도 무방한데요.
- (Relational DataBase Management System)
- 쇼핑몰이라면 주문 정보, 고객 정보 등을 컴퓨터에 저장해야 할텐데, 이걸 도와주는 녀석입니다.
- H2는 RDBMS의 한 종류로, 서버가 켜져있는 동안에만 작동하는 RDB입니다.
- SQL은 데이터를 읽고, 저장하고, 변경하고, 삭제하는 구체적인 문법입니다.
- (Structured Query Language)
스프링은 자바로 작동하고, 데이터베이스는 SQL로 작동합니다. 한국어와 영어 사이에는 번역기가 필요하죠? 자바를 위한 번역기, JPA 사용법을 배워봅니다.
- JPA(Spring Data JPA)는 스프링을 위해 태어난 녀석인데요.
- Java로 코드를 작성하면 SQL로 번역해줄 뿐만 아니라, 기본적인 기능이 거의 완벽하게 들어있습니다.
- Repository는 JPA를 작동시키는 매개체입니다.
- API 요청에도 다양한 방식이 있습니다. 생성, 조회, 수정, 삭제 각각에 맞는 방법을 알아봅니다.
- 스프링은 데이터를 주고받는 방법을 강제합니다. 어떻게 하면 올바르게 주고받을 수 있는지 배웁니다.
- 현업에서는 데이터를 주고받을 때, DTO를 반드시 이용합니다. 왜 그러한지 이유와 방법을 배웁니다.
- 코드를 절약할 수 있는 Lombok 사용법을 익힙니다.
- RDBMS
- RDBMS란?
- RDBMS(Relational DataBase Management System)은 컴퓨터에 정보를 저장하고 관리하는 기술입니다.
- 성능/관리 면에서 매우 고도화된 엑셀 이라고 생각하셔도 좋습니다. 😎
- RDBMS의 종류</aside>
- MySQL
- PostgreSQL
- Oracle Database
- 💡 각 제품 간 차이가 크지 않아서 사실 어떤 걸 사용하든 좋습니다. 유료인 Oracle을 제외하고 보통 MySQL, PostgreSQL 중에서 많이 고르는 편인데, 우리는 실제 배포 시 MySQL을 사용합니다.
- RDBMS란?
- 우리가 사용할 RDBMS
- H2
- In-memory DB의 대표 주자인 H2를 사용해볼겁니다. 인메모리 DB란 서버가 작동하는 동안에만 내용을 저장하고, 서버가 작동을 멈추면 데이터가 모두 삭제되는 데이터베이스를 말합니다.
- 연습용으로 딱이에요! 😎
- MySQL
- MySQL은 우리가 서비스를 배포할 때 사용할 데이터베이스입니다. AWS RDS 라는 서비스를 사용해 붙여볼 예정인데요. 스프링과 궁합이 좋아서 많은 회사에서 사용하고 있답니다.
- H2
<H2 웹콘솔 설정(src > main > resources > application.properties )>
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MYSQL;
http://localhost:8080/h2-console
JDBC URL:jdbc:h2:mem:testdb 로 나는 변경해줘야했다. (~이 들어간 형태로 나왔었는데..)
데이터베이스는 매우 고도화된 엑셀이다 - 라고 설명드렸던 것, 기억하실까요? 엑셀 파일 하나가 "데이터베이스"라면, 엑셀 시트 하나는 "테이블"이라고 부르고, 엑셀 행 하나는 "데이터(entity)"라고 부릅니다.
H2 콘솔에다가 아래 코드를 넣고 RUN.(별다른 반응은 없는 게 정상)
CREATE TABLE IF NOT EXISTS courses (
id bigint(5) NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
tutor varchar(255) NOT NULL,
PRIMARY KEY (id)
);
첫문장은 만약 courses라는 표가 없다면 만들어라
마지막 PRIMARY KEY는 주 키: 행 하나하나를 유일하게 겹치지 않게 구분하는 값.
<삽입하기>요것도 SQL로 H2 콘솔에 입력
INSERT INTO courses (title, tutor) VALUES
('웹개발의 봄, Spring', '남병관'), ('웹개발 종합반', '이범규');
<courses 조회하기>
SELECT * FROM courses;
* : asterisk, 혹자는 아스타라고 부름. 모든 것을 의미.
courses의 모든 데이터를 조회해서 콘솔에 띄워달라.
위에서 사용한 생성, 삽입, 조회 명령문을 SQL(Structured Query Language)이라고 합니다.
그럼, 웹서비스를 만들기 위해 SQL을 모두 배워야할까요?
->우리가 명령을 내린 Java 명령어를 SQL로 번역해주는 녀석이, 바로 다음 시간에 배울 Spring Data JPA 라는 녀석입니다! 이 녀석이 없었다면 우리는 진짜 필요한 개발을 하는 시간보다 SQL 만드느라 훨씬 많은 시간을 소비했을지 모릅니다. 어때요? 벌써 JPA 배울 생각에 두근거리는 심장 소리가 들리세요? 😎
JPA는, SQL을 쓰지 않고 데이터를 생성, 조회, 수정, 삭제할 수 있도록 해주는 번역기입니다.
- 자바로 DB를 사용하도록 도와주는 녀석이 JPA 라고 말씀드렸습니다.
- 그럼 DB를 이용하는데 핵심이었던 "테이블"과 "SQL"과 동일한 개념의 자바 용어가 있겠죠?
- "테이블"은 Domain, "SQL"은 Repository 입니다.
@NoArgsConstructor // 기본생성자를 대신 생성해줍니다.
@Entity // 테이블임을 나타냅니다.
public class Course {
@Id // ID 값, Primary Key로 사용하겠다는 뜻입니다.
@GeneratedValue(strategy = GenerationType.AUTO) // 자동 증가 명령입니다.
private Long id;
@Column(nullable = false) // 컬럼 값이고 반드시 값이 존재해야 함을 나타냅니다.
private String title;
@Column(nullable = false)
private String tutor;
public Course(String title, String tutor) {
this.title = title;
this.tutor = tutor;
}
public Long getId() {
return id;
}
public String getTitle() {
return this.title;
}
public String getTutor() {
return this.tutor;
}
}
코드 위에 @(Annotation)는 클래스나 메소드가 어떤 역할을 했으면 좋겠다고 스프링에게 알려주는 역할.
저 Annotation을 이용해서 사용자가 일일이 구현하지 않아도 스프링이 알아서 필요한 것들을 다 만들어 줌.
이걸 잘 이용하면 스프링에서 견고하고 결함이 없는 설계 가능.
NoArgs = No Arguments
인자가 없는 파라미터가 없는 constructor생성자를 만들어주세요라고 annotation으로 처리.
@Entity : 표..?
사전) 독립체
객체를 정의하는 클래스 상단에 위치
GeenrateValue 하나씩 증가하는 명령(for에 i++같은 느낌인가)
컬럼(열) (빈값 가능= 불가)
Annotation으로 역할을 지정해주고 있기 때문에 역할과 위치 잘 기억 해둘 것!
public interface CourseRepository extends JpaRepository<Course, Long> {
}
JPA는 Repository를 통해서만 사용할 수 있습니다.
CourseRepository가 JpaRepository의 값을 다 물려받아서 기능해 주겠다는 것.
인터페이스는 클래스에서 멤버가 빠진 메소드 모음집.
구현부는 없고 메소드의 이름만 정의해주는 형식의 파일
SQL이 보이도록 application.properties 세팅
spring.jpa.show-sql=true
JPA 실행 코드
// Week02Application.java 의 main 함수 아래에 붙여주세요.
@Bean
public CommandLineRunner demo(CourseRepository repository) {
return (args) -> {
};
}
나중에 실행시 args 뒤에 중괄호 내용이 없기때문에 아무것도 안 뜨는 게 정상
웹콘솔에 입력.
SELECT * FROM course;
course로 부터 asterisk, 전부다 가져와라.
@MappedSuperclass // 상속했을 때, 컬럼으로 인식하게 합니다.
@EntityListeners(AuditingEntityListener.class) // 생성/수정 시간을 자동으로 반영하도록 설정
public class Timestamped {
@CreatedDate // 생성일자임을 나타냅니다.
private LocalDateTime createdAt;
@LastModifiedDate // 마지막 수정일자임을 나타냅니다.
private LocalDateTime modifiedAt;
}
LocalDateTime이라는 형식의 createdAt이라는 변수에 생성일자라는 컬럼 값을 넣겠다.
CRUD란? 정보관리의 기본 기능
→ 생성 (Create) → 조회 (Read) → 변경 (Update) → 삭제 (Delete)
- 스프링의 구조는 3가지 영역으로 나눌 수 있습니다.
- Controller : 가장 바깥 부분, 요청/응답을 처리함.
- → 2주차 후반부에 배울 친구
- Service : 중간 부분, 실제 중요한 작동이 많이 일어나는 부분
- → 지금 배울 녀석
- Repo : 가장 안쪽 부분, DB와 맞닿아 있음.
- → 여태 배운 녀석 (Repository, Entity) 클라이언트와 가장 먼 부분
- Controller : 가장 바깥 부분, 요청/응답을 처리함.
- Update 는 Service 부분에 작성합니다.
<데이터 저장, 조회>
// 데이터 저장하기
repository.save(new Course("프론트엔드의 꽃, 리액트", "임민영"));
// 데이터 전부 조회하기
List<Course> courseList = repository.findAll();
for (int i=0; i<courseList.size(); i++) {
Course course = courseList.get(i);
System.out.println(course.getId());
System.out.println(course.getTitle());
System.out.println(course.getTutor());
}
// 데이터 하나 조회하기
Course course = repository.findById(1L).orElseThrow(
() -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);
상속받았던 jpaRepository에서 .save와 .findAll
new Course이하 내용은 course클래스에서 파라미터를 넣은 인스턴스가 생성.
이 생성된 인스턴스를 그대로 파라미터로 받아서 save 함수가 JPA를 사용해 쿼리로 변환
쿼리가 데이터 베이스로 가서 insert명령어를 수행하게 되는 것.
데이터를 전부 주는 건 여러개니까 list 사용
이 list에는 course라는 객체만 들어갈 수 있음.
Repository에 findAll하게 되면 이 모든 데이터를 다 조회해 오게 됨.
밑에 보면 하나하나 데이터에 대한 내용을 하나하나 확인하기 위해 for문 사용
데이터 하나 조회는 findById
이 데이터 하나를 유일하게 구분할 수 있는 PK로 Id
1L(long 형식) => 1이라는 숫자를 아이디로 가지는 데이터를 찾아서 course라는 변수에다가 대입해줘.
orElseThrow : 만약 이게 없으면 예외처리해서 메세지를 띄워줘.
CourseService.java만들어서
@Service // 스프링에게 이 클래스는 서비스임을 명시
public class CourseService {
// final: 서비스에게 꼭 필요한 녀석임을 명시
private final CourseRepository courseRepository;
// 생성자를 통해, Service 클래스를 만들 때 꼭 Repository를 넣어주도록
// 스프링에게 알려줌
public CourseService(CourseRepository courseRepository) {
this.courseRepository = courseRepository;
}
@Transactional // SQL 쿼리가 일어나야 함을 스프링에게 알려줌
public Long update(Long id, Course course) {
Course course1 = courseRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);
course1.update(course);
return course1.getId();
}
}
Repository를 사용하기 위해서 객체를 하나 인스터스로 만듦.
생성자를 통해 Repository를 만들 때, Repository가 꼭 필요해하고 스프링한테 알려줘야함.
이 서비스는 Repository를 필수 객체로 가져야한다. 필수 파라미터로 가져가야 한다는 걸 알려줘야해.
그래서 service에서 이 Repository를 활용해서 데이터베이스를 조작할 수 있도록
@Transactional 사전)거래, 매매, 처리(과정)
SQL Query 하나하나에 단위.
Transactional붙이면 update라는 메소드가 SQL쿼리 작업이 일어나야한다고 알려줌
업데이트 액션이 순서대로 파라미터를 보면 ID값과 course 객체를 받은 다음
course1에는 id값으로 바꿀 대상을 찾아서 course1에 넣어줌=>원본
course : 바꿀 내용.
<완성>
------Week02Application.java
package com.sparta.week02;
import com.sparta.week02.domain.Course;
import com.sparta.week02.domain.CourseRepository;
import com.sparta.week02.domain.CourseService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import java.util.List;
@EnableJpaAuditing
@SpringBootApplication
public class Week02Application {
public static void main(String[] args) {
SpringApplication.run(Week02Application.class, args);
}
// Week02Application.java 의 main 함수 아래에 붙여주세요.
@Bean
public CommandLineRunner demo(CourseRepository courseRepository, CourseService courseService) {
return (args) -> {
courseRepository.save(new Course("프론트엔드의 꽃, 리액트", "임민영"));
System.out.println("데이터 인쇄");
List<Course> courseList = courseRepository.findAll();
for (int i=0; i<courseList.size(); i++) {
Course course = courseList.get(i);
System.out.println(course.getId());
System.out.println(course.getTitle());
System.out.println(course.getTutor());
}
Course new_course = new Course("웹개발의 봄, Spring", "임민영");
courseService.update(1L, new_course);
courseList = courseRepository.findAll();
for (int i=0; i<courseList.size(); i++) {
Course course = courseList.get(i);
System.out.println(course.getId());
System.out.println(course.getTitle());
System.out.println(course.getTutor());
}
courseRepository.deleteAll();
};
}
}
-------Timestamped.java
package com.sparta.week02.domain;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@MappedSuperclass // 상속했을 때, 컬럼으로 인식하게 합니다.
@EntityListeners(AuditingEntityListener.class) // 생성/수정 시간을 자동으로 반영하도록 설정
public class Timestamped {
@CreatedDate // 생성일자임을 나타냅니다.
private LocalDateTime createdAt;
@LastModifiedDate // 마지막 수정일자임을 나타냅니다.
private LocalDateTime modifiedAt;
}
----------Course.java
package com.sparta.week02.domain;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@NoArgsConstructor // 기본생성자를 대신 생성해줍니다.
@Entity // 테이블임을 나타냅니다.
public class Course extends Timestamped {
@Id // ID 값, Primary Key로 사용하겠다는 뜻입니다.
@GeneratedValue(strategy = GenerationType.AUTO) // 자동 증가 명령입니다.
private Long id;
@Column(nullable = false) // 컬럼 값이고 반드시 값이 존재해야 함을 나타냅니다.
private String title;
@Column(nullable = false)
public void update(Course course) {
this.title = course.title;
this.tutor = course.tutor;
}
private String tutor;
public Course(String title, String tutor) {
this.title = title;
this.tutor = tutor;
}
public Long getId() {
return id;
}
public String getTitle() {
return this.title;
}
public String getTutor() {
return this.tutor;
}
}
------------------CourseService.java
package com.sparta.week02.domain;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service // 스프링에게 이 클래스는 서비스임을 명시
public class CourseService {
// final: 서비스에게 꼭 필요한 녀석임을 명시
private final CourseRepository courseRepository;
// 생성자를 통해, Service 클래스를 만들 때 꼭 Repository를 넣어주도록
// 스프링에게 알려줌
public CourseService(CourseRepository courseRepository) {
this.courseRepository = courseRepository;
}
@Transactional // SQL 쿼리가 일어나야 함을 스프링에게 알려줌
public Long update(Long id, Course course) {
Course course1 = courseRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);
course1.update(course);
return course1.getId();
}
}
-----------------Timestamped
package com.sparta.week02.domain;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@MappedSuperclass // 상속했을 때, 컬럼으로 인식하게 합니다.
@EntityListeners(AuditingEntityListener.class) // 생성/수정 시간을 자동으로 반영하도록 설정
public class Timestamped {
@CreatedDate // 생성일자임을 나타냅니다.
private LocalDateTime createdAt;
@LastModifiedDate // 마지막 수정일자임을 나타냅니다.
private LocalDateTime modifiedAt;
}
-----------------application.properties
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MYSQL;
spring.jpa.show-sql=true
------------------CourseRepository.java
package com.sparta.week02.domain;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CourseRepository extends JpaRepository<Course, Long> {
}
강의자료 : https://teamsparta.notion.site/01-RDBMS-SQL-JPA-99e46820b38d4c4e90801e84c64259cb
'항해99 > 스프링입문' 카테고리의 다른 글
03-1 Controller,Repository,Service (0) | 2022.10.07 |
---|---|
03. 기본 CRUD API 구현 - Level 3 (0) | 2022.10.03 |
02. 기본 CRUD API 구현 - Level 2 (0) | 2022.10.03 |
02. 기본 CRUD API 구현 - Level 1 (1) | 2022.10.02 |
00 - 인텔리제이로 Spring Boot 시작하기 (0) | 2022.10.01 |