หน้าเว็บ

วันพุธที่ 4 ธันวาคม พ.ศ. 2556

manage transaction ใน service spring เอง

        ปกติเวลาเราเขียน service ของ spring ขึ้นมา  เราจะใช้ AOP (Aspect Oriented Programming) ช่วยในการ manage transaction ให้  เพียงแค่ใส่ annotation @Transactional  (org.springframework.transaction.annotation.Transactional) ไว้บน class หรือ method ที่ต้องการ  เมื่อ spring ทำ reflection มาเจอ มันก็จะเช็คว่า method ไหนต้องการให้มี transaction บ้าง มันก็จะ begin / commit transaction ให้  โดยผ่านทาง proxy method  แล้ว proxy method ก็ค่อยไปเรียก method จริงๆ อีกที  การทำแบบนี้ถือว่าสะดวกมาก  เพราะเราไม่ต้อง manage transaction เอง (spring จัดการให้แล้ว)  ซึ่งก็ช่วยลด code ที่ซ้ำซ้อนและไม่สำคัญทาง business logic ออกไปได้ในระดับหนึ่งครับ

        แต่การใช้ AOP มันมีเงื่อนไขอยู่ว่า service instance นั้นจะต้องถูกสร้างขึ้นมาจาก  spring เท่านั้น (เราจะ new instance เองไม่ได้) เพราะ spring จะต้องเอาไปครอบด้วย proxy method (ทำ AOP) อีกทีครับ 

        จากเงื่อนไขการใช้ AOP  ข้อนี้ทำให้มีบางเหตุการณ์ที่เราไม่สามารถใช้ transaction จาก AOP ได้  เช่น  การเรียกใช้งาน method นั้นๆ โดยตรง โดยไม่ผ่าน proxy method  ประมาณนี้

MyService.java
...
...
...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class MyServiceImpl implements MyService{

    @Override 
    public void methodA(){
        for(int i=0; i<10 ;i ++){
            methodB();
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB(){

    }
 }
จาก code ข้างบน methodA() ทำการเรียกใช้งาน methodB() 10 ครั้ง
ทุกครั้งที่เรียกใช้งาน methodB() ต้องการให้ new transaction ใหม่เสมอ

คำถาม  methodB() ถูก new transaction ขึ้นมาใหม่หรือไม่
คำตอบ  ไม่ครับ เพราะ methodA() เรียกใช้งาน methodB() โดยตรง โดยไม่ผ่าน proxy method (แต่ถ้าผ่าน proxy method  methodB() จะถูก new transaction ขึ้นมาใหม่เสมอ เนื่องจากมี annotation @Transactional(propagation = Propagation.REQUIRES_NEW) กำกับอยู่)

คำถาม  อ้าว  ถ้าเรียกตรงๆ แล้ว new transaction ไม่ได้  แล้วเราจะทำยังไงล่ะ?
คำตอบ เราก็ต้อง manage transaction เองยังไงล่ะครับ  ดังนี้

MyService.java
...
...
...
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class MyServiceImpl implements MyService{

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Override 
    public void methodA(){
        TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

        for(int i=0; i<10 ;i ++){
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    try {
                        methodB(); //****
                    } catch (Exception ex) {
                        status.setRollbackOnly();
                    }
                }
            });
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB(){

    }
 }
แค่นี้ เราก็สามารถ new transaction ให้ methodB() ใหม่ทุกครั้ง โดยเรียกใช้งานตรงๆ จาก methodA() ได้แล้วครับ

ซึ่งการ new transaction จะเกิดขึ้นเมื่อมีการเรียก transactionTemplate.execute() และเซต transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

reference : http://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch11s06.html

ไม่มีความคิดเห็น:

แสดงความคิดเห็น