Spring mybatis actualiza varias operaciones

Breve introducción

Es inevitable que haya operaciones por lotes en la programación diaria. Hay muchos marcos para realizar operaciones por lotes físicos, como mybatis plus, y algunos middleware que también tienen operaciones como operaciones entre bases de datos, como ADB de Ali y apache open source shardingsphere , Jaja, todos estos son productos producidos en la era de los microservicios, así que no diré mucho aquí. A continuación, se muestran principalmente dos tipos de actualizaciones por lotes de implementación. Antes de aprender, se recomienda comprender el principio del complemento interceptor de AOP y mybait .
1. El interceptor Mybatis realiza la operación por lotes
2. Bucle Mybatis, caso de uso cuando realizar la operación por lotes

Práctica del código

Operación por lotes del interceptor

1. Escriba una clase de operación por
lotes Herede la clase de operación por lotes que viene con mybatis en la clase

package com.lgh.batch.inteceptor;

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;

/**
 * 批量操作
 *
 * @author guohu
 */
final class BatchExecutorAdaptor extends BatchExecutor {
    
    
    private Logger log = LoggerFactory.getLogger(BatchExecutorAdaptor.class);

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

    @Override
    public int update(MappedStatement ms, Object parameter) throws SQLException {
    
    
        if (parameter == null) {
    
    
            super.update(ms, parameter);
        }
        final Object params;
        if (parameter instanceof Map) {
    
    
            final Map<String, Object> paramMap = (Map<String, Object>) parameter;
            if (paramMap == null || paramMap.size() != 1) {
    
    
                if (paramMap.size() == 2 && paramMap.get("collection") != null) {
    
    
                    params = paramMap.get("collection");
                } else if (!paramMap.containsKey("param1")) {
    
    
                    return super.update(ms, parameter);
                } else {
    
    
                    params = paramMap.get("param1");
                }
            } else {
    
    
                params = paramMap.values().iterator().next();
            }
        } 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);
            }
            List<BatchResult> batchResults = doFlushStatements(false);
            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;
        }
    }


    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;
    }

    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;
    }

}

Interceptor a granel

Después de implementar la clase de operación por lotes, por supuesto, se inyecta el interceptor.

package com.lgh.batch.inteceptor;

import com.lgh.batch.utils.ExecutorUtil;
import com.lgh.batch.utils.Reflection;
import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.Properties;

/**
 * 批量操作拦截器,以Batch结束语句
 *
 * @author guohu
 */
@Intercepts({
    
    
        @Signature(type = Executor.class, method = "update", args = {
    
    MappedStatement.class, Object.class}),
})
public class BatchExecutorInterceptor implements Interceptor {
    
    

    private static final Logger LOGGER = LoggerFactory.getLogger(BatchExecutorInterceptor.class);

    @Override
    public Object intercept(final 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();
        }
        // 找到执行对象
        final Executor targetExecutor = ExecutorUtil.getTargetExecutor((Executor) invocation.getTarget());
        // 若是批处理,则不做操作
        if (targetExecutor instanceof BatchExecutor) {
    
    
            return invocation.proceed();
        }
        // 获取配置文件
        final Configuration configuration = (Configuration) Reflection.getField("configuration", targetExecutor);
        // 创建批处理对象
        final BatchExecutor batchExecutor = new BatchExecutorAdaptor(configuration, targetExecutor.getTransaction());
        try {
    
    
            return batchExecutor.update(mappedStatement, invocation.getArgs()[1]);
        } catch (SQLException e) {
    
    
            LOGGER.error("batch excute", e);
            batchExecutor.flushStatements(true);
            throw e;
        }
    }

    @Override
    public Object plugin(final Object target) {
    
    
        if (!(target instanceof Executor)) {
    
    
            return target;
        }
        if (target instanceof BatchExecutor) {
    
    
            return target;
        }

        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(final Properties properties) {
    
    
    }
}

Interceptor de inyección de configuración

  @Bean
    public Interceptor myBatchInterceptor() {
    
    
        return new BatchExecutorInterceptor();
    }

Prueba sql

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.lgh.mapper.UserTableMapper" >
    <update id="updateBatch" parameterType="java.util.List">
        update user set name=#{name} where id=#{id}
    </update>
</mapper>

objeto mapeador

package com.lgh.mapper;

import com.lgh.entity.UserTable;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface UserTableMapper {
    
    

    List<UserTable> findAll();
    int updateBatch(List<UserTable> userTableList);

Objeto de operación de entidad

package com.lgh.entity;

public class UserTable {
    
    
    private String id;
    private String name;
    private String msg;

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getMsg() {
    
    
        return msg;
    }

    public void setMsg(String msg) {
    
    
        this.msg = msg;
    }
}

Clase de prueba

package com.lgh;

import com.lgh.entity.UserTable;
import com.lgh.mapper.UserTableMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RunApplicationTest {
    
    
    @Autowired
    private UserTableMapper userTableMapper;

    @Test
    public void testSql() {
    
    
        List<UserTable> list = new ArrayList<>();
        UserTable table1 = new UserTable();
        table1.setId("1");
        table1.setName("张三1");
        UserTable table2 = new UserTable();
        table2.setId("2");
        table2.setName("张三2");
        UserTable table3 = new UserTable();
        table3.setId("3");
        table3.setName("张三3");
        list.add(table1);
        list.add(table2);
        list.add(table3);
        userTableMapper.updateBatch(list);
    }
}

Ver los resultados de la operación muestra que es solo una actualización realizada para lograr operaciones físicas por lotes
Inserte la descripción de la imagen aquí

Utilice mybatis para lograr lotes

  <update id="updateByList" parameterType="list">
        update user
        set name = CASE
            <foreach collection="users" item="user">
                WHEN id = #{
    
    user.id} THEN #{
    
    user.name}
            </foreach>
          END
        where id IN
        <foreach collection="users" item="user" separator="," open="(" close=")">
            #{
    
    user.id}
        </foreach>
    </update>

Resultados de la

2021-01-16 20:17:16.877 DEBUG 11388 --- [           main] c.l.mapper.UserTableMapper.updateByList  : ==>  Preparing: update user set name = CASE WHEN id = ? THEN ? WHEN id = ? THEN ? WHEN id = ? THEN ? END where id IN ( ? , ? , ? ) 
2021-01-16 20:17:16.921 DEBUG 11388 --- [           main] c.l.mapper.UserTableMapper.updateByList  : ==> Parameters: 1(String), 张三1(String), 2(String), 张三2(String), 3(String), 张三3(String), 1(String), 2(String), 3(String)
2021-01-16 20:17:16.928 DEBUG 11388 --- [           main] c.l.mapper.UserTableMapper.updateByList  : <==    Updates: 3

Código fuente

github

Referencias
[ lote de sql ]

Supongo que te gusta

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