JAVA & Open Framework2012. 1. 26. 16:55

에러발생 유형 : 데이터 크기가 해당 유형의 최대 크기보다 큷니다 : 2740

varchar2(4000)에 PreparedStatement로 insert를 하는데 고생을 좀 심하게 하여
제 경험을 공유할 겸 해서 글을 남깁니다.

환경
OS : Windows 2003 Server
Oracle Database version : 9.2.0.1.0
NLS_LANG : KOREAN_KOREA.KO16MSWIN949
JDBC Driver version : 9.2.0.5.0 (ojdbc14.jar)
Java version : 1.4.2_12

테스트 테이블
CREATE TABLE BIG_TEST
(
  K    VARCHAR2(10) NOT NULL,
  A    VARCHAR2(4000),
  B    VARCHAR2(4000),
  C    VARCHAR2(4000),
  PRIMARY KEY (K)
);

위와 같은 테이블에 Excel로 만들어진 자료를 insert하는 프로그램을 개발하여
테스트하는데 아래와 같은 에러가 발생했습니다.

pstmt = conn.prepareStatement(
        "insert into BIG_TEST \n" +
        "(K, A, B, C) values \n" +
        "(?, ?, ?, ?) "
);

pstmt.setString(1, k);
pstmt.setString(2, a);  // 여기서 에러
pstmt.setString(3, b);
pstmt.setString(4, c);

java.sql.SQLException: 데이터 크기가 해당 유형의 최대 크기보다 큽니다: 2001
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:134)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:179)
at oracle.jdbc.ttc7.TTCItem.setArrayData(TTCItem.java:147)
at oracle.jdbc.dbaccess.DBDataSetImpl.setBytesBindItem(DBDataSetImpl.java:2492)
at oracle.jdbc.driver.OraclePreparedStatement.setItem(OraclePreparedStatement.java:1194)
at oracle.jdbc.driver.OraclePreparedStatement.setString(OraclePreparedStatement.java:1614)
at oracle.BigTest.start(BigTest.java:144)
at oracle.BigTest.main(BigTest.java:247)

a의 바이트 길이를 찍어봤습니다. 그냥 getBytes().length = 1583 일반적으로 생각하는 영문1, 한글2의 숫자가 나옵니다.
그런데 위에 Exception에 나온 2001과 같지 않아서 getBytes("UTF8").length로 찍어보니 Exception과 같은 숫자가 나오네요.

System.out.println("A : bytes=" + a.getBytes().length + ", UTF8 bytes=" + a.getBytes("UTF8").length);
A : bytes=1583, UTF8 bytes=2001

여러 길이의 테스트 결과 UTF8로 2000이 넘어가면 위와 같은 Exception이 발생합니다.
ORA-xxxxx같은 코드가 없고 해서 혹시나 해서 oracle.jdbc.ttc7.TTCItem.class를 역컴파일 해봤습니다.

public void setArrayData(boolean flag, String s, int i)
    throws SQLException
{
    is_null = flag;
    if(s != null)
    {
        byte_value = StringToBytes(s, i);
        data_size = byte_value.length;
        if((type == 96 || type == 1) && data_size > max_out_size)
            DBError.throwSqlException(70, new Integer(data_size));
    } else
    {
        byte_value = new byte[0];
        data_size = 0;
    }
}


if((type == 96 || type == 1) && data_size > max_out_size)
여기 if문에서 max_out_size가 2000이고 data_size가 2001로 나옵니다.

다른 길이도 테스트 해봤는데 max_out_size는 항상 2000이네요.
저런 상태면 setString으로는 그냥 byte길이도 아닌 UTF8로 2000이 넘는 데이터는 넣을 수가 없다는 얘긴데..
당황스럽네요. DB에러도 아니고 JDBC에서 UTF8 2000으로 길이 체크를 하는건 오바라고 생각되는데
4000이라면 또 모를까, 그래서 주석처리하고 테스트 해 봤습니다.

insert 잘만 되는군요. getBytes().length 4000까지 잘 들어가고 40001부터
"java.sql.SQLException: ORA-01461: LONG 값은 LONG 열에만 입력할 수 있습니다"로 DB에러도 잘 나옵니다.

해결했다고 좋아하며 다시 Excel로 insert하는거 테스트 하는데
다른 문제가 생기더군요. 이번에는 컬럼 순서가 바뀌어서 들어가는 겁니다.

상태를 보니 UTF8로 길이가 2000이 넘지 않는것 앞에 2000이 넘는게 오면 위치가 바뀌더군요.

1. a가 2000이 넘을때
   A B C
   c a b

2. b가 2000이 넘을때
   A B C
   a c b

3. c가 2000이 넘을때(정상)
   A B C
   a b c

4. a, b가 2000이 넘을때
   A B C
   b c a

5. a, c가 2000이 넘을때
   A B C
   b a c

6. b, c가 2000이 넘을때(정상)
   A B C
   a b c

7. a, b, c가 2000이 넘을때(정상)
   A B C
   a b c

8. a, b, c가 2000이 넘지 않을때(정상)
   A B C
   a b c

이제서야 열심히 게시판을 뒤져보니 데이터가 많을때 setString()을 사용하지 말고
setCharacterStream()을 사용하라고 되어 있어서 원래 드라이버를 사용하고 아래 코드로 변경했습니다.

pstmt.setString(1, k);
pstmt.setCharacterStream(2, new StringReader(a), a.length());
pstmt.setCharacterStream(3, new StringReader(b), b.length());
pstmt.setCharacterStream(4, new StringReader(c), c.length());

Exception없이 insert는 잘 되는데 순서가 뒤 바뀌는건 여전하더군요.

이번에는 현상이 좀 다릅니다. 일정길이 이상이면 컬럼이 무조건 맨 뒤로 가버리더군요.
일정길이라고 한건 딱히 같은 byte길이를 찾을 수 없었고 내부적으로 변환되는 byte 길이가 있는것 같더군요.

1. a가 큼
   A B C
   c b a

2. B가 큼
   A B C
   a c b

3. C가 큼(정상)
   A B C
   a b c

4. a, b가 큼(1과 동일)
   A B C
   c b a

또 게시판을 열심히 뒤졌습니다. 저 처럼 컬럼 순서가 바뀌는 경우가 있다고 하는데 Statement로 하면
잘 들어간다더군요. 아래처럼 고쳐서 테스트 해봤습니다.

pstmt = conn.prepareStatement(
        "insert into BIG_TEST \n" +
        "(K, A, B, C) values \n" +
        "('" + k + "', '" + a + "', '" + b + "', '" + c + "') "
);

insert 잘 됩니다. 순서도 안 바뀌고...

그럼 PreparedStatement는 쓰면 안되나? 그냥 Statement를 쓰면 ' 같은거 escape처리 해야돼서
변수마다 함수 둘러줘야되는데... varchar2(4000) 짜리가 한두군데가 아닌데...

고민 끝에 드라이버 소스를 뒤져보기로 했습니다. 왜 순서가 뒤바뀌는지...

결국 찾은 파일이 NonPlsqlTTCDataSet.class입니다.

oracle.jdbc.ttc7.NonPlsqlTTCDataSet.class의 marshalRow함수가 아래와 같습니다.

protected void marshalRow()
    throws SQLException, IOException
{
    int i = -1;
    for(int j = 0; j < columns.length; j++)
        if(!columns[j].sizeExceeded())
        {
            if(columns[j].type.isStream())
                columns[j].pushStream();
            else
                columns[j].marshal();
        } else
        if(i != -1)
        {
            if(columns[j].type.isStream())
                columns[j].pushStream();
            else
                columns[j].marshal();
        } else
        {
            i = j;
        }

    if(i != -1)
        if(columns[i].type.isStream())
            columns[i].pushStream();
        else
            columns[i].marshal();
    marshaledRows++;
}

여기서 ?가 4개니까 for문은 4번 루프를 돕니다. a, b, c가 모두 큰 경우로 테스트하여 j에 대해 찍어봤습니다.
0, 2, 3 이 나오고 맨 밑에 if(i != -1)에서 i가 1이네요.
k일때 !columns[j].sizeExceeded() true (크기가 작으니까)
        if(!columns[j].sizeExceeded())
        {
            if(columns[j].type.isStream())
                columns[j].pushStream();
            else
                columns[j].marshal();
로 타고,
a에 대해 돌때 !columns[j].sizeExceeded() == false, i != -1 false 여서
        } else
        {
            i = j;
        }
로 타고,
b, c일때
        if(i != -1)
        {
            if(columns[j].type.isStream())
                columns[j].pushStream();
            else
                columns[j].marshal();

루프가 끝나고 루프 바깥에서 i = 1로 되서 a에 대한 처리가 이루어집니다.
그래서 a가 맨 뒤쪽 c 컬럼으로 insert되는 현상이 생기더군요.

코드로 봐선 큰게(sizeExceeded) 나오면 맨 나중에 처리하겠다는 의도인데..
그것도 처음 나오는 것만. 왜 저런 코딩이 필요한지 모르겠습니다.
위쪽이 max_out_size가 2000으로 체크하듯 예전 오라클의 LONG타입이 테이블당 1개만 올수 있어서
저런 코드가 필요했는데 JDBC코드가 그대로 유지되어 온게 아닌지...

함수를 아래와 같이 고쳐서 setCharacterStream()으로 테스트 해봤습니다.

protected void marshalRow()
    throws SQLException, IOException
{
    int i = -1;
    for(int j = 0; j < columns.length; j++)
        if(!columns[j].sizeExceeded())
        {
            if(columns[j].type.isStream())
                columns[j].pushStream();
            else
                columns[j].marshal();
        } else
        //if(i != -1)
        {
            if(columns[j].type.isStream())
                columns[j].pushStream();
            else
                columns[j].marshal();
        //} else
        //{
        //    i = j;
        }

    //if(i != -1)
    //    if(columns[i].type.isStream())
    //        columns[i].pushStream();
    //    else
    //        columns[i].marshal();
    marshaledRows++;
}

루프는 순서대로 0, 1, 2, 3으로 타고 DB에 데이터도 순서대로 잘 insert되는군요.

이번에는 맨 처음의 oracle.jdbc.ttc7.TTCItem.class도 아래 코드를 주석처리해서
setString()으로 테스트 해봤습니다. 잘 됩니다.
//if((type == 96 || type == 1) && data_size > max_out_size)
//    DBError.throwSqlException(70, new Integer(data_size));

한글포함 4000bytes 꽉 채워서 a, b, c insert하는데 잘 들어가는군요. 순서도 맞구요.
setString(), setCharaterStream() 둘다 잘 됩니다.
참고로, setString()을 쓰면 marshal()을 타고, setCharaterStream()을 쓰면 pushStream()을 탑니다.

=================================================================================

결론입니다.

1. Orcale JDBC 드라이버에서는 UTF8 bytes길이가 2000이 넘어가면 아래 Exception이 발생하여
   setString()을 사용하지 못합니다.
   java.sql.SQLException: 데이터 크기가 해당 유형의 최대 크기보다 큽니다: UTF8bytes길이

2. UTF8 bytes길이가 2000이 넘어갈때 setCharacterStream()을 사용해야 하는데
   컬럼이 1개 일때만 해당 컬럼을 맨 뒤로 빼서 사용하는게 안전합니다.
   여러개의 setCharacterStream()을 사용하면 순서가 뒤바뀔수 있습니다.

3. 여기까지의 경우로 해결이 안될 상황이면 그냥 Statement를 사용하세요.
   ※ Statement를 사용할때는 '만 ''로 escape하세요. Toad에서 쿼리 실행할때 처럼 &를 \&로 escape하면
      값이 \로 인해 4000바이트가 넘어가면 아래 Exception이 발생합니다.
      java.sql.SQLException: ORA-01704: 문자열이 너무 깁니다

4. 컬럼을 CLOB으로 변경하는걸 고려해 보세요.
   전 테스트 해보지 않았지만 다른 게시물에 CLOB에 대한 조언도 있더군요.
   CLOB으로 변경하면 setClob()을 사용하고, 조회 쪽도 고쳐져야 되는걸로 알고 있습니다.
   CLOB이 여러개일때도 순서 안바뀌는지 테스트가 필요하겠습니다.

5. CLOB으로 변경도, 코드 수정도 싫다면 JDBC드라이버를 수정하세요.
   1) oracle.jdbc.ttc7.TTCItem.class의 public void setArrayData(boolean flag, String s, int i)의
      아래 코드를 제거합니다.

      //if((type == 96 || type == 1) && data_size > max_out_size)
      //    DBError.throwSqlException(70, new Integer(data_size));

   2) oracle.jdbc.ttc7.NonPlsqlTTCDataSet.class의 protected void marshalRow()를
      아래 코드와 같이 수정합니다.

      protected void marshalRow()
          throws SQLException, IOException
      {
          int i = -1;
          for(int j = 0; j < columns.length; j++)
              if(!columns[j].sizeExceeded())
              {
                  if(columns[j].type.isStream())
                      columns[j].pushStream();
                  else
                      columns[j].marshal();
              } else
              //if(i != -1)
              {
                  if(columns[j].type.isStream())
                      columns[j].pushStream();
                  else
                      columns[j].marshal();
              //} else
              //{
              //    i = j;
              }

          //if(i != -1)
          //    if(columns[i].type.isStream())
          //        columns[i].pushStream();
          //    else
          //        columns[i].marshal();
          marshaledRows++;
      }

끝으로, 저는 iBATIS를 사용하는 환경이라 JDBC 드라이버를 고쳐서 해결했으나,
다양한 경우를 테스트 해보진 않아 고친부분이 다른 곳에 문제를 일으키지 않는지는 잘 모르겠습니다.
현재 제가 개발하는 사이트에 올려 돌려보고 있는 중이며 아직까진 특별한 상황은 일어나지 않았고,
제 생각으로는 딱히 문제가 될것 같진 않으나 고치실 분들은 주의하시기 바랍니다.

첨부파일 설명입니다.
BigTest.java            <-- 테스트용 클래스.

ojdbc14_patch.jar       <-- 위에 언급한 2개 클래스만 수정하여 들어있는 jar파일.
                            기존 ojdbc14.jar 앞에 classpath를 잡아주면 고쳐진걸로 실행됩니다.
ojdbc14_patch_full.jar  <-- 2개 클래스만 수정된 전체 jdbc드라이버입니다.
                            classpath의 순서를 잡기 힘든 분들은 드라이버를 이걸로 바꾸세요.


출처 : http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=jdbc&c=r_p&n=1152605190
Posted by 아로나
JAVA & Open Framework2012. 1. 5. 13:59

이클립스에서 "Build Automatically"를 체크했음에도 불구하고 자동빌드가 안되는 경우가 있다.


해결방법 1.

이클립스 재설치


해결방법 2.

Window - Preferences - General - Workspcace - "Refresh automatically" 체크

=> 본인은 이클립스 재설치는 물론 환경설정파일까지 전부 날려보았으나 자동빌드가 안되는 현상이 몇달간 지속되다가
    위와 같은 방법으로 해결됨. (사실 별거 아닐 수 있으나 매번 클린 혹은 전체 컴파일을 해야 하기때문에 작업이 비효율적일
    뿐 아니라 톰캣을 띄워놓고 작업하는 경우 수정사항이 생길 때마다 톰캣을 재기동시켜야 하는 불편함이 있었음.)
Posted by 아로나
JAVA & Open Framework2012. 1. 3. 11:29

영문 문자열 배열을 소팅할 때에는 기본적으로 대소문자를 구분하기에, 부자연스러운 결과가 나오게 됩니다. 대소문자 구분없이 소팅하는 방법입니다.

import java.util.Arrays;

public class Foo {
  public static void main(String[] args) {


    String[] s = { "bbb", "AAA", "DDD", "CCC", "aaa" };


    // 대소문자 구분하여 정렬
    Arrays.sort(s);
    System.out.println(Arrays.toString(s));
    // 결과: [AAA, CCC, DDD, aaa, bbb]




    // 대소문자 구분 없이 정렬
    Arrays.sort(s, String.CASE_INSENSITIVE_ORDER);
    System.out.println(Arrays.toString(s));
    // 결과: [AAA, aaa, bbb, CCC, DDD]


  }
}


출처 : http://mwultong.blogspot.com/2006/12/java-sort-ignore-case-string-array.html
Posted by 아로나
JAVA & Open Framework2011. 12. 19. 17:51

//java로 oracleDB접속 하는 방법?
import java.sql.*; //JDBC API가 있는 package
class DBtest
{
 public static void main(String[] args)
 {
 try{
  //[0].Oracle 접속 Draver를 Loading
  Class.forName("oracle.jdbc.driver.OracleDriver");
  //[1].Connection 객체를 생성
  //C:\Program Files\Java\jdk1.6.0_04\jre\lib\ext
  String conStr = "jdbc:oracle:thin:@127.0.0.1:1521:ORA92";
  String user ="scott";
  String pwd = "tiger";
  Connection objCon=DriverManager.getConnection(conStr,user,pwd);
  if(objCon!=null)
  {
   String strSQL = "select * from japan";
   Statement stmt = objCon.createStatement(); //SqlCommand
   ResultSet rs = stmt.executeQuery(strSQL); //SqlDataReader
   while(rs.next())
   {
    System.out.println(rs.getString("id")+"\t"+rs.getString("name"));
   }
   System.out.println("접속성공");
  }else
  {
   System.out.println("접속실패");
  }
 }catch(Exception e){
  e.printStackTrace();
 }
 }
}




-----------------------------------------------------------------------------------------

create japan (
 id varchar2(10),
name varchar2(10)
);


출처 : http://windjasi.tistory.com/86

Posted by 아로나
JAVA & Open Framework2011. 8. 18. 09:39

첨부파일 참조.

Posted by 아로나
JAVA & Open Framework2011. 8. 16. 09:52

사용 :

boolean isHoli = SchoolUtil.isHoliday(bse_yr+hday_month+StrUtil.lpad(ii+"",2,'0'));
if ( isHoli ) case_no = 1;

 

소스 :

 /**
  * 입력한 날짜가 공휴일인지 검사를 한다.
  */
 public static boolean isHoliday(String yyyymmdd) throws Exception {
  // 검사년도
  int yyyy = Integer.parseInt(yyyymmdd.substring( 0, 4 ));
  boolean isYun = DateUtil.isYunYear(yyyy);

  try {
   // 음력 공휴일을 양력으로 바꾸어서 입력
   String tmp01 = SchoolUtil.lunarTranse( yyyy + "0101", isYun);// 음력설날
   String tmp02 = SchoolUtil.lunarTranse( yyyy + "0815", isYun);// 음력추석
   String tmp03 = SchoolUtil.lunarTranse( yyyy + "0408", isYun);// 석가탄신일

   String[] holidays = {
     DateUtil.dateAdd('d',-1,tmp01,"yyyyMMdd"), // 음력설 첫째날
     tmp01,  // 음력설 둘째날
     DateUtil.dateAdd('d',1,tmp01,"yyyyMMdd"), // 음력설 셋째날
     DateUtil.dateAdd('d',-1,tmp02,"yyyyMMdd"), // 추석 첫째날
     tmp02,  // 추석 둘째날
     DateUtil.dateAdd('d',1,tmp02,"yyyyMMdd"), // 추석 셋째날
     tmp03,  // 석가탄신일
     // 양력 공휴일 입력
     yyyy + "0101",  // 양력설날
     yyyy + "0301",  // 삼일절
     yyyy + "0405",  // 식목일
     yyyy + "0505",  // 어린이날
     yyyy + "0606",  // 현충일
     yyyy + "0717",  // 제헌절
     yyyy + "0815",  // 광복절
     yyyy + "1003",  // 개천절
     yyyy + "1225",  // 성탄절
    };

   for ( int ii = 0 ; ii < holidays.length ; ++ii ) {
    if ( yyyymmdd.equals(holidays[ii])  ) {
     return true ;
    }
   }
        } catch(Exception ex) {
            throw ex;
        }
  return false;
 }

 

    /**
     * 음력을 약력으로
     *
     * @param       String    음력일('yyyyMMdd')
     * @param       boolean   윤달 여부
     * @return      String    처리결과 양력일 엔티티
     * @throws      java.lang.Exception
     */
    public static String lunarTranse(String TranseDay,boolean leapyes) throws Exception{
        int lyear = Integer.parseInt(TranseDay.substring(0,4));
        int lmonth = Integer.parseInt(TranseDay.substring(4,6));
        int lday = Integer.parseInt(TranseDay.substring(6,8));
       
        if(!leapyes && !verifyDate(lyear, lmonth, lday, "solar-"))
        {
            return "";
        }
        if(leapyes && !verifyDate(lyear, lmonth, lday, "solar+"))
        {
            return "";

        }
        int m1 = -1;
        long td = 0L;
        if(lyear != 1881)
        {
            m1 = lyear - 1882;
            for(int i = 0; i <= m1; i++)
            {
                for(int j = 0; j < 13; j++)
                    td = td + (long)KK[i * 13 + j];

                if(KK[i * 13 + 12] == 0)
                    td = td + 336L;
                else
                    td = td + 362L;
            }

        }
        m1++;
        int n2 = lmonth - 1;
        int m2 = -1;
        do
        {
            m2++;
            if(KK[m1 * 13 + m2] > 2)
            {
                td = td + 26L + (long)KK[m1 * 13 + m2];
                n2++;
                continue;
            }
            if(m2 == n2)
                break;
            td = td + 28L + (long)KK[m1 * 13 + m2];
        } while(true);
        if(leapyes)
            td = td + 28L + (long)KK[m1 * 13 + m2];
        td = td + (long)lday + 29L;
        m1 = 1880;
        do
        {
            m1++;
            boolean leap = m1 % 400 == 0 || m1 % 100 != 0 && m1 % 4 == 0;
            if(leap)
                m2 = 366;
            else
                m2 = 365;
            if(td < (long)m2)
                break;
            td = td - (long)m2;
        } while(true);
        int syear = m1;
        MDayCnt[1] = m2 - 337;
        m1 = 0;
        do
        {
            m1++;
            if(td <= (long)MDayCnt[m1 - 1])
                break;
            td = td - (long)MDayCnt[m1 - 1];
        } while(true);
        int smonth = m1;
        int sday = (int)td;
        long y = (long)syear - 1L;
        td = ((y * 365L + y / 4L) - y / 100L) + y / 400L;
        boolean leap = syear % 400 == 0 || syear % 100 != 0 && syear % 4 == 0;
        if(leap)
         MDayCnt[1] = 29;
        else
         MDayCnt[1] = 28;
        for(int i = 0; i < smonth - 1; i++)
            td = td + (long)MDayCnt[i];

        td = td + (long)sday;
        int i = (int)(td % 10L);
        i = (i + 4) % 10;
        int j = (int)(td % 12L);
        j = (j + 2) % 12;

        String sValue=String.valueOf(syear);

        if(smonth<10)
            sValue+="0";
         sValue+=String.valueOf(smonth);
         if(sday<10)
            sValue+="0";
         sValue+=String.valueOf(sday);

        return sValue;
    }

    private static boolean verifyDate(int k, int l, int l1, String s)
    {
        if(k < 1881 || k > 2043 || l < 1 || l > 12)
            return false;
        if(s.equals("lunar") && l1 > MDayCnt[l - 1])
            return false;
        if(s.equals("solar+"))
        {
            if(KK[(k - 1881) * 13 + 12] < 1)
                return false;
            if(KK[(k - 1881) * 13 + l] < 3)
                return false;
            if(KK[(k - 1881) * 13 + l] + 26 < l1)
                return false;
        }
        if(s.equals("solar-"))
        {
            int j = l - 1;
            for(int i = 1; i <= 12; i++)
                if(KK[((k - 1881) * 13 + i) - 1] > 2)
                    j++;

            if(l1 > KK[(k - 1881) * 13 + j] + 28)
                return false;
        }
        return true;
    }
   
    private static final int KK[] = {
        1, 2, 1, 2, 1, 2, 2, 3, 2, 2,
        1, 2, 1, 1, 2, 1, 2, 1, 2, 1,
        2, 2, 1, 2, 2, 0, 1, 1, 2, 1,
        1, 2, 1, 2, 2, 2, 1, 2, 0, 2,
        1, 1, 2, 1, 3, 2, 1, 2, 2, 1,
        2, 2, 2, 1, 1, 2, 1, 1, 2, 1,
        2, 1, 2, 2, 0, 2, 1, 2, 1, 2,
        1, 1, 2, 1, 2, 1, 2, 0, 2, 2,
        1, 2, 3, 2, 1, 1, 2, 1, 2, 1,
        2, 2, 1, 2, 2, 1, 2, 1, 1, 2,
        1, 2, 1, 0, 2, 1, 2, 2, 1, 2,
        1, 2, 1, 2, 1, 2, 0, 1, 2, 3,
        2, 1, 2, 2, 1, 2, 1, 2, 1, 2,
        1, 2, 1, 2, 1, 2, 1, 2, 2, 1,
        2, 2, 0, 1, 1, 2, 1, 1, 2, 3,
        2, 2, 1, 2, 2, 2, 1, 1, 2, 1,
        1, 2, 1, 2, 1, 2, 2, 2, 0, 1,
        2, 1, 2, 1, 1, 2, 1, 2, 1, 2,
        2, 0, 2, 1, 2, 1, 2, 3, 1, 2,
        1, 2, 1, 2, 1, 2, 2, 2, 1, 2,
        1, 1, 2, 1, 2, 1, 2, 0, 1, 2,
        2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
        0, 2, 1, 2, 3, 2, 2, 1, 2, 1,
        2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
        2, 2, 1, 2, 1, 2, 0, 1, 2, 1,
        1, 2, 1, 2, 2, 3, 2, 2, 1, 2,
        1, 2, 1, 1, 2, 1, 2, 1, 2, 2,
        2, 1, 0, 2, 1, 2, 1, 1, 2, 1,
        2, 1, 2, 2, 2, 0, 1, 2, 1, 2,
        1, 3, 2, 1, 1, 2, 2, 1, 2, 2,
        2, 1, 2, 1, 1, 2, 1, 1, 2, 2,
        1, 0, 2, 2, 1, 2, 2, 1, 1, 2,
        1, 2, 1, 2, 0, 1, 2, 2, 1, 4,
        1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
        1, 2, 1, 2, 2, 1, 2, 1, 2, 1,
        0, 2, 1, 1, 2, 2, 1, 2, 1, 2,
        2, 1, 2, 0, 1, 2, 3, 1, 2, 1,
        2, 1, 2, 2, 2, 1, 2, 1, 2, 1,
        1, 2, 1, 2, 1, 2, 2, 2, 1, 0,
        2, 1, 2, 1, 1, 2, 3, 1, 2, 2,
        1, 2, 2, 2, 1, 2, 1, 1, 2, 1,
        1, 2, 2, 1, 2, 0, 2, 2, 1, 2,
        1, 1, 2, 1, 1, 2, 1, 2, 0, 2,
        2, 1, 2, 2, 3, 1, 2, 1, 2, 1,
        1, 2, 2, 1, 2, 2, 1, 2, 1, 2,
        1, 2, 1, 2, 0, 1, 2, 1, 2, 1,
        2, 2, 1, 2, 1, 2, 1, 0, 2, 1,
        3, 2, 1, 2, 2, 1, 2, 2, 1, 2,
        1, 2, 1, 1, 2, 1, 2, 1, 2, 2,
        2, 1, 2, 0, 1, 2, 1, 1, 2, 1,
        2, 3, 2, 2, 1, 2, 2, 1, 2, 1,
        1, 2, 1, 1, 2, 2, 1, 2, 2, 0,
        2, 1, 2, 1, 1, 2, 1, 1, 2, 1,
        2, 2, 0, 2, 1, 2, 2, 1, 3, 2,
        1, 1, 2, 1, 2, 2, 1, 2, 2, 1,
        2, 1, 2, 1, 2, 1, 1, 2, 0, 2,
        1, 2, 1, 2, 2, 1, 2, 1, 2, 1,
        1, 0, 2, 1, 2, 2, 3, 2, 1, 2,
        2, 1, 2, 1, 2, 1, 1, 2, 1, 2,
        1, 2, 2, 1, 2, 2, 1, 0, 2, 1,
        1, 2, 1, 2, 1, 2, 2, 1, 2, 2,
        0, 1, 2, 3, 1, 2, 1, 1, 2, 2,
        1, 2, 2, 2, 1, 2, 1, 1, 2, 1,
        1, 2, 1, 2, 2, 2, 0, 1, 2, 2,
        1, 1, 2, 3, 1, 2, 1, 2, 2, 1,
        2, 2, 2, 1, 1, 2, 1, 1, 2, 1,
        2, 1, 0, 2, 2, 2, 1, 2, 1, 2,
        1, 1, 2, 1, 2, 0, 1, 2, 2, 1,
        2, 4, 1, 2, 1, 2, 1, 1, 2, 1,
        2, 1, 2, 2, 1, 2, 2, 1, 2, 1,
        2, 0, 1, 1, 2, 1, 2, 1, 2, 2,
        1, 2, 2, 1, 0, 2, 1, 1, 4, 1,
        2, 1, 2, 1, 2, 2, 2, 1, 2, 1,
        1, 2, 1, 1, 2, 1, 2, 2, 2, 1,
        0, 2, 2, 1, 1, 2, 1, 1, 4, 1,
        2, 2, 1, 2, 2, 2, 1, 1, 2, 1,
        1, 2, 1, 2, 1, 2, 0, 2, 2, 1,
        2, 1, 2, 1, 1, 2, 1, 2, 1, 0,
        2, 2, 1, 2, 2, 1, 4, 1, 1, 2,
        1, 2, 1, 2, 1, 2, 2, 1, 2, 2,
        1, 2, 1, 1, 2, 0, 1, 2, 1, 2,
        1, 2, 2, 1, 2, 2, 1, 2, 0, 1,
        1, 2, 1, 4, 1, 2, 1, 2, 2, 1,
        2, 2, 1, 1, 2, 1, 1, 2, 1, 2,
        2, 2, 1, 2, 0, 2, 1, 1, 2, 1,
        1, 2, 1, 2, 2, 1, 2, 0, 2, 2,
        3, 1, 2, 1, 1, 2, 1, 2, 1, 2,
        2, 2, 1, 2, 1, 2, 1, 1, 2, 1,
        2, 1, 2, 0, 2, 2, 1, 2, 1, 2,
        1, 3, 2, 1, 2, 1, 2, 2, 1, 2,
        2, 1, 2, 1, 1, 2, 1, 2, 1, 0,
        2, 1, 2, 2, 1, 2, 1, 2, 1, 2,
        1, 2, 0, 1, 2, 1, 2, 1, 4, 2,
        1, 2, 1, 2, 1, 2, 1, 2, 1, 1,
        2, 2, 1, 2, 2, 1, 2, 2, 0, 1,
        1, 2, 1, 1, 2, 1, 2, 2, 1, 2,
        2, 0, 2, 1, 1, 4, 1, 1, 2, 1,
        2, 1, 2, 2, 2, 1, 2, 1, 2, 1,
        1, 2, 1, 2, 1, 2, 2, 0, 2, 1,
        2, 1, 2, 1, 1, 2, 3, 2, 1, 2,
        2, 1, 2, 2, 1, 2, 1, 1, 2, 1,
        2, 1, 2, 0, 1, 2, 2, 1, 2, 1,
        2, 1, 2, 1, 2, 1, 0, 2, 1, 2,
        1, 2, 2, 3, 2, 1, 2, 1, 2, 1,
        2, 1, 2, 1, 2, 1, 2, 2, 1, 2,
        1, 2, 0, 1, 2, 1, 1, 2, 1, 2,
        2, 1, 2, 2, 1, 0, 2, 1, 2, 1,
        3, 2, 1, 2, 1, 2, 2, 2, 1, 2,
        1, 2, 1, 1, 2, 1, 2, 1, 2, 2,
        2, 0, 1, 2, 1, 2, 1, 1, 2, 1,
        1, 2, 2, 1, 0, 2, 2, 2, 3, 2,
        1, 1, 2, 1, 1, 2, 2, 1, 2, 2,
        1, 2, 2, 1, 1, 2, 1, 2, 1, 2,
        0, 1, 2, 2, 1, 2, 1, 2, 3, 2,
        1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
        2, 1, 2, 1, 2, 1, 0, 2, 1, 1,
        2, 2, 1, 2, 1, 2, 2, 1, 2, 0,
        1, 2, 1, 1, 2, 3, 2, 1, 2, 2,
        2, 1, 2, 1, 2, 1, 1, 2, 1, 2,
        1, 2, 2, 2, 1, 0, 2, 1, 2, 1,
        1, 2, 1, 1, 2, 2, 2, 1, 0, 2,
        2, 1, 2, 3, 1, 2, 1, 1, 2, 2,
        1, 2, 2, 2, 1, 2, 1, 1, 2, 1,
        1, 2, 1, 2, 0, 2, 2, 1, 2, 1,
        2, 1, 2, 3, 2, 1, 1, 2, 2, 1,
        2, 2, 1, 2, 1, 2, 1, 2, 1, 1,
        0, 2, 2, 1, 2, 1, 2, 2, 1, 2,
        1, 2, 1, 0, 2, 1, 1, 2, 1, 2,
        4, 1, 2, 2, 1, 2, 1, 2, 1, 1,
        2, 1, 2, 1, 2, 2, 1, 2, 2, 0,
        1, 2, 1, 1, 2, 1, 1, 2, 2, 1,
        2, 2, 0, 2, 1, 2, 1, 3, 2, 1,
        1, 2, 2, 1, 2, 2, 2, 1, 2, 1,
        1, 2, 1, 1, 2, 1, 2, 2, 0, 2,
        1, 2, 2, 1, 1, 2, 1, 1, 2, 3,
        2, 2, 1, 2, 2, 1, 2, 1, 2, 1,
        1, 2, 1, 2, 0, 1, 2, 2, 1, 2,
        2, 1, 2, 1, 2, 1, 1, 0, 2, 1,
        2, 2, 1, 2, 3, 2, 2, 1, 2, 1,
        2, 1, 1, 2, 1, 2, 1, 2, 2, 1,
        2, 2, 1, 0, 2, 1, 1, 2, 1, 2,
        1, 2, 2, 1, 2, 2, 0, 1, 2, 1,
        1, 2, 3, 1, 2, 1, 2, 2, 2, 2,
        1, 2, 1, 1, 2, 1, 1, 2, 1, 2,
        2, 2, 0, 1, 2, 2, 1, 1, 2, 1,
        1, 2, 1, 2, 2, 0, 1, 2, 2, 3,
        2, 1, 2, 1, 1, 2, 1, 2, 1, 2,
        2, 2, 1, 2, 1, 2, 1, 1, 2, 1,
        2, 0, 1, 2, 2, 1, 2, 2, 1, 2,
        3, 2, 1, 1, 2, 1, 2, 1, 2, 2,
        1, 2, 1, 2, 2, 1, 2, 0, 1, 1,
        2, 1, 2, 1, 2, 2, 1, 2, 2, 1,
        0, 2, 1, 1, 2, 1, 3, 2, 2, 1,
        2, 2, 2, 1, 2, 1, 1, 2, 1, 1,
        2, 1, 2, 2, 2, 1, 0, 2, 2, 1,
        1, 2, 1, 1, 2, 1, 2, 2, 1, 0,
        2, 2, 2, 1, 3, 2, 1, 1, 2, 1,
        2, 1, 2, 2, 2, 1, 2, 1, 2, 1,
        1, 2, 1, 2, 1, 0, 2, 2, 1, 2,
        2, 1, 2, 1, 1, 2, 1, 2, 0, 1,
        2, 3, 2, 2, 1, 2, 1, 2, 2, 1,
        1, 2, 1, 2, 1, 2, 1, 2, 2, 1,
        2, 2, 1, 2, 0, 1, 1, 2, 1, 2,
        1, 2, 3, 2, 2, 1, 2, 2, 1, 1,
        2, 1, 1, 2, 1, 2, 2, 2, 1, 2,
        0, 2, 1, 1, 2, 1, 1, 2, 1, 2,
        2, 1, 2, 0, 2, 2, 1, 1, 2, 3,
        1, 2, 1, 2, 1, 2, 2, 2, 1, 2,
        1, 2, 1, 1, 2, 1, 2, 1, 2, 0,
        2, 1, 2, 2, 1, 2, 1, 1, 2, 1,
        2, 1, 0, 2, 1, 2, 4, 2, 1, 2,
        1, 1, 2, 1, 2, 1, 2, 1, 2, 2,
        1, 2, 1, 2, 1, 2, 1, 2, 0, 1,
        2, 1, 2, 1, 2, 1, 2, 2, 3, 2,
        1, 2, 1, 2, 1, 1, 2, 1, 2, 2,
        2, 1, 2, 2, 0, 1, 1, 2, 1, 1,
        2, 1, 2, 2, 1, 2, 2, 0, 2, 1,
        1, 2, 1, 3, 2, 1, 2, 1, 2, 2,
        2, 1, 2, 1, 2, 1, 1, 2, 1, 2,
        1, 2, 2, 0, 2, 1, 2, 1, 2, 1,
        1, 2, 1, 2, 1, 2, 0, 2, 1, 2,
        2, 3, 2, 1, 1, 2, 1, 2, 1, 2,
        1, 2, 2, 1, 2, 1, 2, 1, 2, 1,
        2, 1, 0, 2, 1, 2, 1, 2, 2, 1,
        2, 1, 2, 1, 2, 0, 1, 2, 3, 2,
        1, 2, 1, 2, 2, 1, 2, 1, 2, 1,
        2, 1, 1, 2, 1, 2, 2, 1, 2, 2,
        1, 0, 2, 1, 2, 1, 1, 2, 3, 2,
        1, 2, 2, 2, 1, 2, 1, 2, 1, 1,
        2, 1, 2, 1, 2, 2, 2, 0, 1, 2,
        1, 2, 1, 1, 2, 1, 1, 2, 2, 2,
        0, 1, 2, 2, 1, 2, 3, 1, 2, 1,
        1, 2, 2, 1, 2, 2, 1, 2, 2, 1,
        1, 2, 1, 1, 2, 2, 0, 1, 2, 1,
        2, 2, 1, 2, 1, 2, 1, 2, 1, 0,
        2, 1, 2, 3, 2, 1, 2, 2, 1, 2,
        1, 2, 1, 2, 1, 1, 2, 1, 2, 2,
        1, 2, 2, 1, 2, 0, 1, 2, 1, 1,
        2, 1, 2, 3, 2, 2, 2, 1, 2, 1,
        2, 1, 1, 2, 1, 2, 1, 2, 2, 2,
        1, 0, 2, 1, 2, 1, 1, 2, 1, 1,
        2, 2, 1, 2, 0, 2, 2, 1, 2, 1,
        1, 4, 1, 1, 2, 1, 2, 2, 2, 2,
        1, 2, 1, 1, 2, 1, 1, 2, 1, 2,
        0, 2, 2, 1, 2, 1, 2, 1, 2, 1,
        1, 2, 1, 0, 2, 2, 1, 2, 2, 3,
        2, 1, 2, 1, 2, 1, 1, 2, 1, 2,
        2, 1, 2, 2, 1, 2, 1, 2, 1, 0,
        2, 1, 1, 2, 1, 2, 2, 1, 2, 2,
        1, 2, 0, 1, 2, 3, 1, 2, 1, 2,
        1, 2, 2, 2, 1, 2, 1, 2, 1, 1,
        2, 1, 1, 2, 2, 1, 2, 2, 0
    };
 private static final int MDayCnt[] = {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


출처 : http://blog.daum.net/canel777/98
Posted by 아로나
JAVA & Open Framework2011. 6. 9. 13:29

Java class 파일이 JDK 의 어떤 버전으로 컴파일 되었는지를 확인하는 방법은

다음의 두가지 방법이 존재한다.

 

1. javap 를 이용하는 방법

클래스 파일 디렉토리 위치에서 다음의 명령어를 통해 확인이 가능하다.

 

javap –verbose [클래스파일명(.class는 제외)] | find /N "version"

 

image

 

2. class 파일의 hex 를 직접 확인하는 방법

class 파일을 hex 값 보기가 가능한 편집기(울트라 에디트 등) 으로 열어서

다음을 확인한다.

 

-.minor version : 4~5번 인덱스의 hex 값을 10진수로 변환한 값

-.major version : 6~7번 인덱스의 hex 값을 10진수로 변환한 값

image

   ※ major/minor 버전 별 JDK 실제 배포 버전(7번 인덱스 값으로 구분했을 경우)

   JDK 1.6 = hex 32(dec : 50)

   JDK 1.5 = hex 31(dec : 49)

   JDK 1.4 = hex 30(dec : 48)

   JDK 1.3 = hex 2F(dec : 47)

   JDK 1.2 = hex 2E(dec : 46)


위의 예의 경우 버전은 46.0 이다.

이 방법은 다음의 class 파일 구조를 설명한 문서에 기초한 방법이다.

 

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
    }

(http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html)


 


※ major/minor 버전 별 JDK 실제 배포 버전


  JDK 1.6 = hex 32(dec : 50)

  JDK 1.5 = hex 31(dec : 49)

  JDK 1.4 = hex 30(dec : 48)

  JDK 1.3 = hex 2F(dec : 47)

  JDK 1.2 = hex 2E(dec : 46)



- 출처 : http://lachesis76.blogspot.com/2010/12/java-class-jdk.html -

Posted by 아로나
JAVA & Open Framework2011. 6. 7. 21:17

1. 먼저 JDK 1.5와 1.6을 각각 설치한다.
(물론 기존에 1.6을 사용하고 있었으나 1.5를 써야할 상황이 생겼다;;)

- JDK도 버전별로 설치하고 JRE도 버전별로 설치해야 한다. 주의!!
- 그래서 본인의 경우 jdk의 경우 C:\Java\jdk1.5 와 C:\Java\jdk1.6 가 둘다 있으며
  jre의 경우에도 C:\Program Files\Java\jre1.5 와 C:\Program Files\Java\jre1.6 이 모두 존재한다.


2. 이클립스에서 버전별로 설정하기

- 이클립스 - window - preferences - java - installed JREs에 가보면 기존의 jre 1.6이 설치되어 있다.

- 여기에서 jre 1.5를 추가해야 한다.

- 여기에서 오른쪽 Add 버튼 - JRE Type Standard VM으로 선택 - JRE홈을 아까 JRE 1.5 설치한 폴더로 지정한다.
- 추가된 라이브러리 파일들 확인하고 finish 클릭(체크는 그대로 1.6으로 놔둔다.)

=> JRE 1.5 추가 완료 - 이클립스 재구동(그래야 JRE1.5를 인식한다.)

- 다시 이클립스 - window - preferences - java - installed JREs 에서 또다시 하위에 있는 Execution Environments 선택
- JavaSE-1.6선택하고 오른쪽에서 JRE 1.6 체크 - J2SE-1.5선택했을땐 오른쪽에 JRE1.5와 JRE1.6이 모두 나타나야 한다!!
  (안나타난다면 일단 이클립스 재구동;;)
- 어쨌든 나타났다다면 perfect match로 나타나는 J2SE-1.5와 맞는 JRE 1.5 체크박스에 체크!!! - OK

=> JDK 1.5까지 추가 완료!!



3. 설정 후 프로젝트에 적용(새 프로젝트)

- 일반 Java 프로젝트의 경우에는 프로젝트 생성 시 JRE이 설정을 아까 설정한 J2SE-1.5로 설정해주면 된다.


4. 설정 후 프로젝트에 적용(기존 프로젝트)

- 기존의 1.6으로 설정되어 있는 프로젝트 1.5로 변경하기!!(혹은 기존에 추가되어 있는 CVS 및 톰캣 프로젝트의 경우)

1) java build path 설정
- 해당 프로젝트에서 마우스 오른쪽 - properties - java build path - libraries 탭 - 라이브러리 목록중에서 JRE System
Library를 확인하고 jre1.6으로 되어 있으면 선택해서 remove - 그리고 Add Library 클릭
- JRE System Library 선택 - Excution Environment - J2SE-1.5(jre1.5)로 선택 - finish
- 라이브러리 목록에 J2SE-1.5(jre1.5)추가 됐나 확인하고 - OK하면 완료!!

- 1.5로 변경되었는지 확인 방법
- 이클립스에서 Navigator가 아닌 Package Explore창으로 프로젝트를 확인해본다.
- 그래서 해당 프로젝트 하위에 JRE System Library[J2SE-1.5(jre1.5)]와 같이 jre 1.5버전이 추가되어 있으면 성공!!!

2) java complier 설정
- JDK compiler가 1.6으로 되어 있다면 Enable projct specific settings를 체크하고 컴파일러를 1.5로 수정
- 그리고는 아래 사항들도 1.5로 변경되었나 확인 - apply

=> 사실 여기까지만 하고 각 프로젝트의 Run/Debug Settings을 사용안해도 된다!!!(이게 더 편함)
(Run - Run Configuration - Java Application에 있는 세팅환경을 지워버리고 2번까지만 해도 구동이 된다.)


* 3번은 필요한 경우에만 세팅.

3) Run - Run Configuration가서 1.5에 맞는 세팅환경 추가
- 기존의 1.6 환경을 해당 프로젝트에 맞게 1.5로 변경하는 세팅을 해주어야 한다.
- 이클립스 - Run - Run Configuration - Java Application에서 해당 프로젝트에 맞게 새로운 세팅을 추가한다

- Java Application에 이미 있는 세팅들중 하나를 선택하여 마우스 오른쪽 - Duplicate - Name 설정.( ex)Tomcat 5.x_test1)
Main 탭 - 해당프로젝트 선택
JRE 탭 - Jre 1.5로 설정
Classpath 탭 - Jre 1.5로 설정(기존 1.6이 있으면 제거)
Source 탭 - 기존의 소스를 해당 소스로 변경한다.
-> 추가할 소스들 :
*Java Project : 해당프로젝트 선택 
*External Archive : C:\P~~ Files\java\jre1.5\lib\ext\*.*, C:\P~~ Files\java\jre1.5\lib\*.*, 해당 프로젝트내의 LIB폴더내 파일 전부, 톰캣설치폴더내의 common\lib\servlet-api.jar,jasper-runtime.jar,jsp-api.jar
*Workspace Folder : 해당프로젝트 내 src 폴더(하위폴더 미포함)
- apply - 완료

-> 해당 프로젝트로 가서 마우스 오른쪽 클릭 후 properties - Run/Debug Settings에 가서 방금 추가한 실행환경 설정이 적용되어 있나 확인하면 끝!!!



'JAVA & Open Framework' 카테고리의 다른 글

JAVA - 공휴일 여부 체크  (1) 2011.08.16
Java class 의 JDK 버전 확인 방법  (1) 2011.06.09
PreparedStatement Query  (0) 2011.05.27
Java Collection  (0) 2011.05.25
MessageResources.properties에 한글 입력하기.(eclipse)  (0) 2011.04.19
Posted by 아로나
JAVA & Open Framework2011. 5. 27. 10:50

자바 코딩을 할 일이 생겼는데, 쿼리문을 날리는데 Prepared Statement를 사용하면 좋다는 생각이 나서 사용해보기로 했다.. 평소엔 신경도 쓰지 않던 내용을 일도 많고 바쁜데 하필 적용시켜보겠다고 생각을 했는지는 모르겠다.. 하긴, 물론 이렇게 한다고 해서 시간이 더 걸리거나 어려운 방법은 아니다..

Prepared Statement는 DB 서버가 쿼리문을 캐쉬해두어 성능을 높일 수 있게 해주는 알고리즘을 개선 시키기 위해 등장한 것으로, 패러미터가 계속해서 바뀌는 쿼리문도 캐쉬를 해둘 수 있도록 하기 위해 고안된 것이다.. 즉, 변화되는 부분을 ? 로 처리해두고 실제 실행될때는 실행될때마다 다른 값이 매칭되어 실행되도록 하는 방식이다.. 솔직히 정말로 빠른지 어떤지는 직접 느껴보지는 못했다..

사용자 삽입 이미지

일반적인 쿼리문 날리는 것과 별반 다를바 없지만, 쿼리문 작성할때 변경되어야 하는 부분을 ?로 표시하여 작성을 하고, 그 부분의 값을 setParam 함수 호출을 통해 값을 할당한다는 부분이 조금 다른 부분이다.. setParam 이라는 함수 내부에서는 psmt.setString(1, 변수값); 과 같이 몇번째 ?를 어떤 값으로 바꿔야 하는지를 명시하는 코드가 들어가 있다..

사실, 그냥 막 쿼리 날리는 것과 이렇게 수정하는 것과의 차이는 거의 없고 어려운 일이 아니기 때문에 여기까지는 좋았다.. 그런데 사용중 쿼리문이 에러가 나는 현상이 발생.. 로그를 찍어서 어떤 쿼리문이 날아가고 있는지 보고 싶은데, 이런 젠장.. PraparedStatement의 쿼리 내용을 찍을 수 있는 방법이 없는거다..

자바에서 기본적으로 제공해주는 방법이 있을 것으로 알았다.. ToString으로 하면 될 줄 알았는데, 너무 순진한 생각이었다.. 결국, 자바에서 기본적으로 제공되는 방법으로 Prepared Statement의 실제 실행된 쿼리문 모습을 알아낼 수는 없다는 것을 알게되었다..

검색을 통해 찾은 사이트에서 방법을 알아낼 수 있었다.. (참조 : PreparedStatement - ? 치환해서 로그찍는 소스, 소스 수정에 필요한 파일 다운로드 : LoggableStatement.java) 이런 기능은 자바가 기본으로 제공해주는 Prepared Statement 클래스에 들어가 있으면 좋겠구만 말이지..

사용자 삽입 이미지


이렇게 수정하고 나면, Prepared Statement로 작성된 쿼리문도 최종 수행된 쿼리문이 어떤 모습으로 날아갔는지 확인해 볼 수 있게된다..


- 출처 : http://madchick.tistory.com/10 -
Posted by 아로나
JAVA & Open Framework2011. 5. 25. 15:13
여태 개발하면서 무지 많이 써오던 컬랙션들. 성능이나 별다른 고민없이 걍 대충 써왔던 것 같다. "켄트 벡의 구현 패턴"이란 책을 보다 보니 자세한 설명이 있어서 그 동안 알고 있던것과 더불어 정리해 두는 게 조을 것 같다.

1. 인터페이스
사용자 삽입 이미지

The core collection interfaces.

Queue는 거의 사용하지 않고 책에 없으니까 생략 ^^


- 배열
가장 단순하지만 가장 유연하지 못한 컬렉션.
크기가 고정되어 있고 원소 접근 방법이 용이하면 빠르다.
단순한 연산의 경우 배열은 다른 컬렉션에 비해 시간, 공간 모든 면에서 효율적이다.
일반적으로 배열 접근(element[i])은 ArrayList를 사용했을 때(elements.get(i))에 비해 10배 이상 빠르다고 한다.
대부분의 경우 유연성 문제 때문에 배열보다는 다른 컬렉션을 사용하고, 프로그램의 일부에서 성능이 중요한 경우 배열을 사용하는 것도 고려하는 것이 좋을 듯


- Iterable
기본적인 컬렉션 인터페이스로 순차 열람(iteration)을 지원한다.
어떤 변수를 Iterable로 선언하는 것은 그 변수가 여러 개의 값을 갖고 있음을 뜻할 뿐이다.
실제로 Iterable 인터페이스를 살펴 보면 자바 컬렉션의 모든 인터페이스, 구현 클래스들이 implement하고 있는 것을 확인할 수 있고 Iterable에 정의된 메소드는 Iterator<T> iterator() 뿐이다.
Iterator를 이용하면 Iterator 인터페이스에서 지원하는 세가지 메소드 (hasNext(), next(), remove())를 사용할 수 있다.

자바 5에서는 암묵적으로 iterator() 메소드를 호출하여
for (Element element : elements)
{
......
}
의 형식으로 간편하게 루프를 구성할 수 있게 한다.
실제 프로그램에서는 Iterable 인터페이스를 직접 사용할 일은 없으니 이런게 있다고 정도만 알아두면 될것이다.


- Collection
Iterable을 상속하며, 원소
의 추가, 삭제, 검색, 크기 지원 등의 메소드를 추가로 지원한다.


- List
원소의 순서가 정의되어 있으며, 컬렉션상의 위치를 통해 원소에 접근할 수 있다.
따라서 List를 사용하면 컬렉션 상에서의 인덱스를 통해 어떤 원소를 접근 할 수 있다.
원소간의 순서가 중요한 경우, 예를 들어 도착 순서대로 메세지를 처리하는 큐의 경우에는 리스트를 사용해야 한다.


- Set
중복된 원소가 없는 컬렉션
중복원소(상호간 equals()의 결과가 참인 원소)를 허용하니 않는 컬렉션 원소 사이의 순서가 없으므로, 이전 순차 열람할 때의 원소 순서가 다음 순차 열람할 때 보장되지 않는다.


- SortedSet
중복된 원소가 없으며 원소간의 순서가 정해진 컬렉션
컬렉션에 추가된 순서나 명시적인 인덱스 번호에 따라 순서가 정해지는 List와 달리 SortedSet은 Comparator에 의해 순서를 정한다. 명시적인 순서를 제공하지 않는 경우에는 "자연 순서(natural order)"가 사용된다. 예를 들어 문자열은 알파벳 순으로 정렬

아래는 Comparator의 사용예
public Collection<String> getAlphabeticalAuthors()
{
    Comparator<Author> sorter = new Comparator<Author>()
    {
        public int compare(Author o1, Author o2)
        {
            if (o1.getLastName().equals(o2.getLastName()))
            {
                return o1.getFirstName().compareTo(o2.getFirstName());
                return o1.getLastName().compareTo(o2.getLastName());
            }
        };

        SortedSet<Author> results = new TreeSet<Author>(sorter);
        for (Book each: getBooks())
        {
            results.add(each.getAuthor());
        }
       
        return results;
    }
}


- Map
키에 의해 원소를 저장하고 접근하는 컬렉션
Map은 List처럼 키를 사용해서 원소를 저장하지만, List가 정수만을 키로 사용할 수 있는 반면 Map은 임의의 객체를 키로사용할 수 있다.
또 Map는 다른 컬렉션 인터페이스와는 형태가 상이하여 다른 컬렉션 인터페이스를 상속하지 않고, 내부적으로 키에 대한 컬렉션과 데이터에 대한 컬렉션의 2개 컬렉션을 유지한다.
컬렉션을 사용할 때는 항상 인터페이스를 선언하여 사용

List<String> list = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();

Collection 인터페이스를 사용하면 유연성은 가장 높겠지만 실제 사용한 적은 거의 없는 것 같다.
List, Set, Map이면 ㅇㅋㅂㄹ



2. 구현

사용자 삽입 이미지

컬렉션에 대해 구현 클래스를 선택하는 것은 주로 성능과 관련이 있다.
위의 표에 소개한 구현 이외에도 무지하게 많은 구현들이 있다. 각 구현체의 특성을 살펴보고 필요한 것을 가져다 사용하면 된다.

일단 가장 단순한 구현을 사용하여 시작하고 추후 경험에 따라 튜닝하는 것이 좋다.
컬렉션중 가장 많이 사용되는 클래스는 ArrayList이며, 그 다음은 HashSet이다.
(이클립스와 JDK에서 ArrayList는 3400번, HashSet은 800번 사용되었다고 한다.)


- Collection 구현
Collection 인터페이스만 구현한 클래스는 없는 듯 하다. 단순한 컬렉션이 필요한 경우
그냥 ArrayList를 사용하자. ArrayList 사용시 성능상 문제되는 부분은 컬렉션의 크기에 비례해서 연산 시간이 커지는 contains(Ojbect)와 이 메소드를 이용하는 다른 메소드(remove() 등)이 있다.
이 때 중복 원소들을 제거해도 상관이 없다면 HashSet으로 교체하면 좋다. 그러나 중복 원소가 이미 없는 경우라면 별 차이가 없을 수도 있다.


- List 구현
ArrayList와 LinkedList
ArrayList는 원소 접근이 빠르고 원소 추가 및 제거가 느린 반면 LinkedList는 원소 접근이 느리지만 원소 추가와 제거는 빠르다.


- Set, SortedSet 구현
HashSet은 가장 빠르지만 원소간의 순서를 보장해주지 않는다.
LinkedHashSet은 원소 간 순서를 보장해 주지만 원소 추가 삭제 시 30% 정도 시간이 더 걸린다.
TreeSet은 Comparator에 따라서 원소를 정렬하지만 원소 추가 삭제 시간이 logn(n은 컬렉션의 크기)에 비례해서 커진다.


- Map 구현
Map 구현은 Set 구현과 비슷한 패턴을 보인다.
HashMap은 가장 빠르고 단순하다.
LinkedHashMap은 컬렉션에 추가된 원소 간의 순서를 보장한다.
TreeMap(SortedMap 의 구현)은 키의 순서에 따라 순차 열람이 가능하지만 원소의 추가 제거 시간이 logn(n은 컬렉션의 크기)에 비례한다.



3
. Collections
Collections는 다른 컬렉션 인터페이스에 넣기 적절치 않은 기능들을 모아 놓은 유틸리티 클래스이다.


-
검색
indexOf() 연산에 걸리는 시간은 리스트의 크기에 비례한다. 원소들이 정렬되어 있을 경우Collections.binarySearch(list, element)를 사용하여 log2n에 비례하는 시간에 검색할 수 있다.
원소가 리스트에 존재하지 않는다면 음수를 반환하고, 리스트가 정렬되어 있지 않다면 결과는 예측불가


- 정렬
reverse(list)는 리스트에 속해 있는 모든 원소 간의 순서를 거꾸로 바꾼다.
shuffle(list)는 순서를 임의로 바꾼다.
sort(list), sort(list, comparator)는 오름차순으로 원소를 정렬한다.
이진 검색과 달리 ArrayList와 LinkedList에서 정렬 수행 성능은 거의 같다.
정렬을 수행할 경우
컬렉션의 원소들이 일단 배열로 복사되어 정렬된 후 다시 본래의 컬렉션으로 복사되기 때문


- 수정 불가능한 컬렉션
신뢰할 수 없는 코드에 컬렉션을 전달하는 경우 Collections.unmodifiableCollection() 메소드를 이용하면 클라이언트가 수정하려 들 경우 예외를 발생시키도록 할 수 있다.


-
단일 원소 컬렉션
하나의 원소를 전달해야 하지만 컬렉션 인터페이스를 사용해야 하는 경우 사용
Set의 경우 Collections.singleton(T o), List와 Map의 경우 singletonList(T o), singletonMap(K key, V value)를 사용


- 무원소 컬렉션
컬렉션 인터페이스를 사용해야 하지만 전달할 원소가 없는 경우에는 Collections에서 수정할 수 없는 무원소 컬렉션을 생성해서 사용
Collections.emptyList(), emptySet(), emptyMap()


- 동기화 컬렉션
이전 시대의 유물인 Vector와 Hashtable이 ArrayList와 HashMap간의 차이점은 전자가 쓰레드 안전인 반면 후자는 아니라는 것이다.
동기화가 필요없는 경우라면 ArrayList, HashMap을 사용하고 동기화가 필요한 경우  Collections.synchronizedCollection(), Collections.synchronizedList(),    Collections.synchronizedSet(), Collections.synchronizedMap()를 사용하여 ArrayList, HashMap을 래핑하면 멀티 쓰레드 환경에서도 걱정이 사라진다.

- 출처 : http://blog.naver.com/windziel?Redirect=Log&logNo=60048694876 -

Posted by 아로나