CRUD

  • Create (INSERT)
  • Read (SELECT)
  • Update (UPDATE)
  • Delete (DELETE)

 

Create 구현

필요한 것

  • DB 테이블 생성(Domain → maven compile)
  • 필요한 정보를 가져다 줄 filter
  • View와 Controller(edit화면과 해당 뷰를 띄워줄 controller)
  • view와 controller를 연결할 repository, service

/java/com.OOOO/domain 폴더 아래에 본인이 만들고 싶은 domain 생성

❗ 나는 Project 도메인을 기준으로 잡았다. ❗

@Getter
@Setter
@Entity
@Table(name = "project", indexes = {})
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Project {
  //기본정보
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @EqualsAndHashCode.Include
  private Integer id;

  @JsonIgnore
  @DateTimeFormat(pattern = DateUtil.PATTERN_YMDHMS)
  @Column(nullable = false, insertable = false, updatable = false, columnDefinition = "timestamp(6) default current_timestamp(6)")
  private LocalDateTime createDate;   //생성일시

  @JsonIgnore
  @DateTimeFormat(pattern = DateUtil.PATTERN_YMDHMS)
  @Column(nullable = false, insertable = false, updatable = false, columnDefinition = "timestamp(6) default current_timestamp(6) on update current_timestamp(6)")
  private LocalDateTime updateDate;   //변경일시

  //추가정보
  @Column(nullable = false, length = 255)
  private String date; //프로젝트 진행날짜

  @Column(nullable = false)
  private String title; // 프로젝트 제목

  private String purpose; // 프로젝트 목적

  private String technology; // 프로젝트 사용기술

  private String address; // 프로젝트 깃헙주소

}

 

DB table에서 가져올 내용들을 filter로 가져온다. (/java/com.OOOO/filter)

❗ 나중에 filter를 통해 pageable 사용할 예정 ❗

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ProjectFilter {

  private String title;
  private String date;
  private String purpose;
  private String technology;
  private String address;
}

 

View 화면 생성(/resources/templates)

❗ 아래 코드는 현재 내가 진행하고 있는 프로젝트 코드이므로 그냥 바로 적용하면 깨질 것임. 따라서 알맞게 맞춰서 바꾸어줘야 함! ❗

<!DOCTYPE HTML>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">

<head>
  <th:block th:replace="fragments/head :: head"></th:block>
  <link rel="stylesheet" th:href="@{/static/assets/css/project-edit.css}" type="text/css"/>
  <style>
    h2 {
      font-weight: bold;
      margin-bottom: 20px;
    }
  </style>
</head>

<body class="is-preload">
<div id="wrapper">
  <th:block th:replace="fragments/top :: top"></th:block>
  <div id="main">
    <article id="work" class="panel">
      <h2>Project ADD</h2>
      <form method="post"
            th:action="|@{/project/save}|" th:object="${project}">
        <input th:field="*{id}" type="hidden"/>
        <button class="saveButton" type="submit" th:text="|save|"></button>
        <div class="mb-3">
          <label class="form-label" for="date">Progress Date</label>
          <input type="text" class="form-control" name="date" id="date" th:field="*{date}"
                 placeholder="프로젝트 진행 날짜를 입력해 주세요">
        </div>
        <div class="mb-3">
          <label class="form-label" for="title">Project Title</label>
          <input type="text" class="form-control" name="title" id="title" th:field="*{title}"
                 placeholder="프로젝트 제목을 입력해 주세요">
        </div>
        <div class="mb-3">
          <label class="form-label" for="purpose">Project Purpose</label>
          <input type="text" class="form-control" name="purpose" id="purpose" th:field="*{purpose}"
                 placeholder="프로젝트 목적을 입력해 주세요">
        </div>
        <div class="mb-3">
          <label class="form-label" for="content">Used Technology</label>
          <textarea class="form-control" rows="5" name="content" id="content"
                    th:field="*{technology}"
                    placeholder="사용한 기술을 입력해 주세요"></textarea>
        </div>
        <div class="mb-3">
          <label class="form-label" for="address">Github Address</label>
          <input type="text" class="form-control" name="address" id="address" th:field="*{address}"
                 placeholder="Github 주소를 입력해 주세요">
        </div>
      </form>
    </article>
  </div>
</div>
<th:block th:replace="fragments/footer :: footer"></th:block>
<th:block th:replace="fragments/common-script :: common-script"/>
</body>
</html>

 

이제 view와 연결 지을 controller 구현(/java/com.OOOO/controller)

❗ edit화면을 띄울 것과 적은 내용을 save 해서 DB에 저장이 되어야 함 ❗

@Controller
@RequiredArgsConstructor
@RequestMapping("/project")
public class ProjectController {

 @NonNull
  private final ProjectService projectService;
  
   @GetMapping({"/edit", "/edit/{id}"})
  public String edit(@PathVariable(required = false) Integer id, Model model) {

    Project project = (id == null) ? new Project() : projectService.findById(id).orElseThrow(
        DataNotFoundException::new);

    model.addAttribute("project", project);

    return "project/edit";
  }
  
   @PostMapping("/save")
  public String save(@ModelAttribute("project") Project project, BindingResult result, Model model,
      RedirectAttributes redirectAttr) {

    if (!result.hasErrors()) {
      projectService.save(project);
      redirectAttr.addFlashAttribute("message", "저장되었습니다.");
      return "redirect:/project/edit/" + project.getId();
    }
    model.addAttribute("project", project);
    return "project/edit";
  }
  
}

 

controller 안에 써있는 함수들을 구현해줘야 함!

제일 먼저 Repository 생성(/java/com.OOOO/repository)

@Repository
public interface ProjectRepository extends JpaRepository<Project, Integer>,
    CustomProjectRepository {
}

다음으론 customRepository 생성(/java/com.OOOO/repository/custom)

❗ findAllByFilter : filter를 통해 페이지를 구성할 계획 / 이것은 list를 뽑아낼 때 필요한 것 ❗

@NoRepositoryBean
public interface CustomProjectRepository {

  Page<Project> findAllByFilter(Pageable pageable, ProjectFilter filter);

}

마지막으론 RepositoryImpl 생성(/java/com.OOOO/repository/support)

❗ querydsl사용으로 코드로 db 코드를 만질 수 있다! ❗

public class ProjectRepositoryImpl extends QuerydslRepositorySupport implements
    CustomProjectRepository {

  private final QProject project = QProject.project;

  public ProjectRepositoryImpl() { super(Project.class); }

  @Override
  public Page<Project> findAllByFilter(Pageable pageable, ProjectFilter filter) {
    BooleanBuilder builder = new BooleanBuilder();

    if (!StringUtils.isBlank(filter.getDate())) {
      builder.and(project.date.containsIgnoreCase(filter.getDate()));
    }
    if (!StringUtils.isBlank(filter.getTitle())) {
      builder.and(project.title.containsIgnoreCase(filter.getTitle()));
    }
    if(!StringUtils.isBlank(filter.getAddress())){
      builder.and(project.address.containsIgnoreCase(filter.getAddress()));
    }
    if(!StringUtils.isBlank(filter.getTechnology())){
      builder.and(project.technology.containsIgnoreCase(filter.getTechnology()));
    }
    if(!StringUtils.isBlank(filter.getAddress())){
      builder.and(project.purpose.containsIgnoreCase(filter.getPurpose()));
    }

    final JPQLQuery<Project> query = from(project).where(builder);

    List<Project> result = getQuerydsl().applyPagination(pageable, query).fetch();

    return new PageImpl<>(result,pageable,query.fetchCount());

  }

}

 

repository 구성이 끝났다면 이를 controller에 가져다 사용할 수 있게 service를 구현(/java/com.OOOO/repository/service)

@Service
@RequiredArgsConstructor
public class ProjectService {

  @NonNull
  private final ProjectRepository projectRepository;

  public Optional<Project> findById(Integer id) { return projectRepository.findById(id); }

  public Page<Project> findAllByFilter(Pageable pageable, ProjectFilter filter) {
    return projectRepository.findAllByFilter(pageable, filter);
  }

  public Project save(Project project) {
    return projectRepository.save(project);
  }
}

 

현재까지 진행하면 나오는 결과

edit 화면

save 버튼을 누르면

DB 결과

DB에 저장이 됨을 확인할 수 있다.

 

 

❗ 혼자 진행 중인 프로젝트를 가져온 거라 아마 본인에 맞게 고쳐야 할 부분이 많을 것이다! 그래도 저 순서대로 하면 글 작성과 저장까지는 구현하는데 도움이 될지도...? 다음 포스팅은 R에 관해 구현할 예정 ❗

'Web > Spring' 카테고리의 다른 글

[설정] application.properties  (0) 2021.08.04
[CRUD] R구현  (0) 2021.08.02
[JPA] 데이터베이스 생성 또는 초기화  (0) 2021.08.02
[ERROR] Pageable import  (0) 2021.08.02
[ERROR] Querydsl import  (0) 2021.08.02

스프링을 처음 실행시키거나 DB를 초기화시킬 때 

datasource:
	initialization-mode: always
    
  jpa:
    hibernate:
      ddl-auto: create

위의 코드로 실행시켜주고

 

datasource:
	initialization-mode: never
    
  jpa:
    hibernate:
      ddl-auto: update

그 다음에 다시 바꾸어준다!

 

hibernate의 ddl-auto

  • udpate : 기존의 스키마를 유지하며 JPA에 의해 변경된 부분만 추가
  • validate : 엔티티와 테이블이 정상적으로 매핑되어있는지만 검증
  • create : 기존에 존재하는 스키마를 삭제하고 새로 생성
  • create-drop : 스키마를 생성하고 애플리케이션이 종료될 때 삭제
  • none : 초기화 동작을 하지 않음

 

❗ 도메인 파일에서 변경이 있을 때 해주면 좋다! local환경일 경우 local에서도 적용을 해줘야 하는 듯.. ❗

'Web > Spring' 카테고리의 다른 글

[CRUD] R구현  (0) 2021.08.02
[CRUD] C구현  (0) 2021.08.02
[ERROR] Pageable import  (0) 2021.08.02
[ERROR] Querydsl import  (0) 2021.08.02
[Util] ControllerNameInterceptor  (0) 2021.07.30

Controller에서 Pageable을 파라미터로 받아 사용하기 위해 import 되어야 할 것은

import org.springframework.data.domain.Pageable;

위의 코드와 같다!!

'Web > Spring' 카테고리의 다른 글

[CRUD] C구현  (0) 2021.08.02
[JPA] 데이터베이스 생성 또는 초기화  (0) 2021.08.02
[ERROR] Querydsl import  (0) 2021.08.02
[Util] ControllerNameInterceptor  (0) 2021.07.30
[배경] Querydsl  (0) 2021.07.28

BooleanBuilder를 찾을 수 없다고 오류가 뜬다. 이 때에는 import를 확인해봐야한다.

import com.querydsl.core.BooleanBuilder;

위와 같이 경로가 정확한지 판단한다!! querydsl에 대한 의존성을 pom.xml에 추가해두지 않으면 임의로 import 하기 때문에 pom.xml 파일을 꼭 확인해보길..

    <!--    Querydsl-->
    <dependency>
      <groupId>com.querydsl</groupId>
      <artifactId>querydsl-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>com.querydsl</groupId>
      <artifactId>querydsl-apt</artifactId>
      <scope>provided</scope>
    </dependency>
    
     <!--      Querydsl-->
      <plugin>
        <groupId>com.mysema.maven</groupId>
        <artifactId>apt-maven-plugin</artifactId>
        <version>1.1.3</version>
        <executions>
          <execution>
            <goals>
              <goal>process</goal>
            </goals>
            <configuration>
              <outputDirectory>target/generated-sources/java</outputDirectory>
              <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
          </execution>
        </executions>
      </plugin>

 

❗ Query관련 Domain 설정 또한 의존성이 없으면 maven compile 해도 파일이 자동 생성이 안된다. ❗

'Web > Spring' 카테고리의 다른 글

[JPA] 데이터베이스 생성 또는 초기화  (0) 2021.08.02
[ERROR] Pageable import  (0) 2021.08.02
[Util] ControllerNameInterceptor  (0) 2021.07.30
[배경] Querydsl  (0) 2021.07.28
[배경] JPA  (0) 2021.07.27

템플릿마다 폴더 구성이 다르기 때문에 먼저 main화면부터 띄워서 구성을 확인해보는 게 좋다.

보통 index.html 파일이 메인화면일 가능성이 크다.

 

  • /resources 안으로 본인의 환경과 구성에 맞게 폴더들을 생성하고 파일들을 집어 넣어주면 된다.
    • 먼저 statictemplates 폴더를 생성한다.
    • templates에 뷰화면을 띄우는 html을 집어넣는다.
    • static에 css, image, js 등의 파일들을 넣는다.
  • /resources/templates안
    • controller에 맞게 폴더들 생성(뷰 화면)
    • fragments 폴더 생성(top menu나 footer와 같은 매 뷰마다 들어가는 것들)
  • /resources/static안
    • css, js, images, font를 자신의 생각대로 구성해 넣는다.

예시

 

현재 진행하고 있는 프로젝트의 resources폴더이다.

본인이 먼저 어떻게 정리할 건지 생각하고 진행하면 좋겠다.

 

❗ fragments 파일을 생성해두면 뷰 화면의 코드를 줄일 수 있어 좋다:) ❗

'Web > Front' 카테고리의 다른 글

[HTML, CSS] icon삽입  (0) 2021.08.02
[Javascript] window.location 객체  (0) 2021.07.30
[HTML] HTML 템플릿 다운로드 사이트  (0) 2021.07.30
[ERROR] 500에러 관련  (0) 2021.07.30
[ERROR] static파일 적용 관련  (0) 2021.07.30

이는 front와 연결 짓기 위한 util파일이다.

간단히 설명하면, controller안에 있는 이름을 가져와서 javascript window.controllerName에 집어넣는다.

public class ControllerNameInterceptor extends HandlerInterceptorAdapter {

  @Override
  public void postHandle(HttpServletRequest request, @NonNull HttpServletResponse response,
      Object handler, ModelAndView modelAndView) throws Exception {
    super.postHandle(request, response, handler, modelAndView);

    String controllerName = "";
    String methodName = "";

    if (handler instanceof HandlerMethod) {
      HandlerMethod handlerMethod = (HandlerMethod) handler;
      controllerName = handlerMethod.getBeanType().getSimpleName().replace("Controller", "");
      methodName = handlerMethod.getMethod().getName();
    }

    String queryString = request.getQueryString() != null ? "?" + request.getQueryString() : "";
    request.setAttribute("queryString", queryString);
    request.setAttribute("controllerName", controllerName);
    request.setAttribute("methodName", methodName);
  }
}

위와 같이 util 폴더에 ControllerNameInterceptor.java파일을 만든다.

그 후 아래와 같이 front쪽 스크립트 필요한 곳에 넣어준다.

  • fragments/head.html : 이 파일은 모든 css 파일들을 모아둔 파일이다.
  <script th:inline="javascript">
    /*<![CDATA[*/
    window.contextRoot = /*[[ @{/} ]]*/'/';
    window.controllerName = /*[[ ${controllerName} ]]*/'';
    const numberFormatter = Intl.NumberFormat('en-US');
    /*]]>*/
  </script>

 

 

  • fragments/common-script.html : 이 파일은 모든 js 파일들을 모아둔 파일이다.
  <script th:inline="javascript">
    /*<![CDATA[*/

    var controllerName = /*[[ ${controllerName} ]]*/'';

    $(document).on('click', '.menu', function () {
      window.controllerName = $(this).data('menuname');
    });

    let nav = $('#nav');
    const nav_link = nav.children('a');

    nav_link.each(function (index, el) {
      if ($(el).data('menuname') === controllerName) {
        $(el).addClass('active');
      } else {
        $(el).removeClass('active');
      }
    })


  </script>

위의 코드와 같이 controllerName을 받아올 수 있다.

 

❗ 단, controller폴더 안 파일 이름 설정을 controllerName + Controller라고 정확하게 해줘야한다.

ex) HomeController라고 하면 controllerName은 Home이다! ❗

'Web > Spring' 카테고리의 다른 글

[ERROR] Pageable import  (0) 2021.08.02
[ERROR] Querydsl import  (0) 2021.08.02
[배경] Querydsl  (0) 2021.07.28
[배경] JPA  (0) 2021.07.27
[배경] Maven  (0) 2021.07.27

+ Recent posts