Selasa, 23 November 2010

Spring : How to Declarative Transaction

Dalam tulisan ini kita akan mengekspose Spring declarative transaction dengan menggunakan fitur anotation (hanya support di java 1.5 dan seterusnya...)
dan penggunaan SimpleJdbcInsert.


Berikut contoh penggunaan :

BookManagerImpl merupakan class yang mengatur proses DML pada table 'book' pada database 'spring_book'. Object BookManagerImpl akan dibungkus dengan Transaction pada mode 'read-only' secara default. Method insertBooks(List books) akan dioverride dengan mode transaction yang berbeda sehingga setiap kali method ini dipanggil maka akan dibungkus dengan sebuah transaksi yang baru.


CREATE DATABASE spring_book;

CREATE TABLE `book` ( 
 `id`   varchar(10) NOT NULL,
 `name` varchar(50) NOT NULL,
 PRIMARY KEY(`id`)
);
package greenhornsjava.spring.entity;

public class Book {
 private String id;
 private String name;
 
 public Book(String id, String name) {
  this.id = id;
  this.name = name;
 }
 
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 } 
}


package greenhornsjava.spring.trx;

import java.util.List;

public interface BookManager {
 public void insertBooks(List books);
}
package greenhornsjava.spring.trx;

import greenhornsjava.spring.entity.Book;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * BookManagerImpl adalah contoh class yang mengimplementasikan
 * Kemampuan Spring dalam mengatur transaksi secara declarative.
 */
@Transactional(readOnly = true)
public class BookManagerImpl implements BookManager {
 // SimpleJdbcInsert adalah Object JDBC Template yang disediakan oleh Spring Framework 
 private SimpleJdbcInsert insertBook;
 
 public BookManagerImpl(DataSource dataSource) {
  insertBook = new SimpleJdbcInsert(dataSource).withTableName("book");
 }
 
 /**
  * Secara declarative menjadikan method ini 'terbungkus' oleh transaksi baru. 
  * Sehingga jika terjadi Exception maka keseluruhan proses insert pada method ini
  * akan di-rollback.
  */
 @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
 public void insertBooks(List books) {
  for(Object o : books) {
   Book b = (Book) o;
   Map parameters = new HashMap();
   parameters.put("id", b.getId());
   parameters.put("Name", b.getName());
   insertBook.execute(parameters);
  }
 }
 
 public static void main(String[] args) {  
  AbstractApplicationContext context = new FileSystemXmlApplicationContext(
    new String[] { "DataAccessContext.xml" });

  context.registerShutdownHook();

  final BeanFactory bf = (BeanFactory) context;
  BookManager bookManager = (BookManager) bf.getBean("bookManager");
  
  Book b1 = new Book("book1", "Harry Potter");
  Book b2 = new Book("book2", "The Lost Symbol");
  Book b3 = new Book("book3", "The Adv of Donald Duck");
  
  List books = new ArrayList();
  books.add(b1);
  books.add(b2);
  books.add(b3);
  
  // Proses Insert Book akan berhasil dan tercatat secara fisik di DB
  bookManager.insertBooks(books);
  
  Book b4 = new Book("Book4", "The Destination");
  
  List books2 = new ArrayList();
  books2.add(b4);
  books2.add(b1);
  
  // Proses Insert Book akan gagal dan Book dengan id:Book4 tidak akan tersimpan
  // karena method telah 'terbungkus' transaksi
  bookManager.insertBooks(books2);
 }
}

<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"
    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
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
    
    <bean id="bookManager" class="greenhornsjava.spring.trx.BookManagerImpl">
        <constructor-arg ref="dataSource" />
    </bean>
    
    <!--  Bagian ini akan memberitahukan container agar men-scan semua
          object/method yang ber-anotasi @Transactional agar transaksinya dimanage
          oleh Spring -->
    <tx:annotation-driven transaction-manager="txManager"/>
    
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/spring_book" />
        <property name="username" value="root" />
        <property name="password" value="keriting" />
    </bean>
    
    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

Catatan : Object yang akan menggunakan Spring Declarative Transaction HARUS mengimplement sebuah Interface dimana method2 yang dimaksudkan agar 'dibungkus' dengan transaksi dapat dimanage oleh Spring.

Tidak ada komentar:

Posting Komentar