Falando sobre o plugin mybatis

Antes de discutir a tecnologia, gostaria de fazer uma pergunta, por que interpretar o código-fonte e a tecnologia central? Na verdade, a equipe júnior geralmente pensa que é o suficiente para usar e não há necessidade de gastar tanto tempo estudando o código-fonte e o processo. No entanto, eu pessoalmente sinto que interpretar o código-fonte tem as seguintes vantagens:
1. Conhecimento profundo de java, retorno ao conhecimento geral de si mesmo
2. Adote o modo de design, experimente as idéias de design da tecnologia de código aberto
3. Design independente e melhoria do pensamento lógico matemático
4. Aprenda a resumir e melhorar a si mesmo

Introdução

    O plug-in mybatis é a parte central e tem um pequeno valor em aplicações práticas, como consulta de paginação física, operação em lote, monitoramento e filtragem de script de banco de dados, modificação de parâmetro, monitoramento de log, etc. O plug-in Mybatis permite chamadas de interceptação para a instrução de mapeamento do ponto. Por padrão, é a interceptação de método, como o seguinte método:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject , setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (preparar, parametrizar, lote, atualizar, consultar)

Como usar

    Use a anotação @Intercepts para ter efeito, @Signature define atributos de interceptação, análise de atributo de assinatura

  1. tipo tipo interceptado, tipo de classe
  2. método O método de interceptação, tipo de string
  3. tipo de parâmetro do método args, tipo de matriz de classe, porque evita a sobrecarga
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MyBatchExecutor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 前置处理
        Object returnObject = invocation.proceed(); // 也可以直接执行自己定义的处理器
        // 后置处理
        return returnObject;
    }
}

Duas configurações comumente usadas, uma configuração xml, uma configuração java, não importa o tipo de configuração, é o método configuration # addInterceptor para adicionar o interceptor
definido em xml

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="**.**.MyBatchExecutor" />
</plugins>

Ou consulte o interceptor na anotação

import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisConfig {
    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer() {
        return new ConfigurationCustomizer() {
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                configuration.addInterceptor(new MyBatchExecutor());
            }
        };
    }
}

Princípio do interceptor

carregamento do interceptor

Insira a descrição da imagem aqui

         O Mybati é, na verdade, um multifuncional, ou seja, toda a configuração de dados está enraizada na Configuração e é analisada e armazenada em xml durante a inicialização específica. Outra maneira de viver na primavera é construir a fábrica SqlSessionFactoryBean para construir a SqlSessionFactory, como a montagem automática MybatisAutoConfiguration da bota de mola.
        

interceptor

Insira a descrição da imagem aqui
A resposta na figura acima é porque o interceptor intercepta apenas quatro lugares. O proxy dinâmico é usado aqui. Configure todos os interceptores com antecedência e chame os interceptores antes de chamar o método.
Duas etapas, respectivamente:
1. Gerar proxy dinâmico.
2. O objeto proxy é adquirido por reflexão, e o método é executado diretamente após o julgamento.
Insira a descrição da imagem aqui

Casos de uso de interceptador

Escreva um caso de interceptor de operação em lote aqui

Plugin em massa

    Todos nós sabemos que existem três tipos de Mybatis Executor, SimpleExecutor, ReuseExecutor e BatchExecutor.Na verdade, BatchExecutor implementou operações em lote, mas não é fácil de usar. Em seguida, definimos a operação de lote físico. Quando o id correspondente ao mapa termina com Batch, realizamos a operação de lote físico

import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.transaction.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;


public class BatchAdaptor extends BatchExecutor {
    private Logger log = LoggerFactory.getLogger(BatchAdaptor.class);

    public BatchAdaptor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
    }

    @Override
    public int update(MappedStatement ms, Object parameter) throws SQLException {
        if (parameter == null) {
            return super.update(ms, parameter);
        }
        Object params = null;
        if (Map.class.isAssignableFrom(parameter.getClass())) { // DefaultSqlSession#wrapCollection
            final Map<String, Object> paramMap = (Map<String, Object>) parameter;
            if (paramMap.size() == 1) { // Map中array
                if (paramMap.get("array") != null) {
                    params = paramMap.get("array");
                } else {
                    params = paramMap.values().iterator().next();
                }
            } else if (paramMap.size() == 2) {
                params = paramMap.get("collection");
            }
        } else if (parameter instanceof Iterable || parameter.getClass().isArray()) {
            params = parameter;
        } else {
            params = Collections.singletonList(parameter);
        }
        final Iterable<?> paramIterable = toIterable(params);
        try {
            for (Object obj : paramIterable) {
                super.update(ms, obj); // addBatch
            }
            List<BatchResult> batchResults = doFlushStatements(false); // executeBatch
            if (batchResults == null || batchResults.size() == 0) {
                return 0;
            }
            return resolveUpdateResult(batchResults);
        } catch (Exception e) {
            log.error("batch execute", e);
            doFlushStatements(true);
            /**
             * 批量插入,则报异常
             */
            if ("INSERT".equalsIgnoreCase(ms.getSqlCommandType().name())) {
                throw e;
            }
            return 0;
        }
    }

    /**
     * 返回批量结果成功数
     *
     * @param batchResults
     * @return
     */
    private int resolveUpdateResult(final List<BatchResult> batchResults) {
        int result = 0;
        for (BatchResult batchResult : batchResults) {
            int[] updateCounts = batchResult.getUpdateCounts();
            if (updateCounts == null || updateCounts.length == 0) {
                continue;
            }
            for (int updateCount : updateCounts) {
                result += updateCount;
            }
        }
        return result;
    }

    /**
     * 统一转换
     *
     * @param params array或者Collections
     * @return
     */
    private Iterable<?> toIterable(final Object params) {
        if (params == null) {
            return Collections.emptyList();
        }
        Iterable<?> paramIterable;
        if (params instanceof Iterable) {
            paramIterable = (Iterable<?>) params;
        } else if (params.getClass().isArray()) {
            Object[] array = (Object[]) params;
            paramIterable = Arrays.asList(array);
        } else {
            paramIterable = Collections.singletonList(params);
        }
        return paramIterable;
    }
}

Escrita do interceptor

import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.sql.SQLException;

@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MyBatchInterceptor implements Interceptor {

    private Logger log = LoggerFactory.getLogger(MyBatchInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //check argument
        if (invocation.getArgs()[1] == null) {
            return invocation.proceed();
        }
        final MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 是否需要批处理标识
        if (!mappedStatement.getId().endsWith("Batch")) {
            return invocation.proceed();
        }
        // 若是批处理,则不做操作
        if (BatchExecutor.class.isAssignableFrom(invocation.getTarget().getClass())) {
            return invocation.proceed();
        }
        Executor executor = (Executor) invocation.getTarget();
        // 创建批处理对象
        final BatchExecutor batchExecutor = new BatchAdaptor(getConfiguration(executor), executor.getTransaction());
        try {
            return batchExecutor.update(mappedStatement, invocation.getArgs()[1]);
        } catch (SQLException e) {
            log.error("batch excute", e);
            batchExecutor.flushStatements(true);
            throw e;
        }
    }

    /**
     * 获取配置文件
     *
     * @param executor
     * @return
     */
    public Configuration getConfiguration(Executor executor) {
        Field configField = ReflectionUtils.findField(executor.getClass(), "configuration");
        if (configField == null) { // CachingExecutor
            configField = ReflectionUtils.findField(executor.getClass(), "delegate");
            if (!configField.isAccessible()) {
                configField.setAccessible(true);
            }
            executor = (Executor) ReflectionUtils.getField(configField, executor);
            configField = ReflectionUtils.findField(executor.getClass(), "configuration");
            if (!configField.isAccessible()) {
                configField.setAccessible(true);
            }
        }
        // 获取配置文件
        return (Configuration) ReflectionUtils.getField(configField, executor);
    }
}

Uso de interceptor

@Configuration
public class MybatisConfig {
    @Bean
    public MyBatchInterceptor myBatchInterceptor() {
        return new MyBatchInterceptor();
    }
}

@Mapper
public interface UserTableMapper {
    @Update("update user set name=#{name},msg=#{msg} where id=#{id}")
    int updateBatch(List<UserTable> userTableList);
}

Resumo e reflexão

  1. Faça uso completo do proxy dinâmico JDK, como Plugin # wrap para construir proxy dinâmico e aprenda o modo proxy ao mesmo tempo
  2. Mecanismo de reflexão Java, obter atributos e construir a classe ReflectionUtils
  3. Montagem automática da bota de mola, início do projeto da AutoConfiguração
  4. Use o interceptor mybatis para fazer algumas operações em lote e interceptar processamento especial para os quatro pontos de execução (parameterHandler, statementHandler, resultSetHandler, execute)
  5. Os quatro corpos de execução do executor mybatis
  6. A versão mybatis-plus é mais sobre a versão aprimorada do processamento em lote e assim por diante.

Ver literatura

[1] Descrição e código fonte de mybatis3

Acho que você gosta

Origin blog.csdn.net/soft_z1302/article/details/111150461
Recomendado
Clasificación