728x90
반응형

Java Persistence API의 개요

ORM(Object Relational Mapping)

  • 객체를 관계형 데이터베이스에 있는 데이터와 자동으로 맵핑(연결)해주는 작업
  • SQL 문장 자동으로 생성
  • 객체 사용 ~> 데이터베이스의 데이터 다룰 수 있게 해주는 메커니즘

JPA(Java Persistence API)

  • 자바 ORM 기술에 대한 API 표준 명세
  • 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스(규칙, 약속)
  • EntityManager ~> CRUD 처리

Hibernate

  • JPA의 구현체, 인터페이스를 직접 구현한 라이브러리
  • 생산성, 유지보수, 비종속성

Spring Data JPA

  • Spring Module
  • JPA를 추상화한 Repository 인터페이스 제공

=> JDBC -> Hibernate -> JPA -> Spring Data JPA


JPA 사용을 위한 Dependency 추가와 Entity 설정


      
// pom.xml
// 이미 기존에 추가해둠
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

      
// application.yml
spring:
message:
basename: messages
datasource:
url: jdbc:h2:mem:testdb
username: sa
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
defer-datasource-initialization: true // hibernate 초기화 먼저
h2:
console:
enabled: true
settings:
web-allow-others: true

*spring security 잠시 주석 처리 => 인증 처리 계속해줘야 하기 때문

 

http://localhost:8088/h2-console 접속 -> Test Connection

Table Structure


      
create table user {
id integer not null,
join_date timestamp,
name varchar(255),
password varchar(255),
ssn varchar(20),
primary key(id)
}

-> 프로젝트 생성 시 DB 자동 생성


      
// User.java
@Data
@NoArgsConstructor // 추가, 디폴트 생성자
@AllArgsConstructor
@JsonIgnoreProperties(value = {"password", "ssn"})
@Schema(description = "사용자 상세 정보를 위한 도메인 객체")
@Entity // 추가
@Table(name = "users")
public class User {
@Schema(title = "사용자 ID", description = "사용자 ID는 자동 생성됩니다.")
@Id // 추가
@GeneratedValue // 추가
private Integer id;

생성된 것 확인


Spring Data JPA를 이용한 초기 데이터 생성


      
// resources -> data.sql
insert into users(id, join_date, name, password, ssn) values (90001, now(), 'User1', 'test111', '701010-1111111');
insert into users(id, join_date, name, password, ssn) values (90002, now(), 'User2', 'test222', '810283-1111111');
insert into users(id, join_date, name, password, ssn) values (90003, now(), 'User3', 'test333', '190293-1111111');

 

* spring security 문제

: /h2-console url 무인증 추가


      
// config -> SecurityConfig.java
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return ((web) -> web.ignoring().requestMatchers(new AntPathRequestMatcher("/h2-console/**")));
}


JPA Service 구현을 위한 Controller, Repository 생성


      
// repository -> UserRepository(interface)
package com.example.myrestfulservice.repository;
import com.example.myrestfulservice.bean.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}

      
// controller -> UserJpaController
package com.example.myrestfulservice.controller;
import com.example.myrestfulservice.bean.User;
import com.example.myrestfulservice.repository.UserRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/jpa")
public class UserJpaController {
private UserRepository userRepository;
// 생성자 주입
public UserJpaController(UserRepository userRepository) {
this.userRepository = userRepository;
}
// /jpa/users
@GetMapping("/users")
public List<User> retrieveAllUsers() {
return userRepository.findAll();
}
}


JPA를 이용한 개별 사용자 상세 조회 - HTTP Get method


      
// UserController
// /jpa/users/{id}
@GetMapping("/users/{id}")
public ResponseEntity retrieveUser(@PathVariable int id) {
Optional<User> user = userRepository.findById(id);
if (!user.isPresent()) {
throw new UserNotFoundException("id-" + id);
}
EntityModel entityModel = EntityModel.of(user.get());
WebMvcLinkBuilder linTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());
entityModel.add(linTo.withRel("all-users"));
return ResponseEntity.ok(entityModel);
}


JPA를 이용한 사용자 추가와 삭제 - HTTP POST/DELETE method

사용자 삭제


      
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id) {
userRepository.deleteById(id);
}

      
// SecurityConfig.java
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http,
HandlerMappingIntrospector introspector) throws Exception {
// csrf; cross-site request fogery
http.csrf(AbstractHttpConfigurer::disable);
return http.build();
}

사용자 추가


      
// /jpa/users
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
User savedUser = userRepository.save(user);
// CREATED
// /users/4
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}


게시물 관리를 위한 Post Entity 추가와 초기 데이터 생성

User : Posts -> 1 : N


      
// Post.java
package com.example.myrestfulservice.bean;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String description;
// 지연 로딩: post가 로딩되는 필요한 시점에 그때 그때 가져옴
@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnore
private User user;
}

      
// User.java
@OneToMany(mappedBy = "user")
private List<Post> posts;
public User(Integer id, String name, Date joinDate, String password, String ssn) {
this.id = id;
this.name = name;
this.joinDate = joinDate;
this.password = password;
this.ssn = ssn;
}

      
// data.sql
insert into post(description, user_id) values ('My first post', 90001);
insert into post(description, user_id) values ('My second post', 90001);


게시물 조회를 위한 Post Entity와 User Entity와의 관계 설정


      
@GetMapping("/users/{id}/posts")
public List<Post> retrieveAllPostsByUser(@PathVariable int id) {
Optional<User> user = userRepository.findById(id);
if (!user.isPresent()) {
throw new UserNotFoundException("id-" + id);
}
return user.get().getPosts();
}


JPA를 이용한 새 게시물 추가  - HTTP POST Method


      
// PostRepository
package com.example.myrestfulservice.repository;
import com.example.myrestfulservice.bean.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PostRepository extends JpaRepository<Post, Integer> {
}

      
private PostRepository postRepository;
// 생성자 주입
public UserJpaController(UserRepository userRepository, PostRepository postRepository) {
this.userRepository = userRepository;
this.postRepository = postRepository;
}
@PostMapping("/users/{id}/posts")
public ResponseEntity<Post> createPost(@PathVariable int id, @RequestBody Post post) {
Optional<User> userOptional = userRepository.findById(id);
if (!userOptional.isPresent()) {
throw new UserNotFoundException("id-" + id);
}
User user = userOptional.get();
post.setUser(user);
postRepository.save(post);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(post.getId())
.toUri();
return ResponseEntity.created(location).build();
}


 

[개정판 2023-11-27] Spring Boot 3.x 를 이용한 RESTful Web Services 개발 | Dowon Lee - 인프런

Dowon Lee | 이 강의는 Spring Boot를 이용해서 RESTful Web Services 애플리케이션을 개발하는 과정에 대해 학습하는 강의으로써, REST API 설계에 필요한 기본 지식에 대해 학습할 수 있습니다., 스프링 부트

www.inflearn.com

 

728x90
반응형
김앩옹