그래서 이 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를 붙입니다.
이건 어떤 스레드가 호출을 했으면 다음 스레드는 기다린 다음에 접근하게 되는 즉 싱크방식으로 처리를 시킬 수 있는 메소드가 됩니다.
이렇게 처리하면 효율은 좀 떨어지겠지만, 유니크 중복에러는 그나마 막을 수 있습니다.
다른 방법 있으면 좀 알려주세요 ㅠㅠ 고생하고 있습니다 ㅠ