POJO 클래스에서 기본형 데이터 타입 선언은 문제가 될 수 있다.

POJO 클래스란?


POJO(Plain Old Java Object)는 복잡한 기능 없이 단순하게 데이터만을 저장하기 위한 목적의 오브젝트이다. 대부분 데이터베이스에 대한 CRUD를 수행할 때 로우 정보와 맵핑하기 위해 사용한다. 예를 들면 아래와 같다.


public class User {
    private long id;
    private String name;
    private int age;
    private DateTime dateLastLogged;

    public User() {
        super();
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public DateTime getDateLastLogged() {
        return dateLastLogged;
    }

    public void setDateLastLogged(DateTime dateLastLogged) {
        this.dateLastLogged = dateLastLogged;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + ", dateLastLogged=" + dateLastLogged + "]";
    }
}

기본형 타입 사용은 문제가 될 수 있다.


보통 위와 같이 Java의 기본형 데이터 타입(Primitive Data Types)을 사용하여 POJO 클래스를 작성할 경우 데이터베이스에 대한 CRUD 수행시 문제가 될 수 있다. 이유는 데이터베이스의 경우 NULL을 허용하는 컬럼이 존재할 수 있기 때문이다. Java의 기본형 데이터 타입은 오브젝트가 아니기 때문에 NULL이 허용되지 않는다. 어떤 값도 부여하지 않을 경우 기본 값으로 0이 부여된다.(boolean 타입의 경우 false가 부여된다.) 즉, 개발자로서는 의도하지 않게 데이터베이스 컬럼 값에 0을 삽입할 수 있다.

해결책은?


위 문제에 대한 해결책은 기본형 데이터 타입 대신에 Wrapper 클래스를 사용하는 것이다. 오브젝트이기 때문에 null 부여가 가능하다. 각 기본형 데이터 타입에 대응하는 Wrapper 클래스는 아래와 같다.

  • byte: java.lang.Byte
  • short: java.lang.Short
  • int: java.lang.Integer
  • long: java.lang.Long
  • float: java.lang.Float
  • double: java.lang.Double
  • char: java.lang.Character
  • boolean: java.lang.Boolean

위 예로 들었던 POJO 클래스에 Wrapper 클래스를 적용하면 아래와 같다. Java에서는 기본형 데이터 타입과 Wrapper 클래스 간에 오토박싱(Autoboxing)과 언박싱(Unboxing)을 지원하기 때문에 사용자는 기본형 데이터 타입을 사용할 때와 동일하게 소스 코드를 작성할 수 있다.


import org.joda.time.DateTime;

public class User {
    private Long id;
    private String name;
    private Integer age;
    private DateTime dateLastLogged;

    public User() {
        super();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public DateTime getDateLastLogged() {
        return dateLastLogged;
    }

    public void setDateLastLogged(DateTime dateLastLogged) {
        this.dateLastLogged = dateLastLogged;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + ", dateLastLogged=" + dateLastLogged + "]";
    }
}

주의할 점은?


물론 주의할 점도 있다. 아래 예를 살펴보자.


System.out.println(new Integer(1) == 1); // true
System.out.println(new Integer(1) == new Integer(1)); // false
System.out.println(new Integer(1) == new Integer(1).intValue()); // true

true
false
true

보다시피 Wrapper 오브젝트와 기본형 타입을 비교시 정상적인 결과가 나오지만 Wrapper 오브젝트 간에 비교시 위와 같은 결과가 나온다. 당연한 결과지만 개발자가 무의식적으로 넘어갈 수 있는 부분이다. 해결책으로는 POJO 클래스의 getter() 메써드 작성시 리턴 타입(Return Type)만 기본형 타입으로 작성하는 방법이 있다. 이 방법을 권장한다.

참고 글



저작자 표시 비영리 동일 조건 변경 허락
신고