네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'전체 글'에 해당되는 글 194건


반응형

# 아이템24. 멤버 클래스는 되도록 static으로 만들라.

- 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그 외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다.

 

- 중첩 클래스의 종류는 정적 멤버 클래스, (비정적) 멤버 클래스, 익명 클래스, 지역 클래스 4가지다.

 

## 정적 멤버 클래스
- 정적 멤버 클래스는 다른 클래스 안에 선언이 되고, 바깥 클래스의 private 멤버에도 접근할 수 있다는 점을 제외하고는 일반 클래스와 똑같다. 정적 멤버 클래스는 흔히 바깥 클래스와 함께 쓰일 때만 유용한 private 도우미 클래스로 쓰인다.
public class StaticCar {
    static String _where="I am a Car from Germany!";
    Country _country;            // object of inner class country
    StaticCar(){
        _country=new Country();    // instantiate the inner class
    }
    static class Country {       // static member inner class
            String showCountry() {
            return _where;
        }
    }

    public static void main(String[] args) {
        
        StaticCar myCar= new StaticCar() ;  // instantiated object of class StaticCar
        System.out.print("Access through an Country reference");
        System.out.println(" created in an object of a StaticCar:");
        System.out.println(myCar._country.showCountry());
        
        // instantiated object of class StaticCar.Country
        StaticCar.Country country= new StaticCar.Country();
        System.out.println("Access through an Country reference that is local:");
        System.out.println(country.showCountry());
    }
}
 
Access through an Country reference created in an object of a StaticCar:
I am from Germany!
Access through an Country reference that is local:
I am from Germany!

 

## 비정적 멤버 클래스

- 정적 멤버 클래스와 비정적 멤버 클래스의 구문상 차이는 단지 static이 붙어있고 없고 뿐이지만, 의미상 차이는 의외로 꽤 크다.

- 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결된다. 그래서 비정적 멤버 클래스의 인스턴스 메서드에서 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다.
- 정규화된 this란, 클래스명.this 형태로 바깥 클래스의 이름을 명시하는 용법을 말한다.
- 비정적 멤버 클래스의 인터페이스와 바깥 인터페이스 사이의 관계는 멤버 클래스가 인스턴스화 될 떄 확립되며, 더 이상 변경할 수 없다.
- 비정적 멤버 클래스는 어댑터를 정의할 때 자주 쓰인다. 즉, 어떤 클래스의 인스턴스를 감싸 마치 다른 클래스의 인스턴스처럼 보이게 하는 뷰로 사용하는 것이다.

- 비슷하게, Set과 List 같은 다른 컬렉션 인터페이스 구현들도 자신의 반복자를 구현할 때 비정적 멤버 클래스를 주로 사용한다.

public class MySet<E> extends AbstractSet<E> {
    ... // 생략

    @Override public Iterator<E> iterator() {
        return new MyIterator();
    }

    private class MyIterator implements Iterator<E> {
    ...
    }
}
- 멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만들자. static을 생략하면 바깥 인스턴스로의 숨은 외부 참조를 갖게된다. 그러면 가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수 있다.

 

## 익명 클래스
- 익명 클래스는 바깥 클래스의 멤버도 아니다. 멤버와 달리, 쓰이는 시점에 선언과 동시에 인스턴스가 만들어진다. 그리고 오직 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있다. 정적 문맥에서라도 상수 변수 이외의 정적 멤버는 가질 수 없다. 즉, 상수 표현을 위해 초기화된 final 기본 타입과 문자열 필드만 가질 수 있다.
interface Inter {
    public void hi();
}

class Anony {
    public void f() {
        System.out.println("Anony.f() 호출.");
    }
}

public class Test {
    private Integer number;

    public void nonStaticMethod(){
        Inter ob2 = new Inter() {
            public void hi() {
                System.out.println("비 정적문맥 메소드 호출" + number);
            }
        };
    }

    public static void staticMethod() {
        Inter ob2 = new Inter() {
            Integer number2 = 1;
            public void hi() {
                System.out.println("정적문맥 메소드 호출" + number); // comile Error
            }
        };
    }

    public static void main(String[] args) throws Exception {
        //Inter ob1 = new Inter(); //Compile Error!!!
        Inter ob2 = new Inter() {
            public void hi() {
                System.out.println("안녕하십니까?");
            }
        };

        ob2.hi();

        Anony a1 = new Anony();
        Anony a2 = new Anony() {
            public void f() {
                System.out.println("이히힛.");
            }
        };

        a1.f();
        a2.f();
    }
}
- 익명 클래스를 사용하는 클라이언트는 그 익명 클래스가 상위 타입에서 상속한 멤버 외에는 호출할 수 없다.
- 익명 클래스의 또 다른 주 쓰임은 정적 팩터리 메서드를 구현할 때다.

 

## 지역 클래스
- 지역 클래스는 지역 변수를 선언할 수 있는 곳이면 실질적으로 어디서든 선언할 수 있고, 유효 범위도 지역 변수와 같다. 멤버 클래스처럼 이름이 있고 반복해서 사용할 수 있다. 익명 클래스처럼 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있으며, 정적 멤버는 가질 수 없으며, 가독성을 위해 짧게 작성해야 한다.

 

## 정리
중첩 클래스에는 4가지가 있으며, 각각의 쓰임이 다르다. 메서드 밖에서도 사용해야 하거나 메서드 안에 정의하기엔 너무 길다면 멤버 클래스로 만든다. 멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조한다면 비정적으로, 그렇지 않으면 정적으로 만들면 된다. 중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한 곳이고 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 이미 있다면 익명 클래스로 만들고, 그렇지 않으면 지역 클래스로 만들자.
728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

타임리프(Thymeleaf) 템플릿에서는 for문, while문 등과 유사한 반복 처리(iteration) 처리를 위해 th:each를 사용한다.

루프 처리중 상태를 추적하는 status 변수를 이용하여 index, count 등을 얻을 수 있다.

 

loop를 원칙적으로 break 하거나 다른 곳에서 사용하는 것이 불가하다는 제한사항이 있지만 Thymeleaf가 View Template Engine이기 때문에 비즈니스 로직을 view에 작성하는 것을 지양해야한다.

 

th:each

반복하는 html 엘리먼트에 th:each 속성을 사용하여 Collection을 반복하여 화면을 처리한다.

<table class="tb_col"> 
    <thead>
    <th>seq</th>
    <th>name</th>
    <th>price</th>
    <th>quantity</th>
    </thead>
    <tbody>
        <tr th:each="product : ${productList}">
            <td th:text="${product.seq}"></td>
            <td th:text="${product.name}"></td>
            <td th:text="${product.price}"></td>
            <td th:text="${product.quantity}"></td>
        </tr>
    </tbody>
</table>

 

#numbers.sequence

컬렉션 없이 단순 반복 처리를 하고 싶다면 Number(org.thymeleaf.expression.Numbers) 클래스의 utility 메서드인 #numbers.sequence을 사용하여 먼저 원하는 반복 횟수만큼의 배열을 생성하고 th:each의 컬렉션에 넣으면 된다.

<tr:block th:each="num : ${#numbers.sequence(1,5)}"> 
    <td th:text="${num}"></td> 
</tr:block>

 

반복 상태 변수 (status)

Thymeleaf에서 th:each를 사용하면 반복 상태를 추적할 수 있는 status 변수를 제공해준다. 이를 이용하여 index, count 등 값을 추출할 수 있다.

- index      현재 반복 인덱스 (0부터 시작)

- count      현재 반복 인덱스 (1부터 시작)

- size         총 요소 수

- current   현재 요소

- even        현재 반복이 짝수인지 여부 (boolean)

- add         현재 반복이 홀수인지 여부 (boolean)

- first         현재 반복이 첫번째인지 여부 (boolean)

- last          현재 반복이 마지막인지 여부 (boolean)

 

status 변수는 기본적으로 오브젝트명 + "Stat" 변수명으로 접근할 수 있으며 th:each 선언시 개발자가 직접 명명하여 사용할 수 있다. 

<div th:each="num : ${#numbers.sequence(1,3)}"> 
    <p th:text="${'index : ' + numStat.index}"></p>
    <p th:text="${'count : ' + numStat.count}"></p> 
    <p th:text="${'size : ' + numStat.size}"></p> 
    <p th:text="${'current : ' + numStat.current}"></p> 
    <p th:text="${'even : ' + numStat.even}"></p> 
    <p th:text="${'odd : ' + numStat.odd}"></p> 
    <p th:text="${'first : ' + numStat.first}"></p> 
    <p th:text="${'last : ' + numStat.last}"></p> 
</div>

 

참고

https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#using-theach

 

Tutorial: Using Thymeleaf

1 Introducing Thymeleaf 1.1 What is Thymeleaf? Thymeleaf is a Java library. It is an XML/XHTML/HTML5 template engine able to apply a set of transformations to template files in order to display data and/or text produced by your applications. It is better s

www.thymeleaf.org

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

[Spring] Apache POI 를 이용한 엑셀 파일 읽기

✨ 의존성

- Spring Boot

- Spring Web

- Thymeleaf

- Lombok

 

1. Apache POI , Tika 관련 의존성 추가

maven일 경우 pom.xml

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <version>4.1.2</version>
</dependency>
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-core -->
<dependency>
  <groupId>org.apache.tika</groupId>
  <artifactId>tika-core</artifactId>
  <version>2.3.0</version>
</dependency>

 

 

gradle일 경우 build.gradle

// https://mvnrepository.com/artifact/org.apache.poi/poi
implementation group: 'org.apache.poi', name: 'poi', version: '4.1.2'
implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.2'
// https://mvnrepository.com/artifact/org.apache.tika/tika-core
implementation group: 'org.apache.tika', name: 'tika-core', version: '2.3.0'

spring boot라면 version 입력을 하지 않아도 될 것이다.

 

2. 파일 입력 폼

resources/templates/index.html

<!DOCTYPE HTML>
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>

<body>
<form th:action="@{/excel/read}" method="POST" enctype="multipart/form-data">
    <input type="file" th:name="file1">
    <input th:type="submit" value="제출" />
</form>
</body>
</html>

 

3. 객체

ExcelData.java

package model;

import lombok.*;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class ExcelData {

    private int num;
    private String name;
}

 

4. 컨트롤러

ExcelDataController.java

import model.ExcelData;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.tika.Tika;
import org.apache.tika.exception.TikaException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

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

@Controller
public class ExcelDataController {

    @PostMapping("/excel/read")
    public String readExcel(@RequestParam("file") MultipartFile file, Model model) throws TikaException, IOException { // 2

        List<ExcelData> dataList = new ArrayList<>();

        try (InputStream is = file.getInputStream();) {

            Tika tika = new Tika();
            String mimeType = tika.detect(is);
            if (isAllowedMIMEType(mimeType)) {
                Workbook workbook = new XSSFWorkbook(file.getInputStream());

                Sheet worksheet = workbook.getSheetAt(0);

                String atchFileId = null;

                for (int i = 1; i < worksheet.getPhysicalNumberOfRows(); i++) { // 1번째 행부터 끝까지
                    Row row = worksheet.getRow(i);
                    
                    ExcelData data = new ExcelData();
                    data.setNum((int) row.getCell(0).getNyumericCellValue());
                    data.setName(row.getCell(1).getStringCellValue());

                    dataList.add(data);
                }

                model.addAttribute("list", dataList);
            } else {
                throw new IOException();
            }
        } catch (Exception e) {
            throw new TikaException("ERROR");
        }

        return "list";
    }


    private boolean isAllowedMIMEType(String mimeType) {
        if (mimeType.equals("application/x-tika-ooxml"))
            return true;
        return false;
    }
}

MultipartFile

Spring 환경이라면 Spring에서 제공하고 있는 MultipartFile 클래스와 MultipartHttpServletRequest 클래스를 사용해서 File 업로드 기능을 구현할 수 있다. 클라이언트에서 서버로 HTTP 요청을 할 때, Content-Type 필드의 내용을 multipart/form-data로 요청한다.

@RequestParam 어노테이션과 함께 MultipartFile 타입을 사용한다. 

 

Apache Tika 라이브러리

Apache Tika 를 사용하여 MIME TYPE를 체크하였다.

 

5. 리스트 화면 

resources/templates/list.html

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>
<body>
  <table class="table table-striped">
    <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">번호</th>
      <th scope="col">이름</th>
    </tr>
    </thead>
    <tbody>
    <tr th:each="data : ${datas}" >
      <td th:text="${dataStat.index}"></td>
      <td th:text="${data.num}"></td>
      <td th:text="${data.name}"></td>
    </tr>
    </tbody>
  </table>
</body>
</html>

Stat을 사용해서 상태변수 (index)를 접근하였다.

 

참고

https://stackoverflow.com/questions/50849800/how-to-read-excel-file-using-spring-boot

https://caileb.tistory.com/152

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

SSH로 접속할때 ubuntu@123.123.123.123 식으로 입력하여 접속을 한다

 

# 예시
# 사용자명: ubuntu
# IP 주소: 192.168.10.23
# Port 번호: 8000

$ ssh ubuntu@192.168.10.23 -p 8000

 

접속정보를 설정파일로 저장하여 접속할 수 있다. 아래 명령어를 통해 config 파일을 만든다.

$ vi ~/.ssh/config

 

 

config 파일에 아래와 같이 입력한다.

Host myserver
    HostName 192.168.10.23
    Port 8000
    User ubuntu

vi 명령어를 알면 되지만, i 키 눌러서 입력하고 esc 누르고 :wq 입력해서 저장한다.

 

$ ssh myserver

위와 같이 입력하면 맨 위와 같은 명령어와 동일한 일을 진행한다.

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

1. Mybatis resultMap 기능

mybatis는 ORM 기술 중 하나이다. ORM(Object Relational Mapping)이란 객체 지향 언어의 객체와 관계형 데이터를 서로 변환해준다는 것이다.

DB 조회 결과를 복잡한 객체 구조로 변환해야 할 때 mybatis의 resultMap 기능을 사용한다. 여러 테이블의 조인 결과를 여러 자바 객체에 담을 때 resultMap 기능이 유용하다.

 

2. Mybatis mapper 구현 방법

1) annotation으로 구현하기

간단한 SQL을 구현할 때 annotation을 이용하여 구현하는 것이 편하다.

2) mapper XML에서 구현하기

mybatis resultMap 기능을 구현하려면 mapper XML 파일에 구현하는 것이 편하다.

 

mapper/RegisterMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis//DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper .dtd">

<mapper namespace="mapper.RegisterMapper">
    <resultMap id="RegisterwithStudentCourse" type="dto.Register">
        <id property="id" column="id" />
        <result property="studentId" column="studentId" />
        <result property="courseId" column="courseId" />
        <result property="grade" column="grade" />
        <result property="createDate" column="createDate" />
        <association property="student" javaType="dto.Student">
            <id property="id" column="studentId" />
            <result property="studentNumber" column="studentNumber" />
            <result property="name" column="studentName" />
        </association>
        <association property="course" javaType="dto.Course">
            <id property="id" column="courseId" />
            <result property="courseName" column="courseName" />
            <result property="unit" column="unit" />
        </association>
    </resultMap>
</mapper>

 

<mapper namespace="mapper.RegisterMapper">

RegistMapper에 대한 SQL 명령이나 resultMap 등을 정의하기 위한 mapper 태그이다.

패키지까지 포함해서 mapper 클래스의 이름을 적는다.

 

<resultMap id="RegisterwithStudentCourse" type="dto.Register">

조회 결과를 Regist 객체로 채우는 방법을 정의한다. SQL 조회 결과를 Java 객체 구조에 채우는 방법을 정의한 것이 resultMap이다.

 

<id property="id" column="id" />

조회 결과의 id 칼럼은 Register 클래스의 id 속성(property)에 채운다. 이 칼럼이 테이블의 기본키(primary)이기 때문에 <id> 태그를 사용한다.

 

<result property="studentId" column="studentId" />

조회 결과의 studentId 칼럼을 Register 클래스의 studentId 속성에 채운다. 테이블의 기본키가 아니기 때문에 <result> 태그를 사용한다.

 

<association property="student" javaType="dto.Student">

Register 클래스의 student 속성에 Student 객체를 대입한다.

 

/mapper/RegisterMapper.java

import java.util.List; 

import org.apache.ibatis.annotations.Mapper; 
import org.apache.ibatis.annotations.ResultMap; 
import org.apache.ibatis.annotations.Select; 

import dto.Register; 

@Mapper 
public interface RegisterMapper { 

    @ResultMap("RegisterWithStudentAndCourse") 
    @Select("SELECT r.*, s.studentNumber, s.name studentName, c.courseName, c.unit " + 
            " FROM register r JOIN student s ON r.studentId = s.id " + 
            " JOIN course c ON r.courseId = c.id " + 
            " ORDER BY s.studentNumber ") 
    List<Register> findAll(); 
}

 

@ResultMap("RegisterWithStudentAndCourse")

 

RegisterMapper.xml 파일의 id="RegisterWithStudentAndCourse" resultMap 방법으로 조회 결과를 Java 객체들에 채워서 리턴한다.

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !