Miracle Morning, LHWN

8-2. 프로젝트에서 JPA 직접 사용해보기 본문

IT 기술/[JAVA] Spring Boot

8-2. 프로젝트에서 JPA 직접 사용해보기

Lee Hye Won 2021. 5. 11. 05:24

DB 에 테이블 생성이 확인되면 Car 에 대한 클래스를 정의해준다.

 

이때

(1) @Table annotation 을 통해 어느 테이블과 매핑할지 설정한다.

(2) 식별자는 DB 에게 위임하기 위해 @GeneratedValue(strategy = GenerationType.IDENTITY) 로 설정한다.

(3) 제조사 하나에 여러 자동차가 있을 수 있기 때문에 Company 요소에 @ManyToOne annotation 을 설정한다.

(4) 승객수는 0명 일수도 있으니까 int 가 아닌 Wrapper class 인 Integer 로 설정한다.

(5) 모든 getter/setter 를 직접 설정해주기 번거로우니 lombok 을 사용하여 @Data annotation 을 설정한다.

 

Car.java

package com.springPractice.carInfo.domain;

import lombok.Data;

import javax.persistence.*;

@Entity
@Table(name="car")
@Data
public class Car {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  // 모델명
  @Column(name="model_name")
  private String modelName;

  @ManyToOne
  @JoinColumn(name="company_id")
  private Company company;

  @Column(name="passenger_capacity")
  private Integer passengerCapacity;
  
  @Column(name="created_at")
  @Temporal(value=TemporalType.TIMESTAMP)
  private Date createdAt;

  @Column(name="updated_at")
  @Temporal(value=TemporalType.TIMESTAMP)
  private Date updatedAt;
}

이때 created_at 과 updated_at 은 다른 테이블과 중복될 여지가 많아 @MappedSuperClass 를 사용하여 코드 중복을 줄인다.

 

BaseEntity.java

package com.springPractice.carInfo.domain;

import lombok.Data;

import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;

@MappedSuperclass
@Data
public abstract class BaseEntity {
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date createdAt;

    @Temporal(value = TemporalType.TIMESTAMP)
    private Date updatedAt;
}

BaseEntity 를 만들어주었으니 Car 에서 BaseEntity 를 확장해준다.

 

Car.java

package com.springPractice.carInfo.domain;

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name="car")
@Data
public class Car extends BaseEntity{
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  // 모델명
  @Column(name="model_name")
  private String modelName;

  @ManyToOne
  @JoinColumn(name="company_id")
  private Company company;

  @Column(name="passenger_capacity")
  private Integer passengerCapacity;
}

 이제 기본 화면 index.html 을 작성하는데, thymeleaf 를 사용한다.

Thymeleaf ?

Java 라이브러리로, Spring Boot 가 자동 설정을 지원하는 웹 텝플릿 엔진이다. HTML 문서에 HTML5 문법으로 서버 쪽 로직을 수행하고 적용시킬 수 있다. HTML 디자인에 전혀 영향을 미치지 않고 웹 템플릿 엔진을 통해 HTML 을 생성할 수 있다.

BasicController 에서는 "/" 경로에 들어왔을 때에는 index.html 페이지를 보여주도록 하고,

"/companyForm" 경로에 들어왔을 때에는 companyForm.html 페이지를,

"/saveCompany" 경로에 들어왔을 때에는 Service 의 saveCompanyInputDto 를 실행 후 index.html 로 돌아가게 한다.

BasicController.java

package com.springPractice.carInfo.controller;

import com.springPractice.carInfo.service.CompanyInputDTO;
import com.springPractice.carInfo.service.CompanyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/")
public class BasicController {
    @Autowired
    CompanyService companyService;

    @RequestMapping("/")
    public String index(Model model) {
        return "index";
    }

    @RequestMapping("/companyForm")
    public String companyForm(Model model) {
        return "companyForm";
    }

    @RequestMapping("/saveCompany")
    public String saveCompany(@ModelAttribute(name="companyInputDto") CompanyInputDTO companyInputDTO, Model model) {
        companyService.saveCompanyImputDto(companyInputDTO);
        return "index";
    }

}

Service 에서는 Company 객체에 "companyInputDto 의 프로퍼티 + CreatedAt + UpdatedAt" 을 만들어 save 해준다.

CompanyService.java

package com.springPractice.carInfo.service;

import com.springPractice.carInfo.domain.Company;
import com.springPractice.carInfo.repository.CompanyRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.Date;

@Service
@Transactional
public class CompanyService {
    @Autowired
    CompanyRepository companyRepository;

    @Transactional
    public Company saveCompanyInputDto(CompanyInputDTO companyInputDto) {
        Company company = new Company();
        company.setCompanyName(companyInputDto.getCompanyName());
        company.setCompanyNation(companyInputDto.getCompanyNation());
        company.setCreatedAt(new Date());
        company.setUpdatedAt(new Date());

        return companyRepository.save(company);
    }

    public Company save(Company company) {
        return companyRepository.save(company);
    }
}

CompanyRepository 는 JpaRepository 를 상속받아 확장하는데, 이때 Generic 에는 Entity 와 Primary Key 의 타입을 넣어준다.

이로써 별도의 코드 작성 없이 Jpa 를 통해서 save 가 가능하게 된다.

 

CompanyRepository.java

package com.springPractice.carInfo.repository;

import com.springPractice.carInfo.domain.Company;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CompanyRepository extends JpaRepository<Company, Long> {
    
}

 

index.html 페이지에는 간단한 문구와 /companyForm 에 작성해놓은 등록 Form 으로 이동하는 Button 을 달아준다.

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>HELLO CAR!</h1>
<a href="./companyForm">Company Register</a>
</body>
</html>

companyForm 에는 Name, Nation 을 입력받는 항목과 submit 을 누르면 /saveCompany 로 이동될 수 있도록 한다.

companyForm.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<h1>COMPANY FORM</h1>
<form method="POST" th:action="@{/saveCompany}"
      th:object="${companyInputDto}">
<div class="form-group">
  <label for="companyName">Company Name</label>
  <input th:name="companyName"
         id="companyName"
         type="text"
         class="form-control"
         name="companyName" />

  <label for="companyName">Company Nation</label>
  <input th:name="companyNation"
         id="companyNation"
         type="text"
         class="form-control"
         name="companyNation" />
</div>
  <div class="form-group">
    <button type="submit" class="btn btn-primary btn-block">
      REGISTER
    </button>
  </div>

</form>
</body>
</html>

그리고 서버가 구동될 때마다 테이블 내용 또한 초기화되어 없어지기 때문에 Construct 가 완료된 이후에 자동으로 메서드가 실행되도록

@PostConstruct annotation 을 통해 설정해준다.

AppInitializer.java

package com.springPractice.carInfo;

import com.springPractice.carInfo.domain.Company;
import com.springPractice.carInfo.service.CompanyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Date;

@Component
public class AppInitializer {
    @Autowired
    CompanyService companyService;

    @PostConstruct
    private void init() {
        System.out.println("AppInitializer Start...");

        Company company = new Company();
        company.setUpdatedAt(new Date());
        company.setCreatedAt(new Date());
        company.setCompanyName("Ferrari");
        company.setCompanyNation("Italy");

        Company company2 = new Company();
        company2.setUpdatedAt(new Date());
        company2.setCreatedAt(new Date());
        company2.setCompanyName("lamborghini");
        company2.setCompanyNation("Italy");

        Company company3 = new Company();
        company3.setUpdatedAt(new Date());
        company3.setCreatedAt(new Date());
        company3.setCompanyName("BMW");
        company3.setCompanyNation("Germany");

        Company company4 = new Company();
        company4.setUpdatedAt(new Date());
        company4.setCreatedAt(new Date());
        company4.setCompanyName("Hyundai");
        company4.setCompanyNation("Korea");

        companyService.save(company);
        companyService.save(company2);
        companyService.save(company3);
        companyService.save(company4);

    }
}

 

 

출처 : fastcampus.co.kr/courses/204729/clips/

Comments