이전 포스팅까지 RESTful API 기반으로 사용자 CRUD 기능 구현을 해보았습니다.
오늘은 ORM JPA를 연동해서 데이터 맵핑을 해보겠습니다.
- 개발 환경
- Spring Boot
- JPA
- MySQL
JPA 란?
JPA(Java Persistence API)는 자바에서 제공하는 ORM(Object-Relational Mapping) 기술의 표준 인터페이스입니다.
ORM은 객체 지향 프로그래밍과 관계형 데이터베이스 간의 매핑을 자동화하여 객체를 데이터베이스에 저장하고 조회할 수 있도록 도와주고, 객체 지향 프로그래밍에서는 클래스와 객체를 사용하여 데이터와 기능을 캡슐화하고, 관계형 데이터베이스는 테이블과 레코드로 데이터를 저장합니다.
이 둘 간의 패러다임 불일치를 해결하기 위해 ORM은 객체와 테이블 간의 매핑을 자동으로 처리하여 개발자가 SQL 쿼리를 직접 작성하지 않아도 데이터를 관리할 수 있게 해 줍니다.
JPA의 특징과 장점은 다음과 같습니다.
1. 표준 인터페이스:
JPA는 자바 표준으로, 여러 ORM 프레임워크에서 지원하는 공통된 인터페이스를 제공합니다. 따라서 JPA를 사용하면 ORM 프레임워크를 변경해도 기존 코드를 수정할 필요가 없습니다.
2. 객체 지향적인 코드 작성:
JPA를 사용하면 SQL 쿼리를 직접 작성하지 않아도 됩니다. 대신 객체 지향적인 방식으로 데이터를 처리할 수 있으므로 코드가 간결하고 유지보수가 용이해집니다.
3. 데이터베이스 독립성:
JPA는 데이터베이스에 종속적이지 않으며, 데이터베이스를 변경해도 애플리케이션 코드를 수정할 필요가 없습니다.
4. 지연 로딩 및 캐싱:
JPA는 엔티티를 지연 로딩하여 필요한 시점에 데이터를 가져오며, 캐시를 사용하여 데이터베이스 접근 횟수를 줄일 수 있습니다.
5. 강력한 검색 기능:
JPA는 JPQL(Java Persistence Query Language)을 지원하여 객체를 대상으로 쿼리를 작성할 수 있습니다. 이는 SQL과 유사하지만, 객체를 기준으로 쿼리를 작성하므로 더 직관적이고 유연한 검색 기능을 제공합니다.
6. 트랜잭션 관리:
JPA는 트랜잭션을 관리하여 데이터베이스 작업의 일관성과 안전성을 보장합니다.
JPA를 사용하려면 애플리케이션에서 JPA 인터페이스를 구현하는 ORM 프레임워크를 선택해야 합니다.
Maven 환경에서 진행하기 때문에 pom.xml에 라이브러리를 추가해 줍니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
추가 후 메이븐 업데이트 해주고, 이제 JPA를 사용하기 위한 인터페이스를 구현해 줍니다.
UserRepository
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User,Integer> {
}
인터페이스를 생성하고 JpaRepository <>를 상속받아줍니다.
JpaRepository <>를 상속받아서 사용하면 일반적인 CRUD 작업을 수행하는 메서드들을 간단하게 구현할 수 있으며, 커스텀 쿼리를 정의하는 기능도 제공해 줍니다.
여러 가지 주요 특징이 있지만 이번 포스팅은 CRUD 기능 구현이기 때문에 제공해 주는 메서드인
save,
findById,
findAll,
deleteById 등 기본적인 CRUD 메서드를 활용해서 데이터베이스와 상호작용을 할 수 있게 연결해줍니다.
위 코드에서 'User'는 엔티티 클래스이며, 'Integer'는 엔티티의 기본 키 타입입니다.
User
데이터를 전달하기 위해 객체 클래스를 생성합니다.
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;
// @JsonIgnore // 필드에 노출이 안됨
@Data
@AllArgsConstructor
@NoArgsConstructor
//@JsonIgnoreProperties(value = {"password","ssn"}) // @JsonIgnore를 클래스 블록 처리
//@JsonFilter("UserInfo")
@Entity
@ApiModel(description = "사용자 상세 정보를 위한 도메인 객체")
public class User {
@Id
@GeneratedValue
private Integer id;
@Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
@ApiModelProperty(notes = "사용자 이름을 입력해 주세요.")
private String name;
@Past
@ApiModelProperty(notes = "등록일을 입력해 주세요.")
private Date joinDate;
@ApiModelProperty(notes = "패스워드를 입력해 주세요.")
private String password;
@ApiModelProperty(notes = "주민번호를 입력해 주세요.")
private String ssn;
@OneToMany(mappedBy = "user") // user 테이블과 매핑되게 해준다
private List<Post> posts;
public User(int id, String name, Date joinDate, String password, String ssn) {
this.id = id;
this.name = name;
this.joinDate = joinDate;
this.password =password;
this.ssn = ssn;
}
}
UserJpaController
@RestController
@RequestMapping("/jpa")
public class UserJpaController {
@Autowired
private UserRepository userRepository;
@GetMapping("/users")
public List<User> retrieveAllUsers(){
return userRepository.findAll();
}
@GetMapping("/users/{id}")
public EntityModel<User> retrieveUser(@PathVariable int id){
Optional<User> user = userRepository.findById(id);
if (!user.isPresent()){ // 데이터가 없다면?
throw new UserNotFoundException(String.format("ID[%s] not found" , id));
}
EntityModel<User> model = EntityModel.of(user.get());
WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());
model.add(linkTo.withRel("all-users"));
return model;
}
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id){
userRepository.deleteById(id);
}
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user){
User saveUser = userRepository.save(user);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(saveUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}
생성한 UserRepository 클래스를 Controller와 의존성 주입을 해주고, 이전 포스팅에서는 service와 연결해서 CRUD 작업 요청을 보냈다면, 이번에는 UserRepository 클래스에 해당 매개변수와 함께 요청을 보내줍니다.
위 코드를 실행했을 때 사용자의 전체 정보, 상세 정보, 추가, 삭제 기능이 정상적으로 실행되는지 확인해 봅니다.
실행 결과
PostMan을 활용해서 결과를 알아보겠습니다.
[ MySQL 화면 ]
User 테이블에는 3개의 데이터가 들어가 있습니다.
그렇다면 해당 경로마다 결과 값을 확인해 보겠습니다.
[ /users ] ( 사용자 전체 정보 )
[ /users/{id} ] ( 해당 사용자 정보 )
[ /users ] (POST) ( 사용자 추가 )
추가적으로 MySQL 환경에서도 확인해 보겠습니다.
[ /users/{id} ] (DELETE) ( 사용자 삭제 )
마치며
오늘은 ORM을 연동하여 사용자 CRUD 기능에 대해 알아봤습니다.
다음 포스팅에서 뵙겠습니다.
'[ JAVA ] > JAVA RESTful API' 카테고리의 다른 글
[ RESTful API ] SimpleBeanPropertyFilter (0) | 2023.08.04 |
---|---|
[ RESTful API ] 사용자 수정/삭제를 위한 API 구현 - PUT/DELETE HTTP Method (0) | 2023.07.28 |
[ RESTful API ] 사용자 등록을 위한 API 구현 - POST HTTP Method (0) | 2023.07.27 |
[ RESTful API ] 사용자 목록 조회를 위한 API 구현 - GET HTTP Method (0) | 2023.07.25 |
[ RESTful API ] EntityModel 개념 및 예제 (0) | 2023.07.24 |