반응형

Spring Declarative Transaction Management


(원문 위치 : http://www.tutorialspoint.com/spring/declarative_management.htm )

선언적 트랜젝션 관리는 소스코드에서 hard coding대신 설정으로 트랜젝션을 관리하는 것을 허용한다. 이는 비즈니스코드에서 트랜젝션관리를 분리할 수 있음을 의미한다. 트랜젝션을 관리하기 위해 XML기반 설정 또는 annotation을 사용하면 된다. Bean 설정은 함수가 transaction이 되도록 할 것이다.(The bean configuration will spectify the method to be transactional.). 아래는 선언적 트랜젝션과 관련된 단계이다.
  • We use <tx:advice /> tag, which creates a transaction-handling advice and same time we define a pointcut that matches all methods we wish to make transactional and reference the transactional advice. (트랜젝션 핸들링 advice를 생성하는 <tx:advice/>태그를 사용하면서 동시에 트랜젝션으로 만들고 트랜젝션 advice를 참조하기를 바라는 모든 함수에 대응하는 pointcut을 정의한다.)

  • If a method name has been included in the transactional configuration then created advice will begin the transaction before calling the method. (만약 함수이름이 트랜젝션 설정에 포함되면 생성되어지는 advice는 함수를 호출하기전에 트랜젝션을 시작할 것이다.)

  • Target method will be executed in a try / catch block. (타겟 함수는 try/catch 블록 에서 실행되어 질 것이다.)

  • If the method finishes normally, the AOP advice commits the transaction successfully otherwise it performs a rollback. (일반적으로 함수가 종료되면, AOP advice는 성공적으로 트랜젝션을 commit한다. 아니면 rollback을 수행한다.)

위에 언급한 단계가 어떻게 동작하는지 보자. 그전에 트랜젝션에 사용할 다양한 CRUD를 수해할 수 있는 적어도 두개의 database table이 있어야 한다. 아래의 DDL(Data Definition Language)를 이용하여 Student와 Marks table을 만들자. Marks table의 SID는 Student table의 foreign key이다.

CREATE TABLE Student( ID INT NOT NULL AUTO_INCREMENT, NAME VARCHAR(20) NOT NULL, AGE INT NOT NULL, PRIMARY KEY (ID) );

CREATE TABLE Marks(
   SID INT NOT NULL,
   MARKS  INT NOT NULL,
   YEAR   INT NOT NULL
);

(--> 현재 환경으로 구성된 mariaDB상에서 위 테이블을 구성하는 단계는 아래와 같다.)
CREATE TABLE Student(
   ID   INT NOT NULL AUTO_INCREMENT,
   NAME VARCHAR(20) NOT NULL,
   AGE  INT NOT NULL,
   PRIMARY KEY (ID)
);

CREATE TABLE Marks(
   SID INT NOT NULL,
   MARKS  INT NOT NULL,
   YEAR   INT NOT NULL,
   FOREIGN KEY(SID) REFERENCES Student(ID)
);


[003] HelloWorld Example 강좌를 참고하여 프로젝트를 생성한다.


POM.xml아래의 의존관계를 추가한다.

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>3.2.4.RELEASE</version>

</dependency>


<dependency>

<groupId>org.mariadb.jdbc</groupId>

<artifactId>mariadb-java-client</artifactId>

<version>1.1.7</version>

</dependency>

<!-- Spring AOP + AspectJ -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-aop</artifactId>

<version>${spring-framework.version}</version>

</dependency>


<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>1.6.11</version>

</dependency>

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>1.6.11</version>

</dependency>

StudentMarks.java:

package com.tutorialspoint;

public class StudentMarks {
   private Integer age;
   private String name;
   private Integer id;
   private Integer marks;
   private Integer year;
   private Integer sid;

   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
      return age;
   }

   public void setName(String name) {
      this.name = name;
   }
   public String getName() {
      return name;
   }

   public void setId(Integer id) {
      this.id = id;
   }
   public Integer getId() {
      return id;
   }
   public void setMarks(Integer marks) {
      this.marks = marks;
   }
   public Integer getMarks() {
      return marks;
   }

   public void setYear(Integer year) {
      this.year = year;
   }
   public Integer getYear() {
      return year;
   }

   public void setSid(Integer sid) {
      this.sid = sid;
   }
   public Integer getSid() {
      return sid;
   }
}

StudentDAO.java:

package com.tutorialspoint;

import java.util.List;
import javax.sql.DataSource;

public interface StudentDAO {
   /** 
    * This is the method to be used to initialize
    * database resources ie. connection.
    */
   public void setDataSource(DataSource ds);
   /** 
    * This is the method to be used to create
    * a record in the Student and Marks tables.
    */
   public void create(String name, Integer age, Integer marks, Integer year);
   /** 
    * This is the method to be used to list down
    * all the records from the Student and Marks tables.
    */
   public List<StudentMarks> listStudents();
}

StudentMarksMapper.java:

package com.tutorialspoint;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;

public class StudentMarksMapper implements RowMapper<StudentMarks> {
   public StudentMarks mapRow(ResultSet rs, int rowNum) throws SQLException {

      StudentMarks studentMarks = new StudentMarks();

      studentMarks.setId(rs.getInt("id"));
      studentMarks.setName(rs.getString("name"));
      studentMarks.setAge(rs.getInt("age"));
      studentMarks.setSid(rs.getInt("sid"));
      studentMarks.setMarks(rs.getInt("marks"));
      studentMarks.setYear(rs.getInt("year"));

      return studentMarks;
   }
}

StudentJDBCTemplate.java: 어라? 이제보니 spring 버전을 높은것을 써서 그런지, 이전강좌까지 에러를 발생시켰던 update 함수가 그냥 돌아간다.ㅎㅎ 

package com.tutorialspoint;

import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

public class StudentJDBCTemplate implements StudentDAO{
   private JdbcTemplate jdbcTemplateObject;

   public void setDataSource(DataSource dataSource) {
      this.jdbcTemplateObject = new JdbcTemplate(dataSource);
   }

   public void create(String name, Integer age, Integer marks, Integer year){

      try {
         String SQL1 = "insert into Student (name, age) values (?, ?)";
         jdbcTemplateObject.update( SQL1, name, age);

         // Get the latest student id to be used in Marks table
         String SQL2 = "select max(id) from Student";
         int sid = jdbcTemplateObject.queryForInt( SQL2 );

         String SQL3 = "insert into Marks(sid, marks, year) " + 
                       "values (?, ?, ?)";
         jdbcTemplateObject.update( SQL3, sid, marks, year);

         System.out.println("Created Name = " + name + ", Age = " + age);
         // to simulate the exception.
         throw new RuntimeException("simulate Error condition") ;
      } catch (DataAccessException e) {
         System.out.println("Error in creating record, rolling back");
         throw e;
      }
   }

   public List<StudentMarks> listStudents() {
      String SQL = "select * from Student, Marks where Student.id=Marks.sid";

      List <StudentMarks> studentMarks=jdbcTemplateObject.query(SQL, 
      new StudentMarksMapper());
      return studentMarks;
   }
}

MainApp.java:

package com.tutorialspoint;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");

      StudentDAO studentJDBCTemplate = 
      (StudentDAO)context.getBean("studentJDBCTemplate");
      
      System.out.println("------Records creation--------" );
      studentJDBCTemplate.create("Zara", 11, 99, 2010);
      studentJDBCTemplate.create("Nuha", 20, 97, 2010);
      studentJDBCTemplate.create("Ayan", 25, 100, 2011);

      System.out.println("------Listing all the records--------" );
      List<StudentMarks> studentMarks = studentJDBCTemplate.listStudents();
      for (StudentMarks record : studentMarks) {
         System.out.print("ID : " + record.getId() );
         System.out.print(", Name : " + record.getName() );
         System.out.print(", Marks : " + record.getMarks());
         System.out.print(", Year : " + record.getYear());
         System.out.println(", Age : " + record.getAge());
      }
   }
}

Beans.xml:

<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- Initialization for data source --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/TEST"/> <property name="username" value="root"/> <property name="password" value="cohondob"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="create"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="createOperation" expression="execution(* com.tutorialspoint.StudentJDBCTemplate.create(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="createOperation"/> </aop:config> <!-- Initialization for TransactionManager --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- Definition for studentJDBCTemplate bean --> <bean id="studentJDBCTemplate" class="com.tutorialspoint.StudentJDBCTemplate"> <property name="dataSource" ref="dataSource" /> </bean> </beans>

실행 결과는 아래와 같다. (-->이전 강좌와 동일한 흐름을 따르지만 모두 잘 동작했더라도 exception을 발생시켜 -  StudentJDBCTemplate의 catch문 바로 위 - database에는 아무것도 생성되지 않는다. - 모두 rollback 됨.)

------Records creation--------
Created Name = Zara, Age = 11
Exception in thread "main" java.lang.RuntimeException: simulate Error condition

exception을 발생시키는 문장을 제거하고 실행해 보면 database에 레코드가 생성되는 것을 볼 수 있을 것이다. - commit이 수행됨.


(--> 차이를 알겠는가? 
StudentJDBCTemplate와 Beans.xml을 제외하면 프로그램적 트랜잭션 관리 강좌에서 사용했던 소스와 동일한 소스이다. 서두에서 이야기 한것처럼 <tx:advice/> tag를 사용하여 advice를 생성하고, com.tutorialspoint.StudentJDBCTemplate.create()에 pointcut을 정의하였다.
따라서 AOP에 의해 프로그램적 트랜젝션에서는 try/catch문에 포함되어 있던 commit/rollback함수가 제거되었다.
)


반응형

+ Recent posts