해당 메소드에서 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는 빼구요 ^^
'스프링'에 해당되는 글 35건
- 2008.04.24 [Springframework] 트랜젝션 롤백할 때 주의할 점! 2
- 2008.04.24 [SpringFramework] 트랜젝션(Transaction) MySQL과 Oracle 적용 삽질 후기2
- 2008.04.24 [SpringFramework] 트랜젝션(Transaction) MySQL과 Oracle 적용 삽질 후기1
- 2008.04.18 [Springframework] 어노테이션(annotation)을 이용한 초간단 AOP예제-_-;
- 2008.04.15 [Springframework] Properties 스프링어플리케이션에서 사용해봅시다.
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로 해버릴 수 있더라구요.
뭐 그냥 그렇다구요-_-;
스프링에서 제공하는 트랜젝션을 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 여기에 계속-_-;
정말 초간단예제군요-_-;
어노테이션을 사용하면 굉장히 편해져요.
저번주 스터디시간에 한 내용을-_-;
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가 적용되어 호출됩니다.
Apple : 맛있다
after : println
PS.회사프로그램에 스프링을 이용해서 AOP를 적용해보았는데요. 코드가 깔끔해지고, 참 좋은 것 같아요.
회사프로그램에서 예외처리하는 부분이 굉장히 많았는데 이것을 따로 빼서 하나로 합치니-_-; 코드가독성이 올라가네요. 암튼, 좀 더 배워봐야겠네요.
보통 어플리케이션에서 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에서 할 수 있을 것 같은데-_-;