(복습)spring 총 정리

서지우·2023년 8월 2일

Spring Boot

목록 보기
15/18

spring 총 정리

테이블 만들기

site 데이터베이스에서 테이블을 다 삭제한다.

그리고 user 테이블을 만들고 컬럼을 만들어 save를 해준다.

persist를 눌려준다.

오류가 뜬다..기본키 설정 안했기 때문에 오류가 났다.

idx컬럼명에서 마우스 우측을 눌려 new constraint를 해준다.

constraints에 들어가면 기본키가 설정된 것을 볼 수 있다.

post테이블을 만들어 위와 똑같이 해준다. 다하고 properties에 들어가 면 아래의 사진과 같아야 한다.

user테이블에서 row추가를 해서 데이터를 넣고 save해준다.

post도 마찬가지로 데이터 넣고 save해준다.

post테이블에서 외래키를 설정해준다.

post의 useridx랑 user의 idx연결해준다.

fk를 가지고 있는 건 post고 참조하는게 user다save함


spring initializ 만들기

서버를 만들기 위해 spring initializr을 만든다.

프로젝트를 만들 때는 com.~.~ 이렇게 된다.
패키지명은 거꾸로 만든다.


site2 프로젝트

build.gradle

프로젝트 세팅에 대해 적혀있다.

dependencies {
	// 이게 젤 중요
	// 원하는 라이브러리 입력만 적어도 줘도 알아서 세팅해줌
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

주석적고 나서 프로젝트 다시 로드해야 한다.

원래는 다 다운로드해서 세팅해줘야 하는데 스프링 부트를 사용하면 아래의 이런 것들을 다 신경쓰지 않아도 됨.


application.yml

실행해보면 url오류 난다

properties를 shift + f6을 눌러 yml로 변경한다.

connection에 들어가 url을 클릭해 주소를 가져온다.

jdbc:mariadb://localhost:3306/ -> 데이터베이스 시스템
데이터베이스 site 적어줘야함

yml안에서 이게 젤 중요하다.

spring:
  datasource:
    url: jdbc:mariadb://localhost:3306/site
    username: root
    password: 1234

나머지 yml코드들이다.

  jpa:
    defer-datasource-initialization: true
    open-in-view: false
    database-platform: org.hibernate.dialect.MariaDBDialect
    hibernate:
      ddl-auto: none # create-drop, update, validate, none
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
        implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
    show-sql: true
  servlet:
    multipart:
      max-request-size: 10MB
      max-file-size: 10MB

나중에 실무가서도 그냥 복붙만 하면된다.
코드에서 모르는 거는 gpt한테 물어보면 된다.

실행하면 start가 뜨고 성공했다.

localhost로 들어가면
오류가 뜬다..
해결하기 위해 컨트롤러를 만들어 준다.


MainController.java

package com.example.site2.domain.main.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MainController {
    
    public ModelAndView mainPage(){
        ModelAndView modelAndView = new ModelAndView();
        return modelAndView;
    }
}

main.html

<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org/">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
</html>

실행하면 화면만 나온다.


유저 리스트를 출력하기 위해 body안에 만들어준다.

<body>
    <h1>유저 리스트</h1>
    <ul>
        <li>
            <div>유저idx : <span>1</span></div>
            <div>유저id : <span>홍길동</span></div>
        </li>
        </li>
        <li>
            <div>유저idx : <span>2</span></div>
            <div>유저id : <span>임꺽정</span></div>
        </li>
    </ul>
</body>

controller도 다시 작성한다.

@Controller
public class MainController {
    
    @GetMapping("/")
    public ModelAndView mainPage(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("main");
        return modelAndView;
    }
}

다시 실행하면 임시의 유저 리스트가 나온다.

확장자는 컴퓨터가 아니라 우리가 구분하기 위해 쓰는것
즉, html이지만 html 파일이 아니라는 거

modelAndView보다 String을 많이 한다고 해서 변경해준다.

@Controller
public class MainController {
    
    @GetMapping("/")
    public String mainPage(){
        return "main";
        //화면의 이름이 문자열 일테니까
        //modelandview가 리턴에 있다.
    }

    // @GetMapping("/")
    // public ModelAndView mainPage(){
    //     ModelAndView modelAndView = new ModelAndView();
    //     modelAndView.setViewName("main");
    //     return modelAndView;
    // }
}

구조를 이렇게 만듦


UserEntity.java

주석으로 설명

package com.example.site2.model.user.entity;

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

@Entity //결국 테이블
@Table(name = "`user`") // 연계한다는 말
//user테이블은 예약어라 ``사용
public class UserEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 오라클은 시퀀스 나머지는 identity
    @Column(name = "idx", nullable = false, unique = true) //기본키이기 때문에 unique해줘야함, 유일한 값이니까
    private Long idx; //idx는 int로 되어있는데 long으로 해줘야함

    @Column(name = "id", nullable = false)
    private String id;

    @Column(name = "password", nullable = false)
    private String password;

    // 데이터베이스에서는 띄어쓰기는 _, java에서는 대문자

}

PostEntity.java

package com.example.site2.model.post.entity;

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

@Entity
@Table(name = "post")
public class PostEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "idx", nullable = false, unique = true)
    private Long idx;

    @Column(name = "title", nullable = false)
    private String title;

    @Column(name = "content", nullable = false)
    private String content;

    @Column(name = "user_idx", nullable = false)
    private Long userIdx;
}

참고자료
기본키 매핑 어노테이션 정리


생성자를 만들어도 되지만 롬복을 쓰는이유가 간단히 하기 위해서이다. all~어노테이션을 쓰면 만들어준다.
@AllArgsConstructor //생성자 자동 만들어줌
@NoArgsConstructor //기본 생성자
@Getter

UserEntity, PostEntity 클래스 위에 붙여준다.


UserRepository.java

주석으로 설명

package com.example.site2.model.user.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.site2.model.user.entity.UserEntity;

// 데이터를 가져오는 역할
// interface로 해줌
public interface UserRepository extends JpaRepository<UserEntity, Long> { //user의 idx가 long타입이기 때문에 
    
}

PostRepository.java

package com.example.site2.model.post.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.site2.model.post.entity.PostEntity;

public interface PostRepository extends JpaRepository<PostEntity, Long> { // 엔티티랑, 기본키 타입
    
}

id는 중복이 되면 안되니까 db에 들어가 id컬럼에 unique키 설정을 해준다.
그러고 save를 해준다.


UserEntity에 id를 바꿔준다.

    @Column(name = "id", nullable = false, unique = true)
    private String id;

UserRepository.java

엔티티를 가져오기 위해 repository를 만들어줌

package com.example.site2.model.user.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.site2.model.user.entity.UserEntity;

// 데이터를 가져오는 역할
// interface로 해줌
public interface UserRepository extends JpaRepository<UserEntity, Long> { //user의 idx가 long타입이기 때문에 

    // select * from user where idx = ?
    // interface로 하면 함수형식으로 지가 클래스를 만듦
    Optional<UserEntity> findByIdx(Long idx);
    // 데이터가 없으면 null이 된다
    // UserEntity가 null인걸 표현하는 객체이다
    // null체크를 하기 위함

    // select * from user where idx = ?
    Optional<UserEntity> findById(String id); // id로 찾으면 1개 또는 0이니까 optional로 쓴다.

    // select * from user where password = ?
    List<UserEntity> findByPassword(String password);

    // select *
    // from user
    // where idx = ?
    // and id = ?
    Optional<UserEntity> findByIdxAndId(Long idx, String id);
    // 값이 하나만 나옴, idx랑 id는 중복이 안되니까

    // select *
    // form user
    // where id like '%?%';
    List<UserEntity> findByIdContaining(String id);
}

// 원래라면 이렇게 해야하지만 귀찮기 때문에 jpa를 사용한다
// class UserRepo{
//     Optional<UserEntity> findByIdx(Long idx){
//         Connection ~~
//         PreparedStatement ~~
//         ResultSet ~~
//     }
    
// }

PostRepository.java

주석을 참고

package com.example.site2.model.post.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import com.example.site2.model.post.entity.PostEntity;
import java.util.List;


public interface PostRepository extends JpaRepository<PostEntity, Long> { // 엔티티랑, 기본키 타입

    // 쿼리문으로 데이터 가져오기 (복잡한 쿼리 쓰고 싶으면)
    @Query(value = "select * from post where title = ?", nativeQuery = true) // sql문을 그대로 작성하면 됨
    void getPostList(String title);
    // jpa는 복잡한 조인이 안될 것 같이 생김 그래서 entity로 넘어가본다.
    
    // select * from post where idx = ?
    Optional<PostEntity> findByIdx(Long idx);

    // select * from post where title =?
    List<PostEntity> findByTitle(String title);

    // select * form post where content = ?
    List<PostEntity> findByContent(String content);

    // select * from post wher user_idx = ?
    List<PostEntity> findByUserIdx(Long userIdx);

    // select *
    // from post
    // where title = ?
    // or content = ?
    List<PostEntity> findByTitleOrContent(String title, String content);

    // select *
    // from post
    // where title like '%?%'
    // or content like '%?%';
    List<PostEntity> findByTitleContainingOrContentContaining(String title, String content);
}

PostEntity.java

주석을 참고

    // user_idx랑 연관되는 애를 reference에 넣어줌
    // 이렇게만 작성해주면 알아서 join이 되서 다 가져와줌
    // join문 안적어도 됨
    @JoinColumn(name = "user_idx", referencedColumnName = "idx")
    private UserEntity userEntity;

엔티티들 클래스 위에 붙여준다.

@ToString

MainService.java

repository를 가져와서 만든다.

package com.example.site2.domain.main.service;

import java.util.List;
import java.util.Optional;

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

import com.example.site2.model.user.entity.UserEntity;
import com.example.site2.model.user.repository.UserRepository;

@Service
@Transactional(readOnly = true)
public class MainService {
    
    @Autowired
    private UserRepository userRepository;

    public void getMainData(){
        List<UserEntity> userEntityList = userRepository.findAll();

        Optional<UserEntity> userEntity = userRepository.findByIdx(1L); // Long타입이니까 L붙여줌

        System.out.println(userEntity);
    }
}

MainController.java

mainService를 @Autowired해준다.

public class MainController {

    @Autowired
    private MainService mainService;
    
    @GetMapping("/")
    public String mainPage(){
        mainService.getMainData();
        return "main";
        //화면의 이름이 문자열 일테니까
        //modelandview가 리턴에 있다.
    }
}

실행하면 오류가 뜸


PostRepository에서 findByUserIdx를 삭제해준다.

    // select * from post wher user_idx = ?
    List<PostEntity> findByUserIdx(Long userIdx);

그리고 폴인키라 다음과 같이 변경해준다.

    @ManyToOne
    @JoinColumn(name = "user_idx", referencedColumnName = "idx", nullable = false)
    private UserEntity userEntity;

다시 실행하면 start가 나오고 성공이다


MainService.java

optional을 null처리해준다.

@Service
@Transactional(readOnly = true)
public class MainService {
    
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PostRepository postRepository;

    public void getMainData(){
        Optional<PostEntity> postEntityOptional = postRepository.findByIdx(1L);

        if(postEntityOptional.isEmpty()){ //null이면
            System.out.println("엔티티가 없습니다.");
        }

        // 있다면
        // 애가 진짜 entity
        PostEntity postEntity = postEntityOptional.get();

        System.out.println(postEntity);
    }
}

실행해서 localhost들어가서 새로고침 하면

user의 정보가 들어있고 join을 쓰지않고 jpa를 사용하면 가져올 수 있다.


근데 이렇게 하면 안되고 가공해서 던져줘야한다.

@Service
@Transactional(readOnly = true)
public class MainService {
    
    @Autowired
    private UserRepository userRepository;

    // @Autowired
    // private PostRepository postRepository;

    public void getMainData(){

        // 이렇게 하면 안됨
        // 가공을 해서 던져줘야함
        List<UserEntity> userEntityList = userRepository.findAll();

        // Optional<PostEntity> postEntityOptional = postRepository.findByIdx(1L);

        // if(postEntityOptional.isEmpty()){ //null이면
        //     System.out.println("엔티티가 없습니다.");
        // }

        // // 있다면
        // // 애가 진짜 entity
        // PostEntity postEntity = postEntityOptional.get();

        // System.out.println(postEntity);
    }
}

MainUserDTO.java

화면에 필요한 것만 가져오도록 DTO를 만들어 준다.

package com.example.site2.domain.main.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

// data transfer object = dto
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class MainUserDTO {

    // 화면에서 필요한 것만
    private Long idx;
    private String id;
}

MainService.java

다시 만들어준다.
(주석을 참고)

@Service
@Transactional(readOnly = true)
public class MainService {
    
    @Autowired
    private UserRepository userRepository;

    // @Autowired
    // private PostRepository postRepository;

    public void getMainData(){

        // 이렇게 하면 안됨
        // 가공을 해서 던져줘야함

        // 원두 가져오기
        List<UserEntity> userEntityList = userRepository.findAll();
        // 아메리카노 컵 준비하기
        List<MainUserDTO> mainUserDTOList = new ArrayList<>();

        for (UserEntity userEntity : userEntityList) {
            // 원두를 아메리카노로 만들기
            MainUserDTO mainUserDTO = new MainUserDTO(userEntity.getIdx(), userEntity.getId());
            // 아메리카노를 컵에 담기
            // list에 담는다.
            mainUserDTOList.add(mainUserDTO);
        }
    }
}

ResMainDTO.java

DTO를 담을 DTO를 만들어준다.

package com.example.site2.domain.main.dto;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class ResMainDTO {
    // DTO들 담는 캐리어 같은 친구

    private List<MainUserDTO> mainUserDTOList;

}

MainService.java

주석을 참고

@Service
@Transactional(readOnly = true)
public class MainService {
    
    @Autowired
    private UserRepository userRepository;

    // @Autowired
    // private PostRepository postRepository;

    public ResMainDTO getMainData(){

        // 이렇게 하면 안됨
        // 가공을 해서 던져줘야함

        // 원두 가져오기
        List<UserEntity> userEntityList = userRepository.findAll();
        // 아메리카노 컵 준비하기
        List<MainUserDTO> mainUserDTOList = new ArrayList<>();

        for (UserEntity userEntity : userEntityList) {
            // 원두를 아메리카노로 만들기
            MainUserDTO mainUserDTO = new MainUserDTO(userEntity.getIdx(), userEntity.getId());
            // 아메리카노를 컵에 담기
            // list에 담는다.
            mainUserDTOList.add(mainUserDTO);
        }
        // 캐리어에 아메리카노들 담고 컨트롤러한테 넘겨주기
        return new ResMainDTO(mainUserDTOList);
    }
}

MainController.java

controller를 수정해준다.

package com.example.site2.domain.main.controller;

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 com.example.site2.domain.main.dto.ResMainDTO;
import com.example.site2.domain.main.service.MainService;

@Controller
public class MainController {

    @Autowired
    private MainService mainService;
    
    // 두 개 다 같다! 편한걸로 쓰면 됨
    @GetMapping("/")
    public String mainPage1(Model model){
        ResMainDTO dto = mainService.getMainData();
        model.addAttribute("dto", dto);
        return "main";
        //화면의 이름이 문자열 일테니까
        //modelandview가 리턴에 있다.
    }

    // @GetMapping("/")
    // public ModelAndView mainPage(){
    //     ModelAndView modelAndView = new ModelAndView();
    //     modelAndView.addObject("dto", dto);
    //     modelAndView.setViewName("main");
    //     return modelAndView;
    // }
}

main.html

thymeleaf를 사용한다.

<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org/">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>유저 리스트</h1>
    <ul>
        <li th:each="user : ${dto.mainUserDTOList}">
            <div>유저idx : <span th:text="${user.idx}"></span></div>
            <div>유저id : <span th:text="${user.id}"></span></div>
        </li>
    </ul>
</body>
</html>

정적파일이니까 임시로 적어놨던 li지우고 실행하면 User테이블에 있는 데이터가 나온다.


정리

html만들기
컨트롤러로 html 연결
엔티티
리파지토리
서비스
dto
서비스랑 컨트롤러 연결
model에 데이터담기
html에 데이터 뿌리기

이해하면 사이트 간단히 만들 수 있다고 한다..^^

profile
미래가 기대되는 풀스택개발자 공부 이야기~~

0개의 댓글