자바에서 transient 필드는 뭐하는 것일까?


객체내에 변수를 사용할때 transient로 지정하게 되면 직렬화(serializing), 또는 역직렬화(deserializing)시에 해당 변수는 직렬화 하지 않는 다는 것을 가리키는 것이다. 한마디로 하면 Object를 파일로 직접 저장할 때 직렬화 기능을 이용하는데, transient로 지정되지 않은 변수 값만 저장한다는 것이다.


간단히 테스트 코드를 작성 후 실행하면 다음 결과가 나온다.


package com.billyvme.serialize.sample;


import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;


public class TransientSample {


  public static final String FILENAME = "data.ser";


  /**

   * @param args

   * @throws IOException

   * @throws ClassNotFoundException

   */

  public static void main(String[] args) throws IOException,

      ClassNotFoundException {

    Point before = new Point();

    before.x = 7;

    before.y = 20;

    before.rho = (float) 3.14;

    before.theta = (float) 12.15;


    writeObject(before);


    Object after = readObject();

    System.out.print("Before : ");

    System.out.println(before);

    System.out.print("After  : ");

    System.out.println(after);

  }


  private static void writeObject(Object obj) throws IOException {

    FileOutputStream fos = new FileOutputStream(FILENAME);

    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(obj);

    oos.close();

    fos.close();

  }


  private static Object readObject() throws IOException, ClassNotFoundException {

    FileInputStream fis = new FileInputStream(FILENAME);

    ObjectInputStream ois = new ObjectInputStream(fis);

    Object result = ois.readObject();

    ois.close();

    fis.close();


    return result;

  }

}


class Point implements Serializable {

  int x, y;

  transient float rho, theta;


  @Override

  public String toString() {

    return String.format("x=%d, y=%d, rho=%f, theta=%f", x, y, rho, theta);

  }

}


위 코드를 실행해 보면 다음과 같은 결과가 출력 된다.


Before : x=7, y=20, rho=3.140000, theta=12.150000

After  : x=7, y=20, rho=0.000000, theta=0.000000


결과에서 보듯이 transient로 지정되지 않은 x,y 값만 다시 출력되는 것을 확인 할 수 있다. 

클래스 내에 암호 값이나 키 값을 속성 내에서 사용하는데 전송되거나 저장되면 안되는 값을 transient로 지정하면 좋을 것 같다. 

그런데 실제로 이 지시어를 써서 프로그래밍을 한 적이 없다. 매번 어플리케이션만 개발해서 그런걸까??

'개발자 > Java' 카테고리의 다른 글

자바 테스트를 위한 질문 정리  (0) 2013.09.24
Clone 메소드를 이용한 List의 객체 복사  (0) 2013.06.17
Posted by 빌리 :

** 본 요약은 한빛미디어 '하이버네이트 프로그래밍' 책을 기준으로 작성되어져 있다.

** 본 설명은 바로 따라하기 하기엔 어려움이 있다. 책을 보지 않은 사람에게는 간단히 어떤 기능을 제공하는지 이해하는데 목적을 둔다.

** 하이버네이트 3.2.5.ga 버전, Ant 빌드를 기본적으로 사용한다.


4장 컬렉션과 연관

본 장에서는 객체간 관계나 그룹이 처리되는 방법은 알아본다.


테이블 간의 관계 표현하기


두개의 클래스 용 맵핑 문서를 만들고, 그 안에 'set'이라는 태그를 이용해 연결 정보를 추가하여 사용한다.


N:N 연결 관계는 다음 코드를 연결할 양쪽 클래스 맵핑 문서에 다음 내용을 추가해 구현한다.


<set name="[필드명]" table="[조인 테이블명]">

  <key column="[현재 테이블의 아이디 키]"/>

  <many-to-many class="[연결되는 대상 클래스]" column="[연결되는 테이블의 아이디 키]"/>

</set>


양쪽에 <many-to-many> 태그를 사용하면, 하이버네이트에서는 자동으로 현재 테이블의 아이디 키와 연결되는 테이블의 아이디 키가 컬럼을 가진 조인 테이블을 생성한다. 이 테이블과 실제로 맵핑되는 객체는 존재하지 않고 N:N 관계를 유지하기 위한 테이블인 조인 테이블로 사용하게 된다.


실제 전체 코드를 보면 다음과 같이 작성될 수 있다.


AAA 클래스용 매핑 문서

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="com.billy.sample.data.AAA" table="AAA">

<meta attribute="class-description">

A 샘플 클래스 매핑 예제

</meta>

<id name="id" type="int" column="AAA_ID">

<meta attribute="scope-set">protected</meta>

<generator class="native"/>

</id>

    

<property name="name_a" type="string" not-null="true"/>

    

    <set name="bset" table="AAA_BBB" inverse="true">

      <meta attribute="field-description">연관된 B 클래스</meta>

      <key column="AAA_ID"/>

      <many-to-many class="com.billy.sample.data.BBB" column="BBB_ID"/>

    </set>

  </class>

</hibernate-mapping>


BBB 클래스용 매핑 문서

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="com.billy.sample.data.BBB" table="BBB">

<meta attribute="class-description">

B 샘플 클래스 매핑 예제

</meta>

<id name="id" type="int" column="BBB_ID">

<meta attribute="scope-set">protected</meta>

<generator class="native"/>

</id>

    

<property name="name_b" type="string" not-null="true"/>

    

    <set name="aset" table="AAA_BBB">

      <meta attribute="field-description">연관된 A 클래스</meta>

      <key column="BBB_ID"/>

      <many-to-many class="com.billy.sample.data.AAA" column="AAA_ID"/>

    </set>

  </class>

</hibernate-mapping>


위 코드 중에 AAA 클래스용 매핑 문서의 <set> 태그 안에 inverse(역매핑)이 설정 되어 있다. 이는 AAA클래스의 bset이 변경이 일어나면 AAA_BBB 테이블이 변경되지 않지만, BBB클래스의 aset이 변경이 일어났을때 AAA_BBB 테이블이 자동 갱신되도록 설정한다.


이렇게 맵핑 문서를 작성하고 난 후 Hibernate tool을 이용해 소스코드를 생성하면 해당 객체에 다음과 같은 코드가 추가 된다.


public class AAA implements java.io.Serializable {

  ...

  private Set bset = new HashSet(0);

  

  public Set getBset() {

    return this.bset;

  }

  

  public void setBset(Set bset) {

    this.bset = bset;

  }

  ...

}


그런데 위의 코드는 Java 5 버전에서는 Typesefe 경고가 발생한다. 이를 처리하기 위해서는 자바 코드 생성시 다음과 같은 코드를 추가해준다.(Ant task기준)


<hbm2java jdk5="true">

위 코드를 추가 후에 자바코드를 추가 하면 


private Set<BBB> bset = new HashSet<BBB>(0); 

과 같이 타입이 명시된 제너릭 코드가 생성된다.


 

객체에서 관계된 테이블(컬렉션) 데이터 입력하고, 데이터 읽어오기


위 단계에서 N:N 설정된 데이터를 사용하는 방법은 간단한다. 자동생성된 클래스의 Set 부분에 해당 값을 추가 해서 저장하면 자동으로 조인테이블에 데이터가 생성된다. 데이터 읽어오는 방법도 기존의 앞에서 해당 객체를 불러오는 방법과 동일하게 사용하면 데이터를 가져올 수 있다.



1:N 관계 표현하기


N:N 관계 설정하는 방법과 비슷하지만 조금 더 간단한다. N:N에서 설정한 맵핑 파일에 주석을 단다고 가정하면 다음과 같이 맵핑 코드를 작성할 수 있다.


<set name="comments" table="AAA_COMMENTS">

  <key column="AAA_ID"/>

  <element column="COMMENT" type="string"/>

</set>


그러면 AAA_COMMENTS 테이블이 생성되고, 자바 코드상에도 Set<String> comments가 추가되어 사용할 수 있게 된다.



Posted by 빌리 :

** 본 요약은 한빛미디어 '하이버네이트 프로그래밍' 책을 기준으로 작성되어져 있다.

** 본 설명은 바로 따라하기 하기엔 어려움이 있다. 책을 보지 않은 사람에게는 간단히 어떤 기능을 제공하는지 이해하는데 목적을 둔다.

** 하이버네이트 3.2.5.ga 버전, Ant 빌드를 기본적으로 사용한다.


3장 하이버네이트 활용


하이버네이트 설정(Configuration)을 properties 파일에서 cfg.xml 파일로 대체하기


기본 DB 접속 설정을 properties 파일, xml 파일, 또는 Program에서 직접 설정할 수 있다. 이 단락에서는 xml 파일을 이용한 설정 방법을 알아본다.


- 설정 방법

  1) hibernate.cfg.xml 파일 생성 (샘플코드 참조) : src 최상위 디렉토리에 저장한다.

<?xml version='1.0' encoding='utf-8'?>


<hibernate-configuration

        xmlns="http://www.hibernate.org/xsd/hibernate-configuration"

        xsi:schemaLocation="http://www.hibernate.org/xsd/hibernate-configuration hibernate-configuration-4.0.xsd"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <session-factory>

    <!-- Database connection settings -->

    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>

    <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>

    <property name="connection.username">sa</property>

    <property name="connection.password"></property>


    <!-- JDBC connection pool (use the built-in) -->

    <property name="connection.pool_size">1</property>


    <!-- SQL dialect -->

    <property name="dialect">org.hibernate.dialect.HSQLDialect</property>


    <!-- Enable Hibernate's automatic session context management -->

    <property name="current_session_context_class">thread</property>


    <!-- Disable the second-level cache  -->

    <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>


    <!-- Echo all executed SQL to stdout -->

    <property name="show_sql">true</property>


    <!-- Drop and re-create the database schema on startup -->

    <!-- 아래 기능은 3.2버전에서 동작하는지 확인해야한다. -->

    <!--

    <property name="hbm2ddl.auto">update</property>

    -->

    

    <!-- 사용 중인 매핑 문서 나열 -->

    <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

  </session-factory>

</hibernate-configuration>

  

  2) Ant build.xml 파일에 XML 설정을 사용할 수 있도록 지정

    -> Hibernate Tool Task를 사용하는 Target의 '<hibernatetool>' 태그 안에 다음 줄을 추가한다.    

<configuration configurationfile="${source.root}/hibernate.cfg.xml"/>


자바 객체를 DB에 저장하기


실제로 자바 코드를 이용해 어떻게 데이터를 생성하는지 알아본다. 전체 흐름을 간단히 요약하고, 코드를 보면 다음과 같다.


1) 기본 설정 부르기 

2) 세션 팩토리 가져오기 

3) 세션 열기 

4) 트랜잭션 열기 

5) 객체 생성 저장하기

6) 트랜잭션 반영 

7) 세션 닫기


void sample() {

  Configuration config = new Configuration()  // (1)

  config.configure();

  

  SessionFactory sessionFactory = config.buildSessionFactory(); // (2)

  

  Session session = sessionFactory.openSession()  // (3)

  Transaction tx = null;

  try {

    tx = session.beginTransaction();  // (4)

    

    // 맵핑 객체 생성 

    MyObject obj = new MyObject("name", "value");

    session.save(obj);  // (5)

    

    obj = new MyObject("name2", "value2");

    session.save(obj);

    

    tx.commit();  // (6)

  } catch (Exception e) {

    if (tx != null) {

      tx.rollback();

    }

    throw new Exception("Transaction failed", e);

  } finally {

    session.close();  // (7)

  }

  sessionFactory.close();  // (7)

}


객체는 session.save(obj)가 호출 된 이후에 영속 객체가 된다. 즉 DB 데이터로 저장되어 진다.


SessionFactory 객체는 스레드세이프 하기 때문에 전체 애플리케이션에서 하나만 사용하지만, Session 객체는 스레드세이프 하지 않기 때문에 하나의 작업을 마치고 다음 작업으로 넘어가면 SessionFactory에서 새 세션을 가져와 사용하도록 한다. 



DB 데이터를 자바 객체로 가져오기


저장된 DB 데이터를 가져오는데 하이버네이트에서는 여러가지 방법을 제공한다. 

  - HQL(하이버네이트의 sQL기반 쿼리언어)를 이용한 검색

  - Named Query/Named parameter 이용한 검색

  - Native SQL Query 이요한 검색 

  

이번 단락에서는 HQL와 Named Query/Parameter를 사용하는 간단한 예제를 설명한다.


첫째로 HQL 방법은 다음 코드와 같이 session객체의 createQuery([쿼리]) 메소드를 이용한다.


Query query = session.createQuery("from Track as track " + 

                                  "Where track.playTime <= ?");

query.setParameter(0, value, Hibernate.[TYPE]);

query.list();


HQL은 JDBC의 PreparedStatement와 유사한 파라미터를 지원한다. 이런 방법은 SQL 주입 공격을 막지만 조금더 편리한 방법을 제공한다. 그리고 쿼리의 내용을 보면 객체와 테이블의 이름이 같고 컬럼과 프로퍼티의 이름을 동일하게 사용되어진다. 기본적으로 동일한 이름을 가지고 DB 스키마가 생성되기 때문이지만, 추후 명시적으로 다른 이름을 사용할 수 있도록 지정하는 방법을 알아본다.


다음으로는 위 코드에서 좀 더 편하게 파라미터를 입력할 수 있는 네임트 파라미터를 설명한다. 코드를 먼저 보면 다음과 같다.


Query query = session.createQuery("from Track as track " + 

                                  "Where track.playTime <= :value");

query.setString("value", value);

query.list();


네임드 파라미터는 쿼리 본문에 접두어 콜런(:)으로 구분 한다. Query 객체는 setter 메소드를 자바 타입별로 모두 갖쳐줘 있어서, query.setInteger(), query.setString()등의 메소드를 이용해 적절한 타입으로 설정할 수 있다.


이는 쿼리 상의 파라미터 값을 명시적으로 맵핑 시켜주기에 오류 가능성을 줄여준다. 혹시 파라미터를 수많은 '?'로 지정하고, 그 뒤에 코드로 파라미터 값을 맵핑해주는 코드를 짜본적 있다면 네임드 파라미터 방법의 편리성은 피부로 느낄 수 있다. 제일 큰건 순서 제약이 없어진다는 것이다. 테이블이 변경되어 수많은 파라미터 중에 중간 값이 없어지고, 새로운 값이 추가된다면, 이때 코드를 신경써서 작성하지 않는다면 오류가 발생할 확률은 커지게 마련인데 이를 개선시킨 것이다.


그럼 쿼리문을 자바 소스에서 분리 할 수 있는 방법을 설명한다. 분리는 다음 단계로 진행 된다. 

  1) 하이버네이트 매핑 문서(hbm.xml)의 <hibernate-mapping> 태그 내에 매핑 쿼리 작성하기

<query name="com.billy.getData">

  <![CDATA[

    from Track as track

    where track.playTime <= :value

  ]]>

</query>


  2) session 객체에서 getNamedQuery() 메소드를 이용해 쿼리를 호출

Query query = session.getNamedQuery("com.billy.getData");

query.setString("value", value);

query.list();


물론 하이버네이트 매핑 문서(hbm.xml)는 하이버네이트 설정 파일에 추가되어 있어야 한다.



Posted by 빌리 :