Full parsing of Java Transaction Processing (7) -- using Transactional Annotation (Annotation) Like Spring)

Source: Internet
Author: User

In the previous article in this series, we talked about the use of dynamic proxy to complete transaction processing. In this way, all the public methods at the service layer are added to the transaction, this is obviously not what we need. What we need to proxy is the methods that need to operate the database. In this article, we will talk about how to use the Java Annotation (Annotation) to mark the methods that require transaction processing.

 

This is a series of articles about Java transaction processing. Please download the github source code in the following ways:

Git clone https://github.com/davenkin/java_transaction_workshop.git

 

First define the Transactional annotation:

package davenkin.step6_annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Transactional{}

 

The basic principle of marking transactions with annotations is: still using the dynamic proxy method described in the previous article, only in the InvocationHandler's invoke method, first determine whether the method to be represented is marked with the Transactional annotation, if not, call method directly. invoke (proxied, objects). Otherwise, prepare the transaction before calling method. invoke (proxied, objects), and then call commit or rollback based on whether the method is successfully executed. Define TransactionEnabledAnnotationProxyManager as follows:

package davenkin.step6_annotation;import davenkin.step3_connection_holder.TransactionManager;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class TransactionEnabledAnnotationProxyManager{    private TransactionManager transactionManager;    public TransactionEnabledAnnotationProxyManager(TransactionManager transactionManager)    {        this.transactionManager = transactionManager;    }    public Object proxyFor(Object object)    {        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new AnnotationTransactionInvocationHandler(object, transactionManager));    }}class AnnotationTransactionInvocationHandler implements InvocationHandler{    private Object proxied;    private TransactionManager transactionManager;    AnnotationTransactionInvocationHandler(Object object, TransactionManager transactionManager)    {        this.proxied = object;        this.transactionManager = transactionManager;    }    public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable    {        Method originalMethod = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());        if (!originalMethod.isAnnotationPresent(Transactional.class))        {            return method.invoke(proxied, objects);        }        transactionManager.start();        Object result = null;        try        {            result = method.invoke(proxied, objects);            transactionManager.commit();        } catch (Exception e)        {            transactionManager.rollback();        } finally        {            transactionManager.close();        }        return result;    }}

 

As you can see, in the invoke method of AnnotationTransactionInvocationHandler, we first obtain the transfer method of the original service, and then according to originalMethod. isAnnotationPresent (Transactional. class) determines whether the method is marked with a Transactional annotation. If no, the system directly calls the transfer method of the original service without any additional functions. Otherwise, it is added to the transaction processing.

 

In the service layer, we only need to mark the methods that need to be added to the transaction processing with the Transactional annotation:

package davenkin.step6_annotation;import davenkin.BankService;import davenkin.step3_connection_holder.ConnectionHolderBankDao;import davenkin.step3_connection_holder.ConnectionHolderInsuranceDao;import javax.sql.DataSource;public class AnnotationBankService implements BankService{    private ConnectionHolderBankDao connectionHolderBankDao;    private ConnectionHolderInsuranceDao connectionHolderInsuranceDao;    public AnnotationBankService(DataSource dataSource)    {        connectionHolderBankDao = new ConnectionHolderBankDao(dataSource);        connectionHolderInsuranceDao = new ConnectionHolderInsuranceDao(dataSource);    }    @Transactional    public void transfer(final int fromId, final int toId, final int amount)    {        try        {            connectionHolderBankDao.withdraw(fromId, amount);            connectionHolderInsuranceDao.deposit(toId, amount);        } catch (Exception e)        {            throw new RuntimeException();        }    }}

 

Then perform the test:

    @Test    public void transferFailure() throws SQLException    {        TransactionEnabledAnnotationProxyManager transactionEnabledAnnotationProxyManager = new TransactionEnabledAnnotationProxyManager(new TransactionManager(dataSource));        BankService bankService = new AnnotationBankService(dataSource);        BankService proxyBankService = (BankService) transactionEnabledAnnotationProxyManager.proxyFor(bankService);        int toNonExistId = 3333;        proxyBankService.transfer(1111, toNonExistId, 200);        assertEquals(1000, getBankAmount(1111));        assertEquals(1000, getInsuranceAmount(2222));    }

 

The test runs successfully. If you delete the Transactional annotation of the transfer method in AnnotationBankService, the above test will throw a RuntimeException, Which is thrown by us in the transfer method, that is, because there is no transaction to capture exceptions at this time, the program will directly throw the exception and terminate the operation. In the next article (the last article in this series), we will introduce an example of a distributed transaction.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.