Spring mybatis update several operations

Brief introduction

It is inevitable that there will be batch operations in daily programming. There are many frameworks for realizing physical batch operations, such as mybatis plus, and some middleware that also have operations such as cross-database operations, such as Ali’s ADB , and apache open source shardingsphere , Haha, these are all products produced in the era of microservices, so I won't say much here. Here are mainly two kinds of implementation batch updates. Before learning, it is recommended to understand the plugin principle intercepter of AOP and mybait .
1. Mybatis interceptor realizes batch operation
2. Mybatis loop, use case when to realize batch operation

Code practice

Interceptor batch operation

1. Write a batch operation class
Inherit the batch operation class that comes with mybatis in the class

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

}

Bulk interceptor

After implementing the batch operation class, of course the interceptor is injected.

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) {
    
    
    }
}

Configuration injection interceptor

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

Test 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>

mapper object

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

Entity operation object

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

Test class

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

Viewing the results of the operation shows that it is only an update performed to achieve physical batch operations
Insert picture description here

Use mybatis to achieve batch

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

Results of the

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

Source code

github

References
[ sql batch ]

Guess you like

Origin blog.csdn.net/soft_z1302/article/details/112723447