오라클의 TDE기능을 체험하기 위해-_-; 마땅한 테스트 서버도 없고 해서 솔라리스를 설치해서 오라클을 설치해봤습니다.
2번만에 성공했습니다.

http://www.solanara.net/
여기는 윈디하나의 솔라나라인데, 여기에 너무 정리를 잘 해놔서 설치하는데 무리가 없었습니다.

저는 1번 실패하게 된 것이 SWAP공간이 없어서 실패를 했습니다.
swap공간은 이 분 블로그를 보고 참조했습니다.
http://akmamb.tistory.com/108

그 설치할 때 패키지가 있는지, 공간이 있는지 검사하는 부분이 있는데 저는 swap공간 부족과 SUNWi1cs랑 SUNWi15cs패키지가 없어서 경고를 먹었는데 두개 패키지는 설치하고 swap공간은 그냥 생까고 진행했더니 아놔 안되려면 처음부터 안되던가-_-; 설치하다가 데이터베이스를 생성하는 도중에 메모리가 딸리다면서 안됩니다-_-;

swap공간 늘리고 설치하면 완전 잘돼요 ^^

그리고, 또 걸렸던 것이!
서버 시작과 동시에 자동으로 실행되게 하려고 했는데 $ORACLE_HOME/bin/dbstart 스크립트가 안먹히는 겁니다-_-; 그래서 또 검색해보니-_-;
/var/opt/oracle/oratab 파일이 있는데, 이 놈의 맨 끝이 N으로 되어있습니다.
orcl:/export/home/oracle/oracle/product/10.2.0/db_2:Y

이걸 Y로 바꿔주니 rc3.d에 등록하니 잘 되더라구요.

실행스크립트는 그냥 이렇게-_-;
#!/bin/sh

case "$1" in
        start)
                echo -n "Starting oracle: "
                su - oracle dbstart /export/home/oracle/oracle/product/10.2.0/db_2
                echo
                ;;
        stop)
                echo -n "Shutting down oracle: "
                su - oracle dbshut /export/home/oracle/oracle/product/10.2.0/db_2
                echo
                ;;
esac
exit 0 

참고로 ORACLE_HOME이 oracle계정에 선언이 되어야겠죠? ORACLE_SID 등도 설정 되어야할 겁니다.
그건 윈디하나의 솔라나라에서 보고 따라하시면 돼요 ^^


PS. 윈디하나의 솔라나라에 좋은 자료가 많군요! 앞으로 자주 애용해야겠습니다 ^^

 
Posted by 머드초보
,
 
Oracle XE에서는 기본적으로 APEX를 제공합니다.
웹으로 db를 컨트롤할 수 있는 웹애플리케이션입니다. phpmyadmin같은 거죠.
그런데 이 apex가 디폴트로는 설치된 컴퓨터에서만 접속이 가능합니다.
http://www.oracle.com/technology/global/kr/products/database/application_express/html/apex_and_xe.html
여기에서 5번 항목을 보면,
5. Oracle APEX는 기본적으로 Oracle Database XE가 설치된 컴퓨터에서만 접근이 가능합니다. 관리자는 Database Home Page에서 원격 사용자의 접근을 활성화할 수 있습니다.
라고 되어있습니다.
저기서 원격 사용자의 접근을 활성화하는 것은 apex에서 해야한다는 얘기인데....-_-;
X-Window가 안깔린 리눅스에서 설치를 해버리니 apex에 접속할 수가 없습니다.
리눅스에서 netstat -an 해보면
tcp        0      0 127.0.0.1:8081          0.0.0.0:*               LISTEN
이렇게 되어있습니다.
즉 로컬에서만 접속되도록 8081이 오픈이 되어있네요.

리눅스에 웹브라우저가 설치가 되어있다면, system계정으로 로그인해서
관리 -> http엑세스관리 -> "로컬 서버와 원격 클라이언트에서 사용할 수 있음" 선택 확인.
하면 됩니다.

리눅스에 웹브라우저가 없다면 sqlplus로 직접 접속해서 설정을 변경할 수 있습니다.
root@ubuntu:~# sqlplus

SQL*Plus: Release 10.2.0.1.0 - Production on 목 7월 3 22:11:29 2008

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

사용자명 입력: system
암호 입력:

다음에 접속됨:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production
SQL> EXEC DBMS_XDB.SETLISTENERLOCALACCESS(FALSE);

PL/SQL 처리가 정상적으로 완료되었습니다.

SQL>
저렇게 설정하면 외부에서 접근이 가능합니다.

netstat -an하면
tcp        0      0 0.0.0.0:8081            0.0.0.0:*               LISTEN
아이피가 변경이 되어있네요 ^^

 
Posted by 머드초보
,
 
Thread를 두개를 실행시켜서 DB에 동시에 INSERT를 시켜버리니 INSERT할 때 데이터가 AUTOINCRESEMENT가 아니라 ID부분을 직접 입력해서 하는 부분이면 문제가 발생합니다.
그래서 이 MERGE INTO문을 활용해서 데이터가 있으면 INSERT하고 없으면 UPDATE하는 구문을 만들었습니다.
이렇게 해도 제 생각이지만, Thread한 놈은 이미 데이터가 없는 것을 확인하고 insert를 시도 하려고 하고, 다른 Thread놈은 저 놈이 insert를 아직 하지 않았으니까 검색해서 안나오니 insert를 해보려고 하니 둘 다 insert를 시도하게 되더라구요.

이게 예제 입니다.
우선 테이블구조입니다.
테이블은 autoincreament가 아닌 수동적인 기본키를 사용합니다.
[code]CREATE TABLE "INSERTTABLE" ( "ID" NUMBER NOT NULL ENABLE, "DATA" VARCHAR2(4000) NOT NULL ENABLE, CONSTRAINT "INSERTTABLE_PK" PRIMARY KEY ("ID") ENABLE )
[/code]


applicationContext.xml
[code]<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:component-scan base-package="thread" />
   
    <!-- oracle용 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        p:driverClassName="oracle.jdbc.driver.OracleDriver"
        p:url="jdbc:oracle:thin:@localhost:1521:XE"
        p:username="mudchobo" p:password="1234" />
 
    <!-- SqlMap setup for iBATIS Database Layer -->
    <bean id="sqlMapClient"
        class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"
        p:dataSource-ref="dataSource"
        p:configLocation="classpath:config/SqlMapConfig.xml" />
   
    <bean id="sqlMapClientTemplete"
        class="org.springframework.orm.ibatis.SqlMapClientTemplate"
        p:sqlMapClient-ref="sqlMapClient"/>
           
</beans>
[/code]
SqlMapConfig.xml
[code]<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMapConfig     
    PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"     
    "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
    <sqlMap resource="config/Insert.xml" />
</sqlMapConfig>
[/code]
Insert.xml
[code]<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap     
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Sqlmap">
       <insert id="insertData">
           MERGE INTO INSERTTABLE
           USING DUAL
           ON (ID = 1)
           WHEN MATCHED THEN
           UPDATE SET
           DATA = 'HERMUSSERI'
           WHEN NOT MATCHED THEN
           INSERT (ID, DATA)
           VALUES (1, 'MUDCHOBO')
       </insert>
      
</sqlMap>
[/code]
ThreadTestDao.java
[code]package thread;

public interface ThreadTestDao {
    public void insertData();
}
[/code]
ThreadTestDaoImpl.java
[code]package thread;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class ThreadTestDaoImpl implements ThreadTestDao {

    @Autowired
    private SqlMapClientTemplate sqlMapClientTemplate;

    @Override
    public void insertData() {
        sqlMapClientTemplate.insert("insertData");
    }
}
[/code]
TestThread.java
[code]package thread;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Scope("prototype")
@Component
public class TestThread extends Thread {

    @Autowired
    private ThreadTestDao threadTestDao;
   
    @Override
    public void run() {
        threadTestDao.insertData();
    }
}
[/code]
ThreadTest.java
[code]package thread;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ThreadTest {

    public static void main(String[] args) {
        String configLocation = "config/applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(
                configLocation);

        TestThread testThread1 = (TestThread) context
                .getBean("testThread");
        TestThread testThread2 = (TestThread) context
                .getBean("testThread");
       
        testThread1.start();
        testThread2.start();
    }
}
[/code]
실행할 때 계속 몇번을 실행해보면-_-; unique constraint 또는 ORA-00001: 무결성 제약 조건(MUDCHOBO.INSERTTABLE_PK)에 위배됩니다를 볼 수 있을 겁니다.

이게 제 생각인데, 첫번째 스레드가 돌면서 데이터를 확인하니 없길래 insert를 하려던 찰나에 두번째 스레드놈도 거의 동시에 데이터를 확인하니 없어서 insert를 시키려다가 에러가 나는 듯한데요.

트랜잭션도 먹이고 별 지롤을 다 했는데 안되길래 그냥 싱크방식으로 변경해버렸습니다.
insertData라는 메소드에
[code]@Override
    synchronized public void insertData() {
        sqlMapClientTemplate.insert("insertData");
    }
[/code]
메소드 앞에 synchronized를 붙입니다.
이건 어떤 스레드가 호출을 했으면 다음 스레드는 기다린 다음에 접근하게 되는 즉 싱크방식으로 처리를 시킬 수 있는 메소드가 됩니다.
이렇게 처리하면 효율은 좀 떨어지겠지만, 유니크 중복에러는 그나마 막을 수 있습니다.

다른 방법 있으면 좀 알려주세요 ㅠㅠ 고생하고 있습니다 ㅠ




 
Posted by 머드초보
,
 
저는 전에 데이터를 SELECT해와서 데이터가 0개면 INSERT하고, 있는 놈이면 해당 값을 업데이트 하는 식으로 했었는데요. 우연히 MERGE INTO라는 것을 알게 되었네요.

ORACLE에서만 되는 듯 하네요-_-; 9i이상에서만 된다고 하네요.

MERGE INTO의 목적은 어떤 테이블이나 뷰테이블을 해당 목표테이블과 합체(MERGE)하기 위한 목적인데요. 이걸 이용해서 데이터가 들어왔을 때 있는 데이터면 UPDATE하고, 없는 데이터면 INSERT하는 형태로도 쓰일 수 있습니다.

http://radiocom.kunsan.ac.kr/lecture/oracle/sql/merge.html
위에는 MERGE INTO를 잘 설명한 사이트네요.

여기서 조금 응용하면 원하는 대로 구현할 수 있습니다-_-;

MERGE INTO 테이블명  별칭
USING 대상테이블/뷰  별칭
ON 조인조건
WHEN MATCHED THEN
  UPDATE SET
   컬럼1=값1
   컬럼2=값2
WHEN NOT MATCHED THEN
  INSERT (컬럼1,컬럼2,...)
       VALUES(값1,값2,...);

MERGE INTO다음에 나오는 테이블명은 실제로 데이터가 들어가거나 업데이트 되는 테이블이구요.
USING 다음에 나오는 테이블명은 실제 데이터를 가져오거나 할 테이블이구요.
ON은 WHERE과 같은 조건문이죠.
WHEN MATCHED THEN은 매치되는게 있으면 UPDATE하라는 얘기구요.
WHEN NOT MATCHED THEN은 매치되는게 없으면 INSERT하게 되죠.

응용해봅시다.
[code]
           MERGE INTO INSERTTABLE
           USING DUAL
           ON (ID = 1)
           WHEN MATCHED THEN
           UPDATE SET
           DATA = 'idoori'
           WHEN NOT MATCHED THEN
           INSERT (ID, DATA)
           VALUES (1, 'mudchobo')
[/code]
INSERTTABLE이라는 곳에 USING은 DUAL이라고 했는데 DUAL은 dual은 1개의 레코드 만을 갖는 dummy 테이블이라고 합니다. select 1 from dual해버리면, 1이 나오죠. 대상테이블은 필요없으니 dual로 설정합니다.
ON에서 ID = 1은 ID가 1인게 만약 있으면, DATA부분을 idoori로 업데이트하고, 없으면 mudchobo로 넣게 되는겁니다.

아놔 별거 없는데 막 늘어썼네-_-;
 
Posted by 머드초보
,
 

http://mudchobo.tomeii.com/tt/257 에 이어서-_-;

오라클에서 해보겠습니다.
아 우선 오라클용 jdbc가 필요해요!
ojdbc6.jar 등의 jbdc ^^

걍 오라클용 dataSource로 바꿔주면 돼요-_-;

applicationContext.xml
[code]
<!-- oracle용 -->
 <bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource"
  p:driverClassName="oracle.jdbc.driver.OracleDriver"
  p:url="jdbc:oracle:thin:@localhost:1521:XE"
  p:username="dbid" p:password="dbpw" />

<bean id="insertDataOracle" class="com.mudchobo.TestInsertOracle"
  p:sqlMapClient-ref="sqlMapClient"/>
[/code]
아까 그 TestInsert 인터페이스입니다.
TestInsert.java
[code]
package com.mudchobo;

import org.springframework.transaction.annotation.Transactional;

public interface TestInsert {

 @Transactional(readOnly=true, rollbackFor={Throwable.class})
 public void insertData() throws Throwable;
 
}
[/code]
저기 선언된 bean인 TestInsertOracle을 구현해봅시다.
TestInsertOracle.java
[code]
package com.mudchobo;

import java.io.IOException;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

public class TestInsertOracle extends SqlMapClientDaoSupport implements
  TestInsert {

 @Override
 public void insertData() throws Throwable {
   getSqlMapClientTemplate().insert("insertData");
   System.out.println("oracle에 insert성공");
   throw new IOException();
 }
}
[/code]
MySQL처럼 IOException을 임의로 발생시킵니다.
main부분을 봅시다.
TestTransaction.java
[code]
package com.mudchobo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestTransaction {

 public static void main(String[] args) {
  String[] configLocation = {"applicationContext.xml"};
  ApplicationContext context =
   new ClassPathXmlApplicationContext(configLocation);
 
  TestInsert testInsertOracle =
   (TestInsert) context.getBean("insertDataOracle");
 
  try {
   testInsertOracle.insertData();
  } catch (Throwable e) {
   System.out.println("예외발생");
  }
 }
}
[/code]
오라클에서는 readOnly로 걸어 놨는데도 불구하고 insert가 됩니다.
그리고 예외를 발생시켰기 때문에 rollbackfor 값을 Throwable로 줬기때문에 IOException이 발생했을 때 롤백하게 됩니다. 잘보면 롤백이 되어있습니다.

오라클에서 set transaction read only 라고 쿼리를 날리면 transaction을 read only로 해버릴 수 있더라구요.
뭐 그냥 그렇다구요-_-;

 
Posted by 머드초보
,