빅데이터 Java 개발자 교육 [Spring - ( 외래키 + 주소 및 이미지 )]

Jun_Gyu·2023년 5월 9일
0
post-thumbnail

entity 외래키관련 ()

@ ManyToOne

 // 외래키 지정(생성되는 컬럼은 MEMID 레퍼런스컬럼은 MEMBER1테이블의 ID)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMID", referencedColumnName = "ID" )
    private Member1 member1;

@JoinColumn 어노테이션을 이용해서 외래키를 불러와 새로운 Column으로 지정가능.

@ OneToMany

// EAGER => member1을 조회시 address1을 join하여 보여줌
// LAZY => member1을 조회시 address1을 join하지 않고 address1을 필요할 때 조인함.
	@ToString.Exclude // stackoverflow
	@OneToMany(mappedBy = "member1", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
	@OrderBy(value = "no desc")
	List<Address1> list = new ArrayList<>();

CascadeType = 외래키가 걸려있는 데이터의 삭제여부



pagetotal 환경설정 추가 (페이지네이션)

address.pagetotal=표시할 데이터의 수


먼저 global.properries 파일에 위와 같이 셋팅을해준다.


@Value("${address.pagetotal}") int PAGETOTAL;

어노테이션을 컨트롤러 안에 생성해준다.



위와 같이 사용하고자 하는 부분에 객체명을 넣어주면 되겠다. 이처럼 환경설정 파일에서 미리 값을 지정해두면, 추후에 삭제 및 관리하기가 용이하다.

(기존의 경우에는 직접 사용했던 컨트롤러들을 찾아서 하나하나 수정해주어야 하는 단점이 존재함.)


주소관련 기능

AddressEntity.java

package com.example.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.format.annotation.DateTimeFormat;

import lombok.Data;

@Data
@Entity
@Table(name = "ADDRESS1")
@SequenceGenerator(name = "SEQ_ADDRESS1_NO", sequenceName = "SEQ_ADDRESS1_NO", initialValue = 1, allocationSize = 1)
public class Address1 {
    
    //주소번호, 기본키, 시퀀스    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ADDRESS1_NO")
    @Column(name = "NO")
    private long no;

    // 우편번호
    @Column(name = "POSTCODE", length = 10)
    private String postcode;

    // 주소(생략시 컬럼명 변수명과 같고 길이는 255)
    private String address;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    @UpdateTimestamp  //변경시에도 날짜 정보 변경
    private Date regdate;

    // 외래키 (생성되는 컬럼은 MEMID 레퍼런스컬럼은 MEMBER1테이블의 ID)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMID", referencedColumnName = "ID" )
    private Member1 member1;
}

selectlist.html

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h3>주소목록</h3>
    <a th:href="@{/member1/selectlist.do}"><button>회원목록</button></a>
    <hr />

    회원아이디 : <label th:text="${obj.id}"></label><br />
    회원이름 : <label th:text="${obj.name}"></label><br />
    <hr />
    주소목록 :
    <table border="1">
        <thead>
            <tr>
                <th>주소번호</th>
                <th>주소</th>
                <th>주소</th>
                <th>날짜</th>
                <th>삭제</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="tmp : ${obj.list}">
                <td th:text="${tmp.no}"></td>
                <td th:text="${tmp.postcode}"></td>
                <td th:text="${tmp.address}"></td>
                <td th:text="${tmp.regdate}"></td>
                <td>
                    <form th:action="@{/address1/delete.do}" method="post">
                        <input type="hidden" name="no" th:value="${tmp.no}" />
                        <input type="hidden" name="id1" th:value="${obj.id}" />
                        <input type="submit" value="삭제" />
                    </form>
                </td>
            </tr>
        </tbody>
    </table>
    <th:block th:each="num : ${#numbers.sequence(1, pages)}">
        <a th:href="@{/address1/selectlist.do(id=${param.id}, page=${num})}" th:text="${num}"></a>
    </th:block>
    <hr />
    주소등록 :

    <form th:action="@{/address1/insert.do}" method="post">
        <input type="text" name="member1.id" th:value="${obj.id}" readonly /><br />
        <input type="text" name="postcode" placeholder="우편번호" /><br />
        <input type="text" name="address" placeholder="주소" /><br />
        <input type="submit" value="주소등록" />
    </form>

</body>

</html>

Address1Controller.java

package com.example.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.entity.Address1;
import com.example.entity.Member1;
import com.example.repository.Address1Repository;
import com.example.repository.Member1Repository;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping(value = "/address1")
@RequiredArgsConstructor
public class Address1Controller {

    @Value("${address.pagetotal}") int PAGETOTAL;

    final Member1Repository m1Repository; // 저장소 객체
    final Address1Repository a1Repository;
    final String format = "Address1 => {}";

    @PostMapping(value = "/delete.do")
    public String deletePOST(
            @RequestParam(name = "no") Long no,
            @RequestParam(name = "id1") String id) {
        try {
            log.info(format, no);
            a1Repository.deleteById(no);
            ;

            return "redirect:/address1/selectlist.do?id=" + id;

        } catch (Exception e) {
            return "redirect:/home.do";
        }
    }

    @PostMapping(value = "/insert.do")
    public String insertPOST(@ModelAttribute Address1 address1) {
        try {
            log.info(format, address1.toString()); // stackoverflow
            a1Repository.save(address1);

            return "redirect:/address1/selectlist.do?id=" + address1.getMember1().getId();
        } catch (Exception e) { /* address1해당아이디 */
            e.printStackTrace();
            // redirect: 주소창의 주소 바꿈
            return "redirect:/home.do";
        }
    }

    // /address1/selectlist.do?id=아이디값
    @GetMapping(value = "/selectlist.do")
    public String selectListGET(Model model,
            @RequestParam(name = "id") String id,
            @RequestParam(name = "page", defaultValue = "0", required = false) int page) {
        try {
            if(page==0) { // 페이지 정보가 없다면 1로 변경하기
                return "redirect:/address1/selectlist.do?id=" + id + "&page=1";
            }

            // 회원 정보
            Member1 member1 = m1Repository.findById(id).orElse(null);
            log.info(format, id.toString()); // 오류발생시점 stackoverflow
            model.addAttribute("obj", member1);

            // 전체 갯수 가져오기
            long total = a1Repository.countByMember1_id(member1.getId());
            model.addAttribute("pages", (total - 1) / PAGETOTAL + 1);

            // 페이지네이션 설정
            PageRequest pageRequest = PageRequest.of(0, PAGETOTAL);

            List<Address1> addressList = a1Repository.findByMember1_idOrderByNoDesc(member1.getId(), pageRequest);
            model.addAttribute("address", addressList);

            // redirect 없을때는 html 표시
            return "/address1/selectlist";
        } catch (Exception e) {
            e.printStackTrace();
            // redirect: 주소창의 주소로 바꿈.
            return "redirect:/home.do";
        }
    }
}

이미지기능

BoardImageEntity.java

package com.example.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;
import org.springframework.format.annotation.DateTimeFormat;

import lombok.Data;
import lombok.ToString;

@Data
@Entity
@Table(name = "BOARDIMAGE1")
@SequenceGenerator(name = "SEQ_BOARDIMAGE1_NO", sequenceName = "SEQ_BOARDIMAGE1_NO", initialValue = 1, allocationSize = 1)
public class BoardImage1 {
    
    // 이미지번호
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_BOARDIMAGE1_NO")
    private long no;

    // 이미지명
    private String imageName;

    //  이미지타입(IMAGE_TYPE)
    private String imageType;

    // 이미지사이즈 (IMAGE_SIZE)
    private long imageSize;

    // 이미지데이터
    @Lob
    @ToString.Exclude
    private byte[] imageData;

    // 등록일
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    @CreationTimestamp
    @Column(name = "REGDATE", insertable = true, updatable = false)
    private Date regdate;

    // 외래키 (게시글번호)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "BRDNO", referencedColumnName = "NO")
    private Board1 board1;
}

selectlist.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h3>이미지 목록</h3>
    <a th:href="@{/board1/selectlist.do}"><button>게시글목록</button></a><br />
    대표이미지<img th:src="${board1.imageUrl}" style="width:100px; height: 100px;"><br />
    글번호 : <label th:text="${board1.no}"></label><br />
    글제목 : <label th:text="${board1.title}"></label>
    <hr />

    <div th:each="tmp : ${imageList}" style="display: inline-block; border: 10xp;">
        <img th:src="${tmp}" style="width: 100px; height: 100px;">
    </div>
    <hr/>

    <h3> 이미지등록 </h3>
    <form th:action="@{/boardimage1/insertimage.do}" method="post" enctype="multipart/form-data">
        <input type="text" name="board1.no" th:value="${board1.no}" /> <br />
        <input type="file" name="tmpfile"  accept="image/*" /><br />
        <input type="submit" value="이미지업로드" />
    </form>
</body>
</html>

BoardImageController.java

package com.example.controller;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.example.entity.Board1;
import com.example.entity.BoardImage1;
import com.example.repository.Board1Repository;
import com.example.repository.BoardImage1Repository;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
@RequestMapping(value = "/boardimage1")
@RequiredArgsConstructor
public class BoardImage1Controller {

    final String format = "BImage => {}";
    final Board1Repository b1Repository; // 게시글
    final BoardImage1Repository bi1Repository; // 게시글이미지

    @Value("${default.image}")
    String defaultImage;
    final ResourceLoader resourceLoader; // resources 폴더의 파일을 읽기 위한 객체 생성

    // 127.0.0.1:9090/ROOT/boardimage1/image?no=1
    @GetMapping(value = "/image")
    public ResponseEntity<byte[]> image(@RequestParam(name = "no", defaultValue = "0")long no)throws IOException {
        BoardImage1 obj = bi1Repository.findById(no).orElse(null);
        HttpHeaders headers = new HttpHeaders(); // import org.springframework.http.HttpHeaders
        if (obj != null) { // 이미지가 존재할 경우
            headers.setContentType(MediaType.parseMediaType(obj.getImageType()));
            return new ResponseEntity<>(obj.getImageData(), headers, HttpStatus.OK);
        }
        // 이미지가 없을 경우
        InputStream is = resourceLoader.getResource(defaultImage).getInputStream(); // exception 발생됨
        headers.setContentType(MediaType. IMAGE_PNG);
        return new ResponseEntity<>( is.readAllBytes(), headers, HttpStatus.OK);
    }

    @PostMapping(value = "/insertimage.do")
    public String insertImagePOST(
            @ModelAttribute BoardImage1 image1,
            @RequestParam(name = "tmpfile") MultipartFile file) {
        try {
            image1.setImageSize(file.getSize());
            image1.setImageData(file.getInputStream().readAllBytes());
            image1.setImageType(file.getContentType());
            image1.setImageName(file.getOriginalFilename());
            log.info(format, image1.toString());
            bi1Repository.save(image1);
            return "redirect:/boardimage1/selectlist.do?no=" + image1.getBoard1().getNo();
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/home.do";
        }
    }

    @GetMapping(value = "/selectlist.do")
    public String selectListGET(
            Model model, HttpServletRequest request,
            @RequestParam(name = "no") long no) {
        try {
            // 게시글 정보
            Board1 board1 = b1Repository.findById(no).orElse(null);

            // 대표이미지
            BoardImage1 image1 = bi1Repository.findTopByBoard1_noOrderByNoAsc(no);
            board1.setImageUrl( request.getContextPath() + "/boardimage1/image?no=0");
            if (image1 != null) {
                board1.setImageUrl( request.getContextPath() + "/boardimage1/image?no=" + image1.getNo() );
            }
            // 이미지도 포함하여 view로 전달
            model.addAttribute("board1", board1);

            // 전체이미지
            /*        List<String> 타입으로 만들어서 view로 전달 후 출력       */
            List<String> imageList = new ArrayList<>();
            List<BoardImage1> list1 = bi1Repository.findByBoard1_noOrderByNoAsc(no);
            if ( !list1.isEmpty() ) { // 리스트는 비어있지 않는지 확인
                for(BoardImage1 tmp : list1){
                    imageList.add( request.getContextPath() + "/boardimage1/image?no=" + tmp.getNo() );
                }
            }

            // 전체이미지 view로 전달
            model.addAttribute("imageList", imageList);
            return "/boardimage1/selectlist";

        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/home.do";
        }
    }
}
profile
시작은 미약하지만, 그 끝은 창대하리라

0개의 댓글