mybatis-package tool class SqlSessionUtil, using dynamic proxy package transaction call processor

mybatis-sqlSession tool class, implementation of transaction processor

1. Scope and life cycle

Understanding the different scopes and life cycle categories is crucial, because incorrect use can cause very serious concurrency problems.

The dependency injection framework can create thread-safe, transaction-based SqlSessions and mappers, and inject them directly into your beans, so you can ignore their life cycles.

1.1SqlSessionFactoryBuilder

This class can be instantiated, used and discarded. Once the sqlSessionFactory is created, we don’t need it anymore

1.2SqlSessionFactory

Once created, it should always exist during the running of the application, there is no reason to discard it or re-create another instance, so we can write it in a static method, initialize the instance object, and initialize it only once

1.3SqlSession

Each thread should have its own SqlSession instance. The instance of SqlSession is not safe, so it cannot be shared, so its best scope should be a request or method scope

2. The realization of sqlSession tool class

2.1 Implementation ideas

Ideas:

  1. When the class is loaded, we initialize the sqlSessionFactory object

  2. Get the sqlSession object in the current thread. When we get it for the first time, it must be empty. At this time, we make a null judgment, create a sqlSession object, and bind it to the current thread

  3. The method of releasing resources, unbinding the sqlSession and the current thread

    Important : Tomcat has its own thread pool. When the concurrency is not high, a new thread will be automatically allocated when a new request comes in. In the case of high concurrency, the used t1 thread is likely to use this thread when the next request comes in. At this time, step 2 will inevitably get the thread with sqlSession, so we need to unbind here. Call the remove() method

  4. Ways to roll back transactions

2.2 concrete realization

package com.yth.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
/**
 * SqlSession工具类
 * @author yth
 * @date 2020/10/14 - 8:08
 */
public class SqlSessionUtils {
    
    
    //工具类的构造方法都是一般都是私有化的,因为工具类的使用不需要实例化
    private SqlSessionUtils(){
    
    
    }

    private static SqlSessionFactory sqlSessionFactory;

    private static ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();

    //类加载的时候,初始化sqlSessionFactory对象,初始化一次
    static {
    
    
        try {
    
    
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("sqlMapConfig.xml"));
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    /**
     * 获取当前线程中的sqlSession对象
     * @return
     */
    public static SqlSession getCurrentSqlSession(){
    
    
        SqlSession sqlSession = local.get();
        if(sqlSession == null){
    
    
            sqlSession = sqlSessionFactory.openSession();
            local.set(sqlSession);      //将sqlSession绑定到当前线程上
        }
        return sqlSession;
    }
    /**
     * 释放资源
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
    
    
        if(sqlSession!=null){
    
    
            sqlSession.close();
            //重要 接触sqlSession与当前线程的绑定关系
            local.remove(); //tomcat自带线程池,用过的线程t1,下次可能还会使用线程t1
        }
    }
    /**
     * 回滚事务
     * @param sqlSession
     */
    public static void rollback(SqlSession sqlSession){
    
    
        if(sqlSession!=null){
    
    
            sqlSession.rollback();
        }
    }
}

2.3 About ThreadLocal class

2.3.1 What is ThreadLocal?

ThreadLocal is a structure used to maintain the variables in the thread without being interfered by other threads. It contains a ThreadLocalMap class, which is a local variable of the Thread class. The key stored in the Map is the ThreadLocal object itself, and the value is what we want. The stored objects, in this way, in different threads, are actually copies of the variables of the current thread, completely isolated from other threads, so as to ensure that the thread is not affected by other threads during the execution of the thread.

2.3.2 Methods provided by ThreadLocal

Main method:

  1. void set(Object value)
    sets the value of the thread local variable of the current thread.
  2. public Object get()
    This method returns the thread local variable corresponding to the current thread.
  3. public void remove()
    deletes the value of the current thread local variable, the purpose is to reduce the memory occupation, this method is a new method of JDK 5.0. It should be pointed out that when the thread ends, the local variables corresponding to the thread will be automatically garbage collected, so it is not necessary to explicitly call this method to clear the thread's local variables, but it can speed up the memory recovery.

Therefore, we can use ThreadLocal to save the context information and get it anywhere we need it. From the source code, we can see the entry definition used to store data in ThreadLocalMap.

  static class Entry extends WeakReference<ThreadLocal<?>> {
     
     
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
     
     
                super(k);
                value = v;
            }
        }

From this, we will find that the key of this Map is an instance object of the ThreadLocal class, and the value is the value set by the user, so this working principle is determined: each thread has a variable on its own, not shared.

When the ThreadLocal is garbage collected, the key value of the Entry corresponding to the ThreadLocalMap will become null, but the Entry is a strong reference, and the Object type stored in the indexed Entry cannot be recycled, so in practice, we should actively call it when we don’t use ThreadLocal The remove method cleans up.

3. Implementation of transaction processor

The SqlSession tool class is ready. Now you need to use a dynamic proxy to handle transactions. First, you need to implement the InvocationHandler interface and provide a construction method. Use the SqlSession tool class to get the sqlSession object, call the commit method, and call the rollback method when handling exceptions. In finally The method of releasing resources is called in the method. Through the reflection mechanism, the Method.invoke (object name, parameter value) method dynamically obtains the target method in the service and returns. In this way, we can get the proxy object instance through getProxy().

package com.yth.utils;
import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 事务处理器
 * 动态代理
 * @author yth
 * @date 2020/10/14 - 8:36
 */
public class TransactionInvocationHandler implements InvocationHandler {
    
    
    private Object target;
    public TransactionInvocationHandler(Object target){
    
    
        this.target = target;
    }
    /**
     * 获取代理对象实例
     */
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        SqlSession sqlSession = null;
        Object retValue = null;
        try {
    
    
            sqlSession = SqlSessionUtils.getCurrentSqlSession();
            //调用service当中的目标方法
            retValue = method.invoke(target, args);//重要!重要!重要!!!
            sqlSession.commit();
        } catch (Exception e) {
    
    
            SqlSessionUtils.rollback(sqlSession);
            e.printStackTrace();
        }finally {
    
    
            SqlSessionUtils.close(sqlSession);
        }
        return retValue;
    }
}
   retValue = method.invoke(target, args);//重要!重要!重要!!!

Extension code can be added before and after this code, not necessarily a transaction, it can also be sending an email or making a call. For example, when a system has a problem somewhere, you need to call the leader to deal with it, and make an automatic call. Corresponding methods can be invoked to act as dynamic agents. These have nothing to do with business logic. In this way, this mode can be used, which is similar to the control transaction. Whether the code must be repeated in the control layer. This can solve the problem of code redundancy.

4. How to use these two tools

Copy according to the following code and it's done
Insert picture description here

Guess you like

Origin blog.csdn.net/qq_41520945/article/details/109106940