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 머드초보

댓글을 달아 주세요

  1. BlogIcon 정원희 2008.07.02 17:29  댓글주소  수정/삭제  댓글쓰기

    DB에서 제공되는 auto_increment 값이 아니라 사용자가 수동으로 넣어주는 값이면 뭘 어떻게 하든간에 중복 insert 문제가 발생할 수 밖에 없습니다. 일단 unique 라서 DB의 무결성이 깨지진 않습니다만 민감한 부분의 코드라면 두명이 동시에 사용하는 경우 한명은 오류화면을 보게 된다는 거겠네요 -_-;

    DBMS에서 원자성을 제공하는 기능을 쓰도록 변경하셔야 합니다. 수동으로 뭔가 하거나 키값의 max 를 구해서 입력해도 동일한 문제가 발생할 것이구요, 자바에서 synchorinized 로 처리를 했다고 해도 해당 어플리케이션 내에서만 보장되는 것이지 다른 프로그램이나 DBMS 자체에 로그인해서 수작업으로 쿼리를 날리거나 할때는 또 문제가 될 수 있습니다.

    • 머드초보 2008.07.02 19:46  댓글주소  수정/삭제

      오...좋은 의견감사합니다 ㅠ
      도저히 방법이 생각나지 않아 그냥 synchronized로 처리했는데 ^^
      원자성을 제공하는 기능이라함은 오라클에서 제공하는 그런 기능이 있다는 말씀이신가요?
      음 찾아봐야겠네요 감사해요~ ^^

  2. 2008.07.03 13:25  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  3. 2008.11.24 15:59  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • 머드초보 2008.11.25 11:29  댓글주소  수정/삭제

      전 그냥 synchronized로 해결했는데요.
      그....오라클에서 시퀀스를 사용하면 된다고 하네요^^
      저도 안 써봐서 모르겠네요 ㅠ

  4. BlogIcon yeori 2009.07.13 16:20  댓글주소  수정/삭제  댓글쓰기

    흠... 제가 지금 저것때문에 좀 골치가 아픈데... 저는 스프링 거치지 않고 iBATIS만 사용중인데 unique constraint를 침범하는 상황인데도 iBATIS 자체에서 "ORA-00001: 무결성 제약 조건" 에러를 던지지 않고 bean 클래스의 프로퍼티를 설정하려다가 null poiner exception만 던지고 있어서 iBATIS 코드를 보는 중이네요. -_-

    일단 예외 발생하면 똑같은 데이터가 존재한다고 가정하고 select 로 시도해서 해결중인데 좀 깔끔하지가 못하죠... 고민입니다..

    • 머드초보 2009.07.19 16:14  댓글주소  수정/삭제

      안녕하세요~
      사실 시퀀스를 걸어서 개발하는 게 맞는건데, 어쩔 수 없는 상황도 있죠. 저도 저 당시 고민을 좀 했었는데-_- 그냥 뭐 느리더라도 sync를 써서 했던 기억이^^
      암튼, 방문해주셔서 감사해요~ ^^

  5. BlogIcon huikyun 2009.12.16 14:49  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 같은 문제로 고민하다가 유익한 글 잘 읽었습니다.
    저는 성능이 중시되는 상황에서 Insert문만 딱 한가지여서 무결성 예외가 뜨는걸 그냥 로그로 찍기만 하고 그냥 두었습니다.
    synchronized 를 이용하려고 하니 저보다도 다른 개발자가 오히려 ㅎㄷㄷ 해버리네요...

    • 머드초보 2009.12.17 09:55  댓글주소  수정/삭제

      그렇군요-_-
      성능이 우선시 된다면야....시퀀스를....-_-

    • BlogIcon huikyun 2009.12.17 11:56  댓글주소  수정/삭제

      아. 모 대기업의 개발건이라서
      데이터베이스에 스키마는 정해진대로 가야합니다.
      혹시 다음에 기회가 된다면 꼭 시퀀스 사용을 고려해보도록 하겠습니다.^^;

    • 머드초보 2009.12.20 13:55  댓글주소  수정/삭제

      아 그렇군요^^
      방문해주셔서 감사해요~ ^^

 
사실 VARCHAR2타입에서 최대값을 뽑는거 자체가 바보같은 짓이지만-_-;
VARCHAR2타입은 말그대로 CHAR타입입니다. 그래서 문자열만 들어가야합니다.
근데, 이건뭐 설계를 다 VARCHAR으로 때려박아서 SEQ부분을 VARCHAR로 해놨더군요.
그래서 시퀀스하게 숫자를 관리하겠다고 얘기를 하더군요.

SEQ칼럼이 999가 최대값이면 INSERT할 때마다 1씩 증가해서 다음에 인서트를 할 때 1000가 되게 하라-_-;
이건 Number타입으로 AutoIncrement로 할 수 있고, Number타입이면 그냥 max함수써서 하면되는데-_-;

어쨌든, VARCHAR타입을 숫자로 바꿔줘야합니다.
TO_NUMBER라는 함수가 있습니다.

"ID",CHARNUMBER,GROUPID
2,10000,1
1,9999,1
3,10001,1
요런 데이터가 있다고 칩시다.
그럼,
select max(to_number(charnumber)) from chartonumber group by groupid
요렇게 때려주시면, MAX값을 추출합니다.

저게 보니까 ASCII 순서대로 하는건지는 잘 모르겠지만, 'MUDCHOBO'라는 문자를 넣어버리면, 쿼리가 안날려집니다-_-; ORA-01722: 수치가 부적합합니다 라는 에러가 뜨네요.

자.....이제 DB설계자를 막 욕합시다-_-;

 
Posted by 머드초보

댓글을 달아 주세요

  1. BlogIcon 공대남 2012.01.30 18:54  댓글주소  수정/삭제  댓글쓰기

    도움 받고 갑니다. :)

 

저번달에 JCO놀러갔다가 알티베이스라는 국산DBMS를 알게 되었습니다. 그래서 한번 사용해볼까 라는 생각을 가지고 있었는데 시간이 없어서 그냥 있다가 갑자기 생각나서 설치를 해봤습니다.

Windows용은 설치가 완전 쉽습니다-_-; installer를 제공해서 걍 설치를 하면 알아서 환경변수도 등록해주고, 알아서 다 해주네요. 나중에 Linux나 Unix용으로도 한번 해봐야겠습니다.

우선 altibase홈페이지에서 Windows용으로 설치파일을 받습니다. 회원가입을 해야합니다.
http://adc.altibase.com/
여기서 Product -> Package클릭 후 WINDOWS용으로 클릭하면 나옵니다.
32BIT이면 32BIT용으로 받고, 64BIT이면 B4BIT용으로 받아야합니다.
저는 이걸로 altibase-WIN_NT_5.0-32bit-4.3.9.75-release-VC6.exe

실행하면 설치화면이 나오고 다음신공으로 설치를 합니다. 설치가 완료되면 환경변수를 등록해야하는데
자동으로 환경변수를 등록하게 됩니다.
ALTIBASE_HOME이라는 환경변수를 등록하고 해당 BIN디렉토리를 PATH로 잡아주더군요.

라이센스를 받아야합니다.
홈페이지에서 Member -> Download Info를 클릭하면(http://adc.altibase.com/member/down_info.jsp)
자기가 뭘 다운받았는지 다 나옵니다. 오른쪽에 보시면 라이센스신청클릭해서 신청하면 됩니다.
여기서 Windows에서는 Mac Address를 입력 잘해야합니다.
잘못 입력했더니 Internal Error : Invalid Protocol이라면서 안되더군요.
참고로 ipconfig/all해서 나오는 것 중에 랜카드가 여러개 있으면 가장 위에 것을 입력해야하는 것 같습니다.
가장 위에 것을 입력했더니 되더군요 ^^

라이센스가 메일로 날아오는데 이 license라는 파일을 ALTIBASE_HOME/conf 디렉토리에 복사하면됩니다.
그다음 이렇게 하면 됩니다.


C:\Documents and Settings\Owner>isql -u sys -p manager -sysdba
-----------------------------------------------------------------
     Altibase Client Query utility.
     Release Version 4.3.9.75
     Copyright 2000, ALTIBASE Corporation or its subsidiaries.
     All Rights Reserved.
-----------------------------------------------------------------
ISQL_CONNECTION = TCP, SERVER = 127.0.0.1, PORT_NO = 20300
iSQL(sysdba)> startup process;
Trying connect to database server. Connected with database server.

TRANSITION TO PHASE : PROCESS
Command execute success.
iSQL(sysdba)> create database mydb initsize=10M noarchivelog;
DB Info (Page Size     = 32768)
        (Page Count    = 385)
        (Total DB Size = 12615680)
        (DB File Size  = 1073741824)
        Creating MMDB FILES     [SUCCESS]
        Creating Catalog Tables [SUCCESS]
        Creating DRDB FILES     [SUCCESS]
  [SM] Rebuilding Indices [Total Count:0] **** [SUCCESS]
DB Writing Completed. All Done.
Create success.
iSQL(sysdba)> shutdown abort;
Database server killed..
iSQL(sysdba)> startup service;
Trying connect to database server. Connected with database server.

TRANSITION TO PHASE : PROCESS

TRANSITION TO PHASE : CONTROL

TRANSITION TO PHASE : META
  [SM] Recovery Phase - 1 : Preparing Database
  [SM] Recovery Phase - 2 : Loading Database
                          : Dynamic Memory Version => Loading Type-2
  [SM] Recovery Phase - 3 : Skipping Recovery & Starting Threads...
                            Refining Disk Table
  [SM] Refine Memory Table : ...................................................
... [SUCCESS]
  [SM] Rebuilding Indices [Total Count:64] ****.................................
................................................................................
............... [SUCCESS]

TRANSITION TO PHASE : SERVICE
--- STARTUP Process SUCCESS ---
Command execute success.
iSQL(sysdba)> exit
C:\Documents and Settings\Owner>

근데 그냥 위처럼 안하고 clean이라고 쳐도 돼요-_-;
그리고 나중에 서버구동할 때에는 그냥 isql접속 안하고 server start 라고 치면 돼요.
종료할 때는 server stop이라고 치면 되구요 ^^
어쨌든 이제 됐는지 확인해봅시다.

이클립스(eclipse)에서 제공하는 Data Source Explorer를 이용해서 접속해봅시다.
우선 Altibase용 JDBC가 필요하겠죠? 이것도 ALTIBASE홈페이지에서 받도록 합시다.
Product -> Environment에 있습니다. http://adc.altibase.com/product/environment.jsp
다운로드받고 Data Source Explorer 설정을 해봅시다.

오른쪽 아래에 Data Source Explorer가 안보인다면 Windows -> Other -> Data Source Explorer 확인 ^^

Databases대고 오른쪽버튼을 누르고 New클릭
Generic JDBC Connection 클릭
Name은 대충 AltibaseTest라고 써놓고 Next
Select a driver from the drop-down에서 ...을 클릭해서 Add
Generic JDBC Driver선택, Add Jar/ZIP클릭 후 다운받은 Altibase.jar 선택
Driver Name은 Altibase Driver라고 바꿈.
아래부분에 Driver Class를 ...클릭해서 Altibase.jdbc.driver.AltibaseDriver 선택
방금 추가한 Altibase Driver를 선택
Database는 mydb, URL은 jdbc:Altibase://127.0.0.1:20300/mydb
User name은 sys
Password는 manager
Finish후에 AlitibaseTest오른쪽버튼 대고 Connect!
접속 되면 된겁니다 ^^ 아마도-_-;

이제 설치밖에 안해봤는데 사용자계정 추가하고, 뭐 이것저것 알아볼라면 또 삽질해야하네요 ^^
어쨌든 국산인데 이런 훌륭한 DBMS가 있는 줄 몰랐네요. 지켜봐야겠네요^^

 
Posted by 머드초보

댓글을 달아 주세요

  1. 알티인 2008.03.21 09:39  댓글주소  수정/삭제  댓글쓰기

    안녕하세요..이것저것 검색하다가 님의 블로그에 방문하게 되었네요..
    참.. 제 소개가 늦었네요.. 전.. 알티베이스 직원입니다.. JCO 행사에도 참가했던...^^
    관심 가져 주셔서 감사하고.. 직접 설치까지 해 봐주셔서 더 감사해서 저도 모르게 그만.. 글을 올립니다..^^
    앞으로 더 많은 관심 보여주시구요.. 좋은 제품 개발에 더욱 매진하겠습니다...(제가 많이 좀 딱딱하네요..)
    사용하시면서 궁금한 점은 ADC 게시판에 올려 주시거나 support@altibase.com으로 메일 주시면 바로 처리되니깐.. 우리 지원팀과 개발자들 많이 좀 괴롭혀 주세요~~~^^ 피가 되고 살이 되지 않을까요?

    • 머드초보 2008.03.24 08:25  댓글주소  수정/삭제

      헛 방문해주셔서 감사합니다 ^^
      그냥 설치만 해보고 사용은 안해봤어요^^
      나중에 간단히 예제 테스트를 할 때 사용해보도록 할께요^^
      저는 DB는 4개정도 밖에 없는 줄알았는데 국산도 있을 줄이야 ^^
      어쨌든 더욱 멋진 DB가 되었으면 좋겠네요 ^^

  2. 강혀기 2010.04.08 01:30  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.... 저도 이것 저것 검색하다가 님 블로그에 오게됬네요 후후
    혹시 은 티베이스 4의 아쉬점이 있다면 뭐가 있다고 생각하시나요?? 또 그에 따른 해결 방안이 있다면
    어떤것이 있을까요?

    • 머드초보 2010.04.09 10:34  댓글주소  수정/삭제

      안녕하세요~
      제가 db전공이 아니다보니-_-
      알티베이스를 많이 써보진 않아서 ㅠㅠ
      근데 데이터베이스는 고객들이 원하는 것은 정말 안정적이게 돌아가는 것 하나만 보고 많이들 선택하죠.
      저희는 mysql을 쓰고 있는데....이게 툭하면 죽어버려서-_-
      근데 제생각인데....설치가 좀 어려운 것 같은....-_-

    • BlogIcon 머드초보 2011.12.02 17:44 신고  댓글주소  수정/삭제

      아마 상업적으로 쓰시면 라이센스를 지불해야하지 않을까요? ㄷㄷ 저도 정확하게 모르겠네요ㅠ
      홈페이지가서 문의해보시는 게^^