티스토리 뷰

728x90

1. 지난 Spring : REST + Hibernate with Java Config - CRUD 포스트와 동일한 기능을 Spring Boot으로 구현한다.

  1-1 데이터베이스와 전반적인 내용은 아래 링크를 참조한다.

 

Spring : REST + Hibernate with Java Config - CRUD 서비스 서버 구현

1. 앞의 포스트 내용을 기반으로 CRUD를 수행하는 서비스를 구현한다. 1-1 데이터베이스 구조가 동일한 이 포스트를 참조한다. Spring : Web MVC + Hibernate - 설정하기 -1. 예제를 위해 Customer 테이블을 생�

kogle.tistory.com

2. 스프링 부트는 Spring Starter Project로 만들면 편리하다. Maven Archetype과 다르게 해 줄 것이 없다.

 

3. 의존성 추가하기

  3-1 web - REST 기능을 제공한다.

  3-2 devtoos - 소스나 설정 변경 시 자동으로 서버 재기동한다.

  3-3 data-jpa, my-sql - 데이터베이스, Hibernate, JPA 지원

  3-4 lombok - bean 설정 간소화

    <!-- rest api support -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>

	<!-- auto restart  -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<scope>runtime</scope>
		<optional>true</optional>
	</dependency>
		
	<!-- database support  -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<scope>runtime</scope>
	</dependency>
	
	<!-- development convenience -->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<optional>true</optional>
	</dependency>

4. 데이터베이스 설정하기

  4-0 데이터베이스를 위해 data-jpa를 추가하였다. 

    4-0-1 JPA 표준 API로 인터페이스만 정의되어 있다. Hibernate, EclipseLink같은 구현 라이브러리들이 존재한다.

    4-0-2 스프링부트의 기본 구현라이브러리는 Hibernate이다. 원하면 로직 수정없이 다른 라이브러리로 변경가능하다.

    4-0-3 EntityManager는 Hibernate의 SessionFactory와 유사한 기능을 하고 JPA에서 표준을 지정하고 있다.

  4-1 스프링 부트는 DB설정은 데이터베이스 주소와 계정만 알려주면 된다.

    4-1-1 스프링부트는 설정파일을 가지고 DataSource, EntityManager 등의 객체를 자동생성해 준다.

spring.datasource.url=jdbc:mysql://localhost:3306/web_customer_tracker?serverTimezone=Asia/Seoul
spring.datasource.username=springstudent
spring.datasource.password=springstudent

  4-2 Customer 엔티티 설정하기

package pe.pilseong.bootcustomer.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Table(name = "customer")
@Data
public class Customer {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  
  @Column(name = "first_name")
  private String firstName;
  
  @Column(name = "last_name")
  private String lastName;
  
  @Column
  private String email;
}

5. 웹 설정하기 할 게 없다.

 

6. 데이터베이스 처리로직을 담을 CustomerDAO를 설정한다.

  6-1 EntityManager에서 Hibernate Session를 받아오는 로직 부분만 특별하다.

    6-1-1 CustomerDAO 인터페이스

package pe.pilseong.bootcustomer.dao;

import java.util.List;

import pe.pilseong.bootcustomer.entity.Customer;

public interface CustomerDAO {
  List<Customer> getCustomers();
  
  Customer getCustomer(Long id);
  
  void saveCustomer(Customer customer);
  
  void deleteCustomer(Customer id);
}

    6-1-2 CustomerDAO 구현 클래스

      6-1-2-0 스프링부트는 EntityManager를 자동생성하므로 주입하여 사용한다.

      6-1-2-1 JPA 표준에서 생성하는 EntityManager에서 unwrap을 통해서 Session객체를 얻어올 수 있다.

      6-1-2-2 나머지는 동일하다.

package pe.pilseong.bootcustomer.dao;

import java.util.List;

import javax.persistence.EntityManager;

import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import pe.pilseong.bootcustomer.entity.Customer;

@Repository
public class CustomerDAOImpl implements CustomerDAO {

  @Autowired
  private EntityManager entityManager;
  
  @Override
  public List<Customer> getCustomers() {
    Session session = entityManager.unwrap(Session.class);
    return session.createQuery("from Customer", Customer.class).getResultList();
  }

  @Override
  public Customer getCustomer(Long id) {
    Session session = entityManager.unwrap(Session.class);
    
    return session.get(Customer.class, id);
  }

  @Override
  public void saveCustomer(Customer customer) {
    Session session = entityManager.unwrap(Session.class);
    
    session.saveOrUpdate(customer);
  }

  @Override
  public void deleteCustomer(Customer customer) {
    Session session = entityManager.unwrap(Session.class);
    
    session.delete(customer);
  }
}

 

7. CustomerService 구현

  7-1 CustomerService 인터페이스이다.

package pe.pilseong.bootcustomer.service;

import java.util.List;

import pe.pilseong.bootcustomer.entity.Customer;

public interface CustomerService {
  
  List<Customer> getCustomers();
  
  Customer getCustomer(Long id);
  
  void saveCustomer(Customer customer);
  
  void deleteCustomer(Long id);
}

  7-2  CustomerService 구현 클래스

    7-2-1 저장 시에 우선 조회를 하고 존재하는 경우에 삭제를 한다.

    7-2-2 예외 처리를 위한 로직이 여기에 위치한다.

package pe.pilseong.bootcustomer.service;

import java.util.List;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import pe.pilseong.bootcustomer.dao.CustomerDAO;
import pe.pilseong.bootcustomer.entity.Customer;

@Service
public class CustomerServiceImpl implements CustomerService {

  @Autowired
  private CustomerDAO customerDAO;
  
  @Override
  @Transactional
  public List<Customer> getCustomers() {
    return this.customerDAO.getCustomers();
  }

  @Override
  @Transactional
  public Customer getCustomer(Long id) {
    Customer customer = this.customerDAO.getCustomer(id);
 
    if (customer == null) {
      throw new RuntimeException("Customer not found");
    }
    
    return customer;
  }

  @Override
  @Transactional
  public void saveCustomer(Customer customer) {
    this.customerDAO.saveCustomer(customer);
  }

  @Override
  @Transactional
  public void deleteCustomer(Long id) {
    Customer customer = this.customerDAO.getCustomer(id);
    
    if (customer == null) {
      throw new RuntimeException("Customer not found");
    }
    
    this.customerDAO.deleteCustomer(customer);
  }
}

8. 예외 처리 -

  8-0 예외 발생시에 적절한 에러 코드 매핑과 json형태의 오류구문을 전달한다.

  8-1 예외 내용을 담기 위한 클래스 작성 CustomerErrorResponse

package pe.pilseong.bootcustomer.rest;

import lombok.Data;

@Data
public class CustomerErrorResponse {
  private int statusCode;
  
  private String message;
  
  private long timestamp;
}

  8-2 전역 예외 처리 클래스

package pe.pilseong.bootcustomer.rest;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

@ControllerAdvice
public class CustomerExceptionHandler {
  
  @ExceptionHandler
  public ResponseEntity<CustomerErrorResponse> errorHandler(Exception e) {
    
    CustomerErrorResponse response = new CustomerErrorResponse();
    
    HttpStatus status = HttpStatus.NOT_FOUND;
    
    if (e instanceof MethodArgumentTypeMismatchException) {
      status = HttpStatus.BAD_REQUEST;
      response.setStatusCode(status.value());
      response.setMessage("Wrong type");
    } else {
      response.setStatusCode(status.value());
      response.setMessage("not found");
    }
     
    response.setTimestamp(System.currentTimeMillis());
    
    return new ResponseEntity<CustomerErrorResponse>(response, status);
  }
}

9. Rest 서비스를 제공하는 Rest Controller 작성

  9-1 Spring으로 작성한 로직과 동일하다.

package pe.pilseong.bootcustomer.rest;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import pe.pilseong.bootcustomer.entity.Customer;
import pe.pilseong.bootcustomer.service.CustomerService;

@RestController
@RequestMapping("/api")
public class CustomerController {

  @Autowired
  private CustomerService customerService;
  
  @GetMapping("/customers")
  public List<Customer> getCustomers() {
    return this.customerService.getCustomers();
  }
  
  @GetMapping("/customers/{id}")
  public Customer getCustomer(@PathVariable("id") Long id) {
    return this.customerService.getCustomer(id);
  }
  
  @PostMapping("/customers")
  public Customer addCustomer(@RequestBody Customer customer)  {
    
    this.customerService.saveCustomer(customer);
    
    return customer;
  }
  
  @PutMapping("/customers")
  public Customer updateCustomer(@RequestBody Customer customer) {
    this.customerService.saveCustomer(customer);
    
    return customer;
  }
  
  @DeleteMapping("/customers/{id}")
  public void deleteCustomer(@PathVariable("id") Long id) {
    this.customerService.deleteCustomer(id);
  }
  
}
728x90
댓글