@ Transactional Implementation Principle

Catalog

1. Introduction

2. Custom Annotations

2.1 Definition

2.2 Test

2.3 Summary

3. Notes on Handwritten Transactions

3.1 maven dependence

3.2 Configure spring.xml file

3.3 Customize transaction annotations (by reflecting annotations on parsing methods, if any, execute transaction logic)

3.4 Packaging Programming Transactions

3.5 Encapsulate transaction tool classes via AOP to trigger transactions based on surround notification and exception notification

3.6 dao layer

3.7 service Layer

3.8 Test

1. Introduction

Transactional is a transaction annotation defined in spring, which is added to a method or class to open a transaction. The annotation information of bean s is acquired mainly by reflection, and programmable transactions are encapsulated and implemented by AOP. (spring-5.1.8.RELEASE)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

2. Custom Annotations

2.1 Definition

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation {
    //Properties of custom annotations
    int id() default 0;
    String name() default "Default name";
    String[]arrays();
    String title() default "Default title";
}

2.2 Test

public class App {

    @MyAnnotation(name = "Yongqian Wang", arrays = {"2", "3"})
    public void aMethod() {}

    public void bMethod() {}

    public static void main(String[] args) throws ClassNotFoundException {
        // Reflect the information to the class
        Class<?> clazz = Class.forName("com.wyq.App");

        // Get all methods of the current class (excluding inheritance)
        Method[] methods = clazz.getDeclaredMethods();

        // Traversing the information for each method
        for (Method method : methods) {
            System.out.println("Method name:" + method.getName());

            // Get the annotations above the method
            MyAnnotation annotation = method.getDeclaredAnnotation(MyAnnotation.class);

            if (annotation == null) {
                System.out.println("There is no comment on this method....");
            } else {
                System.out.println("Id : " + annotation.id());
                System.out.println("Name : " + annotation.name());
                System.out.println("Title : " + annotation.title());
                System.out.println("Arrays : " + annotation.arrays());
            }

            System.out.println("--------------------------");
        }

    }
}


//Console information:

//Method name: main
//There is no comment on this method.
--------------------------
//Method Name: aMethod
Id : 0
Name : Yongqian Wang
Title : Default title
Arrays : [Ljava.lang.String;@24d46ca6
--------------------------
//Method Name: bMethod
//There is no comment on this method.
--------------------------

2.3 Summary

From a small demo like the one above, we can see that reflection captures annotation information for each method and then judges it. If this is the @Transactional annotation, spring will open the transaction. Next, we can implement a transaction annotation on our own in this way.

3. Notes on Handwritten Transactions

3.1 maven dependence

    <dependencies>
        <!-- Introduce Spring-AOP Equivalent correlation Jar -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.5.4</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>
    </dependencies>

3.2 Configure spring.xml file

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--Scan the specified path-->
    <context:component-scan base-package="com.wyq" />
    <!--Open Section Agent-->
    <aop:aspectj-autoproxy />
    <!--Data source object, C3P0 Connection pool-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--JdbcTemplate Tool class instance-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--Configuration transaction-->
    <bean id="dataSoutceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

3.3 Customize transaction annotations (by reflecting annotations on parsing methods, if any, execute transaction logic)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation {
    //Properties of custom annotations
    int id() default 0;

    String name() default "Default name";

    String[] arrays() default {};

    String title() default "Default title";
}

3.4 Packaging Programming Transactions

@Component
@Scope("prototype")
public class TransactionUtil {
    // Global Acceptance Transaction State
    private TransactionStatus transactionStatus;

    // Acquisition Transaction Origin
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    // Open a transaction
    public TransactionStatus begin() {
        System.out.println("Open a transaction");
        transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transactionStatus;
    }

    // Submission transaction
    public void commit(TransactionStatus transaction) {
        System.out.println("Submission transaction");
        if (dataSourceTransactionManager != null) {
            dataSourceTransactionManager.commit(transaction);
        }
    }

    public void rollback(TransactionStatus transaction) {
        System.out.println("Roll back transactions");
        if (dataSourceTransactionManager != null) {
            dataSourceTransactionManager.rollback(transaction);
        }
    }
}

3.5 Encapsulate transaction tool classes via AOP to trigger transactions based on surround notification and exception notification

@Component
@Aspect
public class AopTransaction {
    @Autowired
    private TransactionUtil transactionUtil;

    private TransactionStatus transactionStatus;

    /**
     * Around notifications, before and after methods
     *
     * @param pjp breakthrough point
     */
    @Around("execution(* com.wyq.service.*.*(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        // Annotations for acquisition methods
        MyAnnotation annotation = this.getMethodMyAnnotation(pjp);
        // Determine whether a transaction needs to be opened
        transactionStatus = begin(annotation);
        // Calling the target proxy object method
        pjp.proceed();
        // Judging closure of transactions
        commit(transactionStatus);
    }

    /**
     * Get transaction annotations on proxy methods
     *
     * @param pjp
     * @return
     * @throws Exception
     */
    private MyAnnotation getMethodMyAnnotation(ProceedingJoinPoint pjp) throws Exception {
        // Method of obtaining proxy object
        String methodName = pjp.getSignature().getName();
        // Getting the target object
        Class<?> classTarget = pjp.getTarget().getClass();
        // Get the target object type
        Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
        // Method of Obtaining Target Object
        Method objMethod = classTarget.getMethod(methodName, par);
        // Obtain transaction annotations on this method
        MyAnnotation annotation = objMethod.getDeclaredAnnotation(MyAnnotation.class);
        return annotation;
    }

    /**
     * Open a transaction
     *
     * @param annotation
     * @return
     */
    private TransactionStatus begin(MyAnnotation annotation) {
        if (annotation == null) {
            return null;
        }
        return transactionUtil.begin();
    }

    /**
     * Submission transaction
     *
     * @param transactionStatus
     */
    private void commit(TransactionStatus transactionStatus) {
        if (transactionStatus != null) {
            transactionUtil.commit(transactionStatus);
        }
    }

    /**
     * Exception notification rollback transaction
     */
    @AfterThrowing("execution(* com.wyq.service.*.*(..))")
    public void afterThrowing() {
        // Get the current transaction rollback directly
        if (transactionStatus != null) {
            transactionUtil.rollback(transactionStatus);
        }
    }
}

3.6 dao layer

/*
CREATE TABLE `t_user0` (
  `name` varchar(20) NOT NULL,
  `age` int(5) DEFAULT NULL,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/
@Repository
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public int add(String name, Integer age) {
        String sql = "INSERT INTO t_user0(NAME, age) VALUES(?,?);";
        int result = jdbcTemplate.update(sql, name, age);
        System.out.println("Successful insertion");
        return result;
    }
}

3.7 service Layer

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    // Add transaction annotations
    @MyAnnotation
    public void add() {
        userDao.add("test001", 20);
        int i = 1 / 0;  //Set exceptions to check the correctness of transactions
        userDao.add("test002", 21);
//        // If you do not want to try, then an exception will not be detected by aop's exception notification, and you must manually roll back the transaction in the catch.
//        try {
//            userDao.add("test001", 20);
//            int i = 1 / 0;
//            userDao.add("test002", 21);
//        } catch (Exception e) {
//            // Roll back the current transaction
//            System.out.println("rollback");
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//        }
    }

    public void del() {
        System.out.println("del...");
    }
}

3.8 Test

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        // aop() intercepts the userService class, and adding custom transaction annotations triggers transaction logic
        userService.add();
        // If the del() method is not annotated, nothing will trigger.
        //userService.del();
    }
}

4. Reference

https://www.cnblogs.com/wlwl/p/10092494.html

https://www.cnblogs.com/wlwl/p/10032206.html

Keywords: Spring xml MySQL JDBC

Added by natefanaro on Sun, 01 Sep 2019 05:05:02 +0300