해당 메소드에서 catch해버리면 롤백이 안됩니다-_-;

[code]
@Transactional(readOnly=true, rollbackFor={Throwable.class})
 public void insertData() throws Throwable {
  try {
   getSqlMapClientTemplate().insert("insertData");
   System.out.println("oracle에 insert성공");
   throw new IOException();
  } catch (IOException e) {
   System.out.println("io예외발생");
  }
 }
[/code]

여기서 처럼 IOException이 발생했는데 해당 트랜젝션으로 설정한 메소드에서 예외를 catch해버리면 롤백이 안되더라구요-_-;
그 메소드를 호출한 놈한테 가서 해야합니다.
main에서 호출했다고 하면, main에 가서

[code]
try {
   testInsertOracle.insertData();
  } catch (Throwable e) {
   System.out.println("예외발생");
  }
[/code]
요렇게 해줘야 합니다. 메소드에 있는 catch는 빼구요 ^^

 
Posted by 머드초보

댓글을 달아 주세요

  1. BlogIcon THLIFE 2008.06.30 15:23  댓글주소  수정/삭제  댓글쓰기

    본 포스팅을 보고 몇일간의 삽질을 마무리 했습니다. ^_^ 감사합니다!!!

    • 머드초보 2008.06.30 22:12  댓글주소  수정/삭제

      도움이 되셨다니 다행이네요 ^^
      저도 저거 트랜잭션이 롤백이 안되서 삽질했었는데-_-;
      AOP형태로 적용되는거라 그런 것 같아요 ^^
      반가워요~ ^^

 

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

댓글을 달아 주세요

 

스프링에서 제공하는 트랜젝션을 MySQL과 Oracle에다가 적용을 해봤습니다.
제가 잘 이해를 못하는 건지 모르겠는데, Oracle에서는 ReadOnly가 안먹혀요-_-;
MySQL에서는 먹히는데....-_-; Oracle에서는 자체적으로 transaction에서 ReadOnly를 설정하는게 있더군요.
또 Oracle은 트랜젝션기반이라고 하더라구요. 암튼, 참 어렵습니다-_-;
우선 삽질한 코드입니다.
MySQL로 해보았습니다.

필요한 lib입니다.
spring.jar - 스프링할껀데 필요하겠죠?-_-;
commons-logging.jar - 스프링하려면 이눔이 있어야 돼요-_-; 위에 있는 lib와 종속돼요-_-;
ibatis-2.3.0.677.jar - ibatis로 할껍니다. 이게 db연동을 쉽게 해주니까요-_-;
mysql-connector-java-5.1.5-bin.jar - mysql connector가 필요하겠죠!

프로젝트를 만듭시다. TestTransaction이라는 프로젝트를 만듭시다.
최상위 src폴더에다가 applicationContext.xml파일을 만듭시다.

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:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

 <tx:annotation-driven />
 
 <!-- MySQL용 -->
 <bean id="dataSource"
  class="org.springframework.jdbc.datasource.DriverManagerDataSource"
  p:driverClassName="com.mysql.jdbc.Driver"
  p:url="jdbc:mysql://db주소"
  p:username="dbid" p:password="dbpw" />
 
   <!--  Transaction manager for iBATIS Daos -->
 <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
  p:dataSource-ref="dataSource" />
 
 <!-- SqlMap setup for iBATIS Database Layer -->
 <bean id="sqlMapClient"
  class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"
  p:dataSource-ref="dataSource"
  p:configLocation="classpath:SqlMapConfig.xml" />
 
<bean id="insertDataMysql" class="com.mudchobo.TestInsertMysql"
  p:sqlMapClient-ref="sqlMapClient"/>
</beans>
[/code]
TestInsert라는 interface를 작성해봅시다.
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]
인터페이스에다가 트랜젝션을 걸어도 되더라구요-_-; 구현되는 모든놈이 저 옵션으로 트랜젝션이 먹히는 듯 합니다. 근데 별로 안좋은 방법인듯한데-_-; 그냥 메소드에 거는게.....-_-; 보면 readOnly옵션에다가 Throwable예외가 발생하면 롤백합니다.

구현해봅시다.
TestInsertMysql.java
[code]
package com.mudchobo;

import java.io.IOException;

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

public class TestInsertMysql extends SqlMapClientDaoSupport implements
  TestInsert {

 @Override
 public void insertData() throws Throwable {
   getSqlMapClientTemplate().insert("insertData");
   System.out.println("mysql에 insert성공");
   throw new IOException();
 }
}
[/code]
insert를 시키고, 강제로 IOException을 발생시켰습니다.

ibatis xml부분을 봅시다.
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="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">
     insert into TESTDATA (DATA) values ('MUDCHOBO')
    </insert>
</sqlMap>
[/code]
보시면 단순 insert를 하고 있습니다-_-;

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) throws Throwable{
  String[] configLocation = {"applicationContext.xml"};
  ApplicationContext context =
   new ClassPathXmlApplicationContext(configLocation);
 
  TestInsert testInsertMysql =
   (TestInsert) context.getBean("insertData");
  try {
   testInsertMysql.insertData();
  } catch (Throwable e) {
   System.out.println("예외발생");
  }
}
[/code]
이걸 실행하면 트랜젝션이 readOnly이기 때문에 insert하려고 하면 에러가 나버리겠죠.
Connection is read-only. Queries leading to data modification are not allowed; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:
요런 에러를 보실 수 있습니다.

Oracle은 다음 장에서-_-;
http://mudchobo.tomeii.com/tt/258 여기에 계속-_-;

 
Posted by 머드초보

댓글을 달아 주세요

 

정말 초간단예제군요-_-;
어노테이션을 사용하면 굉장히 편해져요.
저번주 스터디시간에 한 내용을-_-;

AOP를 적용하려면 인터페이스가 있어야 돼요.
Apple이라는 인터페이스를 만들어 봅시다.
com.mudchobo.example4라는 패키지에 Apple이라는 interface를 만듭시다.
Apple.java
[code]
package com.mudchobo.example4;

public interface Apple {
 public void println();
}
[/code]
println이라는 메소드가 하나있네요. 구현해봅시다.
AppleImpl.java
[code]
package com.mudchobo.example4;

public class AppleImpl implements Apple {

 @Override
 public void println(){
  System.out.println("Apple : 맛있다");
 }
}
[/code]
사과는 맛있다 라는 메시지를 출력하는군요. 그럼 AOP....(갑자기 두리녀석이 전화를..-_-; 갑자기 2시간남겨놓고 술먹자고 전화하는 이상한놈이네요-_-;)
AOP 클래스를 만들어봅시다.
[code]
package com.mudchobo.example4;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AnnotLoggingAspect {

 @Around("execution(public * com.mudchobo.example4.*.println*(..))")
 public void logging(ProceedingJoinPoint joinPoint) {
  String methodName = joinPoint.getSignature().getName();
 
  try {
   System.out.println("before : " + methodName);
   joinPoint.proceed();
   System.out.println("after : " + methodName);
  } catch (Throwable e) {
   System.out.println("예외가 발생했어요!");
  }
 }
}
[/code]
Around는 before, after, 예외 등을 다 포함하고 있죠. 그냥 메소드하나 구현하는거라고 보면 돼요.
execution은 AspectJ의 정규식을 넣으면 돼요. 문법은 검색 고고싱~--;
저건 맨앞에는 public, private정하는거고, 두번째는 리턴타입(*면 전부다겠죠?), 그다음은 패키지명 쓰고, (..)부분은 파라메터관련된 부분이죠. 검색고고싱~
저건 com.mudchobo.example4패키지에서 모든클래스중에 println으로 시작하는 메소드에 aop를 적용하겠다는 얘기에요. println메소드는 우리가 방금 구현한 Apple인터페이스에 있죠.

스프링 설정파일을 봅시다. 최상위 폴더에 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:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

 <aop:aspectj-autoproxy />
 
 <bean id="apple" class="com.mudchobo.example4.AppleImpl" />

 <bean class="com.mudchobo.example4.AnnotLoggingAspect" />
 
</beans>
[/code]
자 보면, apple이라는 bean은 우리가 만든 사과클래스구요. aop클래스를 bean으로 해서 선언해주었어요.
만약 SpringIDE를 설치했다면 왼쪽에 보시면 aop가 어디에 또는 몇개 적용이 되어있는지 나와요.
그럼 메인클래스를 보도록 해요.

Main4.java
[code]
package com.mudchobo.example4;

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

public class Main4 {

 public static void main(String[] args) {
  String configLocation = "applicationContext.xml";
  ApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
  Apple apple = (Apple) context.getBean("apple");
  apple.println();
 }
}
[/code]
설정파일을 불러와서 ApplicationContext를 만들고, 거기서 apple이라는 bean을 불러옵니다.
그리고, apple.println()메소드를 호출하게 되면 aop가 적용되어 호출됩니다.

before : println
Apple : 맛있다
after : println

PS.회사프로그램에 스프링을 이용해서 AOP를 적용해보았는데요. 코드가 깔끔해지고, 참 좋은 것 같아요.
회사프로그램에서 예외처리하는 부분이 굉장히 많았는데 이것을 따로 빼서 하나로 합치니-_-; 코드가독성이 올라가네요. 암튼, 좀 더 배워봐야겠네요.
 
Posted by 머드초보

댓글을 달아 주세요

 

보통 어플리케이션에서 Properties를 사용하려면 Properties클래스를 사용해서...
Properties properties = new Properties();
properties.load(new FileInputStream("C:/~~~파일명경로");
String mudchobo = properties.getProperty("mudchobo");

이렇게 하면 풀경로를 다 적어줘야지 되더라구요.

이 파일이 클래스패스에 넣어두면 classpath:file.properties 하면 먹혔으면 좋겠는데 안먹히더라구요-_-;
그래서 스프링을 사용하면 클래스패스에 넣어도 할 수 있어요!(아놔 이렇게 하는거 맞나-_-)

properties패키지에 있는 file.properties파일을 불러올겁니다.
mudchobo=dwaeji
hermusseri=meongchungi
2개의 property가 있습니다.

우선 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"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

 <bean id="messageSource"
  class="org.springframework.context.support.ResourceBundleMessageSource"
  p:basename="properties/file" />
 
 <bean id="messageSourceAccessor"
  class="org.springframework.context.support.MessageSourceAccessor">
  <constructor-arg ref="messageSource" />
 </bean>
</beans>
[/code]
잘 보면 messageSource라는 bean이 있는데 이것은 ResourceBundleMessageSource구요.
p:basename은 해당패키지에서 파일을 찾더라구요.
properties라는 패키지에 file.properties파일을 지정했습니다.

그다음 bean은 MessageSourceAccessor인데요. 말그대로 메시지소스를 접근하는 접근자라고 자신을 표현하고 있네요. 이것의 생성자로 해당 messageSource를 받는군요.

애플리케이션에서 봅시다.
[code]
package test;

import java.util.Locale;

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

public class Test {
 
 public static void main(String[] args) {
  String[] configLocation = {"applicationContext.xml"};
  ApplicationContext context = new ClassPathXmlApplicationContext(configLocation);
 
  MessageSourceAccessor msAccessor =
   (MessageSourceAccessor)context.getBean("messageSourceAccessor");
 
  System.out.println(context.getMessage("mudchobo", null, Locale.getDefault()));
  System.out.println(context.getMessage("hermusseri", null, Locale.getDefault()));
  System.out.println(msAccessor.getMessage("mudchobo"));
  System.out.println(msAccessor.getMessage("hermusseri"));
 }
}
[/code]
MessageSourceAccessor를 선언해서 해당 bean을 가져옵니다.
그러면 MessageSourceAccessor를 통해서 Message를 가져올 수 있습니다.
저걸 사용하지 않게 된다면 ApplicationContext인터페이스에서 getMessage를 지원해서 저걸 통해서도 가져올 수 있습니다. 저렇게 가져온다면, 여기저기 다른 클래스에서 사용하게 된다면 context를 해당 클래스로 넘겨야하는데 이건 applicationContext에서 MessageSourceAccessor를 DI해버리면 해당 클래스에서 사용할 수 있겠죠?^^

다시 말하지만... 정말 이것말곤 방법이 없나-_-; 그냥 Properties에서 할 수 있을 것 같은데-_-;

 
Posted by 머드초보

댓글을 달아 주세요