Spring 2주차(JPA, API)
프로젝트 생성시 만들어야 하는 dependencies : lombok , jpa , spring web, mySql, h2
- H2
In-memory DB의 대표 주자인 H2를, 4주차까지 사용해볼겁니다. 인메모리 DB란 서버가 작동하는 동안에만 내용을 저장하고, 서버가 작동을 멈추면 데이터가 모두 삭제되는 데이터베이스를 말합니다.
- JPA
JPA는, SQL을 쓰지 않고 데이터를 생성, 조회, 수정, 삭제할 수 있도록 해주는 번역기
sql문을 짤 필요 없이 명령을 자바로(db프로그램이 아닌) 만들 수 있다.
- JPA 시작하기
Course.java > db를 만들어 가는 과정
public class Course {
@Id // ID 값, Primary Key로 사용하겠다는 뜻입니다.
@GeneratedValue(strategy = GenerationType.AUTO) // 자동 증가 명령입니다. > id를 하나씩 증가시켜라는 명령
private Long id;
@Column(nullable = false) // 컬럼 값이고 반드시 값이 존재해야 함을 나타냅니다.
private String title;
@Column(nullable = false)
private String tutor;
public String getTitle() { // setter를 생성안하는 이유는 repository에서 다 생성해줌
return this.title;
}
public String getTutor() {
return this.tutor;
}
public Course(String title, String tutor) {
this.title = title;
this.tutor = tutor;
}
CourseRepository.java > sql의 역할을 대신해주는 역할, 인터페이스는 jpa를 사용하는 방법
public interface CourseRepository extends JpaRepository<Course, Long> {
}
-JPA 사용해보기
Review02Application
@SpringBootApplication
public class Review02Application {
public static void main(String[] args) {
SpringApplication.run(Review02Application.class, args);
}
// Week02Application.java 의 main 함수 아래에 붙여주세요.
@Bean
public CommandLineRunner demo(CourseRepository repository) {
return (args) -> {
// 데이터 저장하기
Course course1 = new Course("웹개발의 봄", "김태웅");
repository.save(course1); > save = insert
// 데이터 전부 조회하기
List<Course> courseList = repository.findAll(); > findAll = select // course 여러개가 순서대로 오니 List로 받아줘야 한다
for(int i = 0; i < courseList.size(); i++){ > 리스트를 하나하나 확인하기 위한 for
Course course = courseList.get(i);
// 데이터 하나 조회하기
Course course = repository.findById(1L).orElseThrow(
() -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);
}
};
}
}
- JPA 심화 (CRUD)
스프링의 구조
Controller : 가장 바깥 부분, 요청/응답을 처리함.
Service : 중간 부분, 실제 중요한 작동이 많이 일어나는 부분 > 주로 update에서 사용(update는 밖에서도 안에서도 정보가 들어가기에)
Repo : 가장 안쪽 부분, DB와 맞닿아 있음.
Update
Course.Java
public void update(Course course) { > Course라는 것을 받았을때 타이틀과 튜터를 바꾸라는 뜻
this.title = course.title;
this.tutor = course.tutor;
}
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 안에는 타이틀과 튜터정보 존재
Course course1 = courseRepository.findById(id).orElseThrow( > course정보를 id를 통해 찾고
() -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
);
course1.update(course);
return course1.getId();
}
}
Week02Application.java
@EnableScheduling
@SpringBootApplication
public class Review02Application {
public static void main(String[] args) {
SpringApplication.run(Review02Application.class, args);
}
@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); // update 활용
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());
}
};
- lombok , dto
Lombok
메소드/생성자 등을 자동생성해줌으로써 코드를 절약할 수 있도록 도와주는 라이브러리
Course.java
@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)
private String tutor;
public Long getId() { >> lombok을 사용해서 get의 반복을 줄인다 해당 내용을 삭제하고 Course.java에 @Getter 삽입
return this.id;
}
public String getTitle() {
return this.title;
}
public String getTutor() {
return this.tutor;
}
public Course(String title, String tutor) {
this.title = title;
this.tutor = tutor;
}
public void update(Course course) {
this.title = course.title;
this.tutor = course.tutor;
}
}
CourseService.java
@RequiredArgsConstructor : repository 생성자를 만들지 않게 도와주는 lombok의 또 다른 기능
DTO
데이터를 주고 받을때는 기존의 클래스를 사용하지 말고 새로운 클래스를 이용하는 것
이전에는 데이터를 Course 클래스에서 받았는데 ? course는 테이블을 저장하는 공간이기 때문에 누군가 변경하면 안된다 > 따라서 dto사용
데이터를 물고다니는 용도 , 어떤 데이터? course에 대한 데이터
@Getter
@Setter
@RequiredArgsConstructor
public class CourseRequestDto {
private final String title;
private final String tutor;
}
기존 Week02Application.java 에서 데이터를 변경할때 new_course로 선언했지만 이제는 그러지 않을것
Course new_course = new Course("웹개발의 봄, Spring", "임민영"); >> 지워주고 requestDto로 사용할 거임
CourseRequestDto requestDto = new CourseRequestDto("웹개발의 봄, 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());
+ 기존에 service에도 course를 requestDto로 변경해야함
+ service에 있는 update는 course 클래스에 있었기에 다시 바꿔주러 가야함
- API(GET)
응답을 json으로 할 것이기에 restController를 만들 것
CourseController
@RequiredArgsConstructor // final을 사용하기 위해 넣은 것 , lombok
@RestController
public class CourseController {
private final CourseRepository courseRepository;
@GetMapping("/api/courses") // get 방식으로 요청이 오면
public List<Course> getCourses() {
return courseRepository.findAll(); // 해당 메소드를 실행해라
}
}
-API(Post, Put, Delete)
CourseController
@PostMapping("/api/courses")
public Course createCourse(@RequestBody CourseRequestDto requestDto) { // @RequestBody 가 있어야지만 요청한 정보를 requestDto로 부터 받아온다
// requestDto 는, 생성 요청을 의미합니다.
// 강의 정보를 만들기 위해서는 강의 제목과 튜터 이름이 필요하잖아요?
// 그 정보를 가져오는 녀석입니다.
// 저장하는 것은 Dto가 아니라 Course이니, Dto의 정보를 course에 담아야 합니다.
// 잠시 뒤 새로운 생성자를 만듭니다.
Course course = new Course(requestDto); // Course.java 클래스 안에 requestDto 를 만들어 줘야 한다
// JPA를 이용하여 DB에 저장하고, 그 결과를 반환합니다.
return courseRepository.save(course);
}
put
@PutMapping("/api/courses/{id}") //
public Long updateCourse(@PathVariable Long id, @RequestBody CourseRequestDto requestDto) {
return courseService.update(id, requestDto);
}
delete
@DeleteMapping("/api/courses/{id}")
public Long deleteCourse(@PathVariable Long id) {
courseRepository.deleteById(id);
return id;
}
deleteById 가 아무것도 반환하는게 없기 때문에 return값을 준다