스프링에서 제공하는 트랜젝션을 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 여기에 계속-_-;
'SpringFramework'에 해당되는 글 19건
- 2008.04.24 [SpringFramework] 트랜젝션(Transaction) MySQL과 Oracle 적용 삽질 후기1
- 2008.04.18 [Springframework] 어노테이션(annotation)을 이용한 초간단 AOP예제-_-;
- 2008.04.15 [Springframework] Properties 스프링어플리케이션에서 사용해봅시다.
- 2008.03.20 스프링프레임워크(SpringFramework)와 플렉스(BlazeDS)를 연동한 초간단예제4(클라이언트단구현) 6
- 2008.03.20 스프링프레임워크(SpringFramework)와 플렉스(BlazeDS)를 연동한 초간단예제3(서버단구현2) 4
정말 초간단예제군요-_-;
어노테이션을 사용하면 굉장히 편해져요.
저번주 스터디시간에 한 내용을-_-;
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에서 할 수 있을 것 같은데-_-;
우선 service-config.xml파일을 수정해야합니다.
<factories>
<factory id="springfactory" class="flex.messaging.factory.SpringFactory" />
</factories>
를 추가합니다.
그 다음 remote-config.xml파일을 수정해야합니다.
<destination id="productmanager">
<properties>
<factory>springfactory</factory>
<source>productManager</source>
</properties>
</destination>
자세히 보시면 factory는 위에 service-config.xml파일에 정의한 놈이고, source는 bean이름입니다.
즉 applicationContext.xml파일에 정의한 그 bean이름을 저기에 적어 놓으면 됩니다.
그러면 그 bean을 flex로 가져와서 쓸 수 있습니다.
아 그리고 프로젝트에서 이상하게 contextroot가 WebContent로 되어있는데 프로젝트이름으로 고쳐줍시다-_-;
프로젝트 이름에 대고 마우스오른쪽버튼(alt+enter) properties를 선택, Flex Server부분 클릭.
context root를 프로젝트이름(SpringAndBlazeds)으로 바꿔줍시다.
자 그러면 flex_src에 있는 SpringAndBlazeds.mxml을 수정해봅시다.
SpringAndBlazeds.mxml
[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
applicationComplete="init();">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;
private function init():void {
var remoteObject:RemoteObject = new RemoteObject();
remoteObject.destination = "productmanager";
remoteObject.addEventListener(ResultEvent.RESULT, resultHandler);
remoteObject.addEventListener(FaultEvent.FAULT, faultHandler);
remoteObject.getProducts();
}
private function resultHandler(event:ResultEvent):void{
dg.dataProvider = event.result;
}
private function faultHandler(event:FaultEvent):void{
Alert.show("실패 메세지 : " + event.fault.message);
}
]]>
</mx:Script>
<mx:DataGrid id="dg" width="100%" height="100%" />
</mx:Application>
[/code]
간단하게 Manager에 있는 getProducts를 호출해서 DataGrid에 넣는 코드입니다.
자 이제 실행해봅시다-_-;
이클립스 오른쪽아래에 server에다가 SpringAndBlazeds프로젝트를 추가합니다.
서버에 대고, 오른쪽버튼누르면, Add and Remove Project클릭해서 추가하면 됩니다.
서버를 가동합니다.
Run Flex Application을 실행해봅시다!-_-;
아....잘되....는.....군.....요......-_-;
이제 Manager클래스를 만들어봅시다.
실제로 BlazeDS를 이용해서 가져오는 놈은 이 Manager클래스가 되겠죠^^
Java Resources: src에서 오른쪽버튼 클릭하고, New를 해서 interface를 구현합니다.
[code]
package springapp.service;
import java.util.List;
import springapp.domain.Product;
public interface ProductManager {
public List<Product> getProducts();
}
[/code]
getProducts라는 메소드가 하나 있군요! 구현해봅시다!!!
[code]
package springapp.service;
import java.util.List;
import springapp.dao.ProductDao;
import springapp.domain.Product;
public class ProductManagerImpl implements ProductManager {
private ProductDao productDao;
@Override
public List<Product> getProducts() {
return productDao.getProductList();
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
}
[/code]
getProducts라는 함수는 Dao에서 getProductList를 호출하는 놈이네요.
요 아래에는 setter가 있네요. 이건 spring에서 DI를 하기위해 존재하는 setter입니다^^
서비스도 완성이 되었네요! 이제 설정파일을 작성해봅시다.
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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- Enable @Transactional support -->
<tx:annotation-driven />
<!-- Enable @AspectJ support -->
<aop:aspectj-autoproxy />
<aop:config>
<aop:advisor pointcut="execution(* *..ProductManager.*(..))"
advice-ref="txAdvice" />
</aop:config>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="save*" />
<tx:method name="get*" read-only="true" />
</tx:attributes>
</tx:advice>
<bean id="productManager"
class="springapp.service.ProductManagerImpl">
<property name="productDao" ref="productDao" />
</bean>
</beans>
[/code]
Dao부분의 설정파일인 applicationContext-ibatis.xml파일을 봅시다.
applicationContext-ibatis.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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:properties/jdbc.properties" />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"
p:username="${jdbc.username}" p:password="${jdbc.password}" />
<!-- Transaction manager for iBATIS Daos -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- SqlMap setup for iBATIS Database Layer -->
<bean id="sqlMapClient"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation"
value="classpath:springapp/dao/SqlMapConfig.xml" />
</bean>
<!-- Add additional Dao definitions here -->
<bean id="productDao"
class="springapp.dao.ProductDaoImpl">
<property name="sqlMapClient" ref="sqlMapClient" />
</bean>
</beans>
[/code]
jdbc.properties파일은 properties라는 package를 만들고, jdbc.properties파일을 넣어버립시다.
[code]
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://DB주소
jdbc.username=DB아이디
jdbc.password=DB비밀번호
[/code]
이 설정들의 bean들의 관계를 보고 싶다면-_-;
Spring Elements에서 오른쪽버튼 클릭하고 properties를 선택.
Bean Support를 선택, Add한 뒤 두개의 설정파일(applicationContext.xml, applicationContext-ibatis.xml)선택.
Config set에서 New하고 Name에 applicationContext라고 하고, 두개다 체크 오케이~
그럼 이제 스프링 설정파일에서 에러를 찾아낼 수 있어요!
bean들의 관계를 그래프로도 볼 수 있네요!
Config set에 추가한 applicationContext에다가 마우스오른쪽버튼을 클릭하면 open graph로 볼 수 있어요!
이제 클라이언트 구현으로....다음 시간에-_-;