프로그래밍

[웹 프로그래밍] 스프링부트JPA 1. 데이터를 클래스화 하기. 자동 생성.

tt2t2am1118 2023. 1. 1. 16:48
반응형

공부해봅시다.

 

오늘은 클래스 형식으로 데이터 만들어보기 입니다. 스프링부트를 통해서, 엔티티클래스를 만들면, 자동화를 통해 프로그래밍을 더 쉽게 할 수 있습니다.

 

 

 

 

 백엔드와 프론트엔드로 프로그래밍을 구분할 수 있겠죠. 여기에 서로 주고 받는 데이터. 그 데이터를 엔티티클래스 사용해서 만드는 것이죠. Restful 컨트롤러를 사용해서, JSON형식으로 백엔드, 프론트엔드. 데이터를 주고 받게 만들 수 있습니다.

 

오늘, 일 하시는 것은 어떤가요. 좋은 하루되세요. (infott2t.github.io)

 

 

여기 Git을 import해보세요. 인텔리제이에서, File, New, Get from Version Control. 여기에 Github주소를 적어넣으면 되겠습니다.

https://github.com/infott2t/ex05-springboot-querydsl

 

실제 위의 Github에 만들면서 push를 했기 때문에, 완성된 상태이예요. 자동화코드사용법. 참조해보면서 읽어보세요. 만들어져 있으니, 코드를 적을 필요는 없습니다. 실제, 웹프로그래밍에서 테이블추가할 때에, 아래의 방법을 사용하게 되는것이죠. 스프링부트JPA의 사용이 처음이라면, 오히려 어려울 수 있구요. 사용해봤다면, 다음에 테이블, 엔티티를 추가할 때라던지.. 쉽게 코드를 생성할 수 있습니다. 

 

 MySQL을 사용했습니다. 사용자를 추가해주고, application.properties의 내용을 아래처럼 해주세요.

#데이터베이스 생성 EXAMPLE0
CREATE DATABASE EXAMPLE0 DEFAULT CHARACTER SET utf8;

#유저 생성 admin0
CREATE USER 'admin0'@'localhost' IDENTIFIED WITH mysql_native_password BY '1234';

USE EXAMPLE0;

 

이 부분을 이렇게 바꿔주세요. username과 password.

 

application.properties 파일.

# MySQL ??
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# DB Source URL
spring.datasource.url=jdbc:mysql://localhost:3306/example0?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul

# DB username
spring.datasource.username= admin0

# DB password
spring.datasource.password= 1234

# true ??? JPA ??? ?? ??
spring.jpa.show-sql=true

# DDL(create, alter, drop) ??? DB? ?? ??? ??? ? ??.  # ??? create? ??, ??? update? ????.
spring.jpa.hibernate.ddl-auto=update

# JPA? ???? Hibernate? ????? ??? SQL? ???? ????.
spring.jpa.properties.hibernate.format_sql=true

#Thymeleaf Setting.
spring.thymeleaf.prefix=classpath:templates/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.cache=false
spring.thymeleaf.order=0

 

여기 글 아래부터는 참조해보세요. Git에 다 올라간 내용이군요.

 

 

 

 또, 추천하는 것은 Github Copilot이라는 것이있죠. 인텔리제이에서 사용하면, 스스로 코드를 만드는데, Tab키로 쉽게 코드를 이어 붙일 수 있구요. #Github #GithubCopilot

 

<h5> A 푸드. 음식. 김치를 만들어보세요.</h5>
    <br />
    <div class="row  justify-content-center">
      <div class="col d-flex justify-content-center">
        <div class="card" style="width: 18rem;">

          <div class="card-body">
            <h5 class="card-title">A 푸드</h5>
            <p class="card-text">배추 김치 만들기</p>
            <a href="kimchi.html" class="btn btn-primary">보기</a>
          </div>
          <div class="card-footer">
            음식, 요리 | 2021년 6월 21일 ~
          </div>
        </div>
      </div>
      <div class="col d-flex justify-content-center">
        <div class="card" style="width: 18rem;">
          <div class="card-body">
            <h5 class="card-title">A 푸드</h5>
            <p class="card-text">제품 운반, 적재하기</p>
            <a href="#" class="btn btn-primary disabled">보기</a>
          </div>
          <div class="card-footer">
            음식, 창고 | 2021년 6월 21일 ~
          </div>
        </div>
      </div>

      <div class="col  d-flex justify-content-center">
        <div class="card" style="width: 18rem;">

          <div class="card-body">
            <h5 class="card-title">A 푸드</h5>
            <p class="card-text">제품 포장하기</p>
            <a href="#" class="btn btn-primary disabled">보기</a>
          </div>


          <div class="card-footer">
            음식, 포장 | 2021년 6월 21일 ~
          </div>
        </div>
      </div>

    </div>

코드를 보면, 내용은 다른데, 포멧은 같다. 이렇게 생각해볼 수 있습니다. 한블럭을 보자면...

 

 

클래스화 해봄니다. Json을 자동으로 만들어 줄테니까요. 나중에 그렇게 사용하겠죠~.

 

id는 Long형태. 그 다음, 회사이름, 서비스 제목, 태그, 시작일. 이렇게 되어있는데요. 엔티티클래스를 하나 만들어 줌니다.

 

 

package org.example.domain.serv.workplan;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Getter
@Setter
@RequiredArgsConstructor
@Table(name="T_WORKPLAN")
public class WorkPlan {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "T_WORKPLAN_ID")
    private Long id;    //아이디

    private String workPlanCooperation; //협력사

    private String workPlanTitle;  //제목

    private String workPlanTag; //태그

    private LocalDateTime workPlanStartDate;    //시작일

    private LocalDateTime crateDate;    //  생성일

    private LocalDateTime updateDate; //  수정일

    private String workPlanStatus;  //상태 N인 경우, 서비스 중단의 경우.


    //private ServView servView;  //서비스 뷰 보기버튼을 눌렸을 때.


}

위처럼 만들면, 테이블을 스프링부트JPA가 자동으로 만들어 줌니다. ServView의 경우, 버튼을 눌린 후, 자세한 정보가 실릴텐데요. 아직 테이블을 만들지 않았으니 주석처리를 해줌니다.

 

실제 데이터를 넣어봅시다. 제가 만든, QueryDSL 생성기를 사용해서 만들어 보자면...

https://github.com/infott2t/SpringAutoCodeJPAEntity3

 

인텔리제이에서, File, New, Get from Version Control. 여기에 Github주소를 적어 넣고, 새창으로 만들어 줌니다. 사용방법은, https://blog.naver.com/tt2t2am1118/222823741903 여기를 참조해보세요.

 

v3버젼을 실행해주고, 위처럼 적어줌니다. 변수명에, Long, String, LocalDateTime. 이 세개의 변수는 최소 1개씩은 들어가야 생성이 되요. 맵핑이 되는 클래스. serView의 경우는 자동생성되지 않기때문에 안적어야 합니다. 맵핑의 경우는, 일반적으로 참조하는 테이블의 id만 가져오게 되는 군요. 다음 시간에 해볼게요.또, 더 커지는 엔티티에 적어야하기 때문에, SerView엔티티에서, WorkPlan 테이블의 id를 가져오게 되겠죠. 

 

 여러 파일이 생성 된 것을 알 수 있습니다. 파일들은, c:\category. 해당 폴더에 저장이 됨니다.

위의 파일들을 전부사용하는 것은 아니구요. 일부 일부. 필요한 파일을 사용하게 되겠죠. 위의 파일을 인텔리제이. 해당 도메인. 여기서는 workplan이 되겠죠. 그안에 다 집어넣습니다.

WorkPlanHashMapPrint.java, WorkPlanSetMethod.java 이 파일은 txt로 바꿔주거나 지워주세요.

 

지워야할 파일, WorkPlanTbApiDto.java, WorkPlanUpdateForm, WorkPlanSaveForm. 그리고, 각 파일들에 들어가서, 패키지 임포트를 시켜줌니다. 또, Ctrl+space키를 사용해서, 빨간색 글자. import가 되지 않은 클래스들을 임포트 시켜주세요.

 

package org.example.domain.serv.workplan;

RepositoryCustom에서, WorkPlanApiDto를 참조하게 해주세요.

package org.example.domain.serv.workplan;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

public interface WorkPlanRepositoryCustom {

    Page<WorkPlanApiDto> searchAllV2(WorkPlanSearchCondition condition, Pageable pageable);

  List<WorkPlanApiDto> searchFindAllDesc();


}

RepositoryImpl에서도 WorkPlanApiDto를 참조하게 바꿔줌니다. 여기서, static import의 경우, 이렇게 바꿔주세요.

import static org.example.domain.serv.workplan.QWorkPlan.workPlan;

searchAllV2 메소드에서, QueryProjection을 사용하게 해줌니다. 마지막에 콤마를 지워줌니다.

 @Override
    public Page<WorkPlanApiDto> searchAllV2(WorkPlanSearchCondition condition, Pageable pageable) {

        List<WorkPlanApiDto> content = queryFactory.
                select(Projections.constructor(WorkPlanApiDto.class,
                        workPlan.id    ,
                        workPlan.workPlanCooperation,
                        workPlan.workPlanTitle ,
                        workPlan.workPlanTag ,
                        workPlan.workPlanStatus ,
                        workPlan.workPlanStartDate  ,
                        workPlan.crateDate    ,
                        workPlan.updateDate
                )).from(workPlan)
                .where(
                     //   searchAllV2Predicate(condition)
                )
                .orderBy(workPlan.id.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        long total = queryFactory
                .select(workPlan.count())
                .from(workPlan)
                .where(
                    //    searchAllV2Predicate(condition)
                )
                .fetch().get(0);

        return new PageImpl<>(content, pageable, total);
    }

그리고 다음 파일... WorkPlanRestController.java파일에는 '.' 점이 찍혀있어요. 이 점을 지워줌니다.

그다음 QueryDSL을 다시 인식할 수 있도록, 빌드를 다시 해줌니다. 아까전의 WorkPlanRepositoryImpl의 파일을 한번 읽어주게 만들어야 하는 군요.

compileJava로 다시 빌드를 하게 되면, 글자색이 바뀌면서 인식된 것을 확인해볼 수 있습니다.

글자 색이 바꼈어요.

이렇게 만들면, WorkPlan이라는 엔티티를 사용할 수 있게 됨니다. 그럼, 데이터를 넣어볼까요? WorkPlan 클래스에, Builder어노테이션을 만들어 줌니다.

 

 

@Entity
@Getter
@Setter
@RequiredArgsConstructor
@SuperBuilder
@Table(name="T_WORKPLAN")
public class WorkPlan {

@SuperBuilder라는 어노테이션만 위에 적어주면 됨니다.

 

 

 

데이터를 Insert해야하기 때문에, 빌더를 사용해줌니다. 엔티티의 @SuperBuilder라는 어노테이션때문에, 쉽게 데이터를 추가할 수 있습니다.  

package org.example;

import lombok.RequiredArgsConstructor;
import org.example.domain.member.MemberDto;
import org.example.domain.member.MemberService;
import org.example.domain.serv.workplan.WorkPlan;
import org.example.domain.serv.workplan.WorkPlanRepository;
import org.example.domain.serv.workplan.WorkPlanRepositoryImpl;
import org.example.domain.serv.workplan.WorkPlanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.time.LocalDateTime;

@RequiredArgsConstructor
@Controller
public class BaseController {
    @Autowired
    private MemberService memberService;
    @Autowired
    private WorkPlanService workPlanService;
    @Autowired
    private WorkPlanRepository workPlanRepository;

    @GetMapping("/")
    public String indexDefault(Model model){

        LocalDateTime insertDate = LocalDateTime.of(2023,6,1,0,0,0);
        LocalDateTime now = LocalDateTime.now();

        WorkPlan workPlan = WorkPlan.builder()
                .workPlanTitle("배추 김치 만들기")
                .workPlanCooperation("A 푸드")
                .workPlanStatus("Y")
                .workPlanTag("음식, 요리")
                .workPlanStartDate(insertDate)
                .crateDate(now)
                .updateDate(now)
                .build();

        WorkPlan workPlan0 = WorkPlan.builder()
                .workPlanTitle("제품 운반, 적재하기")
                .workPlanCooperation("A 푸드")
                .workPlanStatus("Y")
                .workPlanTag("음식, 창고")
                .workPlanStartDate(insertDate)
                .crateDate(now)
                .updateDate(now)
                .build();

        WorkPlan workPlan1 = WorkPlan.builder()
                .workPlanTitle("음식 재료 다듬기")
                .workPlanCooperation("B 푸드")
                .workPlanStatus("Y")
                .workPlanTag("음식, 요리")
                .workPlanStartDate(insertDate)
                .crateDate(now)
                .updateDate(now)
                .build();

        WorkPlan workPlan2 = WorkPlan.builder()
                .workPlanTitle("음식 재료 만들기")
                .workPlanCooperation("B 푸드")
                .workPlanStatus("Y")
                .workPlanTag("음식, 요리")
                .workPlanStartDate(insertDate)
                .crateDate(now)
                .updateDate(now)
                .build();

        workPlanRepository.save(workPlan);
        workPlanRepository.save(workPlan0);
        workPlanRepository.save(workPlan1);
        workPlanRepository.save(workPlan2);

        model.addAttribute("workPlanList", workPlanService.searchFindAllDesc());

        return "index_leaf";
    }

    @GetMapping("/signup")
    public String signupForm(Model model){
        model.addAttribute("member", new MemberDto());
        return "signupForm";
    }

    @GetMapping("/test/board")
    public String testBoard(){
        return "test/board";
    }

    @PostMapping("/signup")
    public String signup( MemberDto memberDto){
        memberService.signup(memberDto);
        return "redirect:/";
    }

    @GetMapping("/login")
    public String login(){
        return "login";
    }
}

컨트롤러의 메소드. index()가 처음페이지로 이동할 때에 들어가게 되는 메소드인데요. 여기에 Model을 웹페이지로 전달해주는 것이죠. workPlanList라는 이름을 붙였네요. 해당, index페이지에서 이 데이터를 가져옴니다.

 

index_leaf.html의 파일의 경우, model을 가져와야 겠죠~.

 

 

타임리프를 사용하는 군요. 이어서 적어볼게요~. 공부해보세요~.

 

 

 

--

저의 글, 봐 주셔서 감사합니다.

 

반응형