ResultSet의 데이터를 POJO class로 매핑하기

Sonar0·2022년 12월 2일
0

ResultSet

쿼리의 결과를 cursor를 이용해서 다룰 수 있도록 하는 객체이다.
쿼리의 결과를 테이블 형태로 받아 특정 row를 가르키는 cursor를 가진다.
왜 테이블 형태로 받는가? -> 쿼리의 결과를 배열로 주면? -> 쿼리의 결과가 대규모일 경우 애플리케이션의 메모리에 모두 로드할 수 없을 수 있음.

Cursor

커서는 방향성이 있으며 초기값은 첫번째 row의 이전을 가르키고 있음.

ResultSet의 주요 메서드

Javadoc ← 참고

Cursor 이동하기

public boolean next()

  • cursor를 현재보다 한 row 다음으로 이동한다.

public boolean previous()

  • cursor를 현재보다 한 row 이전으로 이동한다.

public boolean first()

  • cursor를 첫번째 row로 이동한다.

public boolean last()

  • cursor를 마지막 row로 이동한다.

public boolean absolute(int row)

  • cursor를 입력받은 row로 이동한다.

public boolean relative(int row)

  • cursor를 현재 위치로부터 입력받은 위치만큼 이동한다.

Cursor의 데이터 가져오기

public int getInt(int columnIndex)

  • 현재 cursor가 가르키고 있는 row의 주어진 column index에 해당하는 컬럼의 값을 int로 가져온다.

public int getInt(String columnIndex)

  • 현재 cursor가 가르키고 있는 row의 주어진 column index에 해당하는 컬럼의 값을 int로 가져온다.

public String getString(int columnIndex)

  • 현재 cursor가 가르키고 있는 row의 주어진 column index에 해당하는 컬럼의 값을 int로 가져온다.

public String getString(String columnIndex)

  • 현재 cursor가 가르키고 있는 row의 주어진 column index에 해당하는 컬럼의 값을 int로 가져온다.

public Blob getBlob(int columnIndex)

  • BLOB은 이진수 형태의 대형 객체를 저장할 때 쓴다. (ex. 이미지, 동영상, 파일 ...)
  • 현재 cursor가 가르키고 있는 데이터 행의 주어진 column index에 해당하는 컬럼의 값을 Blob로 가져온다.

public Blob getBlob(String columnIndex)

  • 현재 cursor가 가르키고 있는 데이터 행의 주어진 column index에 해당하는 컬럼의 값을 Blob로 가져온다.

public Clob getclob(int columnIndex)

  • CLOB은 문자 형태의 대형 객체를 저장할 때 쓴다. (ex. 대용량 텍스트/문서 파일, 대용량 파일 ...)
  • 현재 cursor가 가르키고 있는 데이터 행의 주어진 column index에 해당하는 컬럼의 값을 Clob로 가져온다.

public Clob getclob(String columnIndex)

  • 현재 cursor가 가르키고 있는 데이터 행의 주어진 column index에 해당하는 컬럼의 값을 Clob로 가져온다.

POJO class(Plain Old Java Object)란?

위키백과
Plain Old Java Object, 간단히 POJO는 말 그대로 해석을 하면 오래된 방식의 간단한 자바 오브젝트라는 말로서 Java EE 등의 중량 프레임워크들을 사용하게 되면서 해당 프레임워크에 종속된 "무거운" 객체를 만들게 된 것에 반발해서 사용되게 된 용어이다. 2000년 9월에 마틴 파울러, 레베카 파슨, 조쉬 맥킨지 등이 사용하기 시작한 용어로서 마틴 파울러는 다음과 같이 그 기원을 밝히고 있다.

“ 우리는 사람들이 자기네 시스템에 보통의 객체를 사용하는 것을 왜 그렇게 반대하는지 궁금하였는데, 간단한 객체는 폼 나는 명칭이 없기 때문에 그랬던 것이라고 결론지었다. 그래서 적당한 이름을 하나 만들어 붙였더니, 아 글쎄, 다들 좋아하더라고. ”

— 마틴 파울러

중량 프레임워크들의 특정 기술과 환경에 종속되어 의존하게 된 자바 코드는 가독성이 떨어져 유지보수에 어려움이 생겼고 객체지향 설계의 장점들을 잃어버리게 됐습니다.
그에 반발해 생긴 POJO는 본래 자바의 장점을 살리는 '오래된' 방식의 '간단한' 자바 오브젝트를 의미합니다.

필요성

자바는 객체지향 언어이다. 매번 코드로 SQL의 쿼리를 짜려면 효율성과 가독성이 떨어진다.
→ ResultSet의 결과를 Java Object로 만들면 타입 안정성과 객체지향 프로그래밍의 장점을 살려서 프로그램을 간결하게 만들 수 있다.

JDBC 개요 에서 만들었던 테이블과 Java 코드를 이용하겠습니다.

Mapping해보기

테이블

이전 코드

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class main {
    public static void main(String[] args) {
        try {
            Connection con = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/jdbc", "root", "1234");
            Statement stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery("select * from product");
            while (rs.next()) {
                System.out.println(
                rs.getInt(1) + " " + rs.getString(2) + " " + rs.getDate(3) 
                + " " + rs.getString(4) + " " + rs.getInt(5));
            }
            con.close();
        }catch (Exception e) {System.out.println(e);}
    }
}

새로운 코드

Product.java
import java.time.LocalDate;

public class Product { // 객체 = 인스턴스
    int id;
    String name;
    LocalDate updatedAt;
    String contents;
    int price;

    public void setPrice(int price) {
        this.price = price;
    }

    public int getPrice() {
        return price;
    }

    Product(int id, String name, LocalDateTime updatedAt, String contents, int price) {
        this.id = id;
        this.name = name;
        this.updatedAt = updatedAt.toLocalDate();
        this.contents = contents;
        this.price = price;
    }

    @Override
    public String toString() { // 객체의 속성 출력을 위해서 String으로 concat
        return id + " " + name + " " + updatedAt + " " + contents + " " + price;
    }
}
ResultSetMapper.java
import java.sql.ResultSet;
import java.sql.SQLException;

public class ResultSetMapper {
    public static Product create(ResultSet rs) throws SQLException {
        return new Product( // Product의 생성자에 각 레코드의 속성을 넣어준다.
                rs.getInt(1), rs.getString(2), rs.getTimestamp(3).toLocalDateTime()
                ,rs.getString(4) , rs.getInt(5));

    }
}
Main
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Main {
    public static void main(String[] args) {
        try {
            Connection con = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/jdbc", "root", "1234");
            Statement stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery("select * from product");
            while (rs.next()) {
                System.out.println(ResultSetMapper.create(rs)); // 변경해준 부분
            }
            con.close();
        }catch (Exception e) {System.out.println(e);}
    }
}

실행결과

표준입출력에 객체를 넣으면 toString() method를 자동으로 호출합니다.
모든 class는 Java의 Object를 자동으로 상속 받고 Product class 내에서 toString()을 오버라이딩 했기 때문에 다음과 같이 출력합니다.

1 shoes1 2022-08-01 This is shoes1 40000
2 shoes2 2022-08-01 This is shoes2 50000
3 shoes3 2022-08-01 This is shoes3 60000
4 shoes4 2022-08-01 This is shoes4 40000
5 shoes5 2022-08-01 This is shoes5 50000
6 shoes6 2022-08-01 This is shoes6 60000
7 backpack 2022-08-02 This is backpack 1500
8 shirt 2022-08-03 This is shirt 20000
9 glasses 2022-08-04 This is glasses 10000

장점

while (rs.next()) 
{
        System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getDate(3) + " "
        + rs.getString(4)
        + " " + rs.getInt(5));
}

위 코드와 같이 컬럼의 타입을 외워 매번 코딩할 필요 없이 DB의 데이터를 사용할 수 있음

단점

SELECT문의 결과에 따라 별도의 Java Class로 선언해야 한다.
예를 들어 JOIN을 할 경우 나온 임시 테이블 레코드의 속성이 매번 다를 수 있음
이런 경우 코드를 짜는데 시간이 오히려 많이 들어간다. 또한 ResultSet은 index별로 타입을 구분해서 호출해야하므로 쿼리나 코드변경에 취약하다. (변경시 버그가 발생할 확률이 높아진다.)

ORM (Object-Relational Mapping)

이런 단점 때문에 ResultSet과 POJO class를 매핑하는 코드를 매번 짜는 것이 불편해서 ORM (Object-Relational Mapping) Library가 만들어졌다.
ORM은 객체와 Realational Model(관계형 데이터베이스 모델)을 매핑할 수 있는 기능을 가지고 있다.
하나의 테이블이 하나의 Java Class에 해당하고, FK와 같은 부가적인 기능(JOIN과 같은 참조기능)은 함수로 제공한다.

profile
초보 개발자

0개의 댓글

관련 채용 정보