mybaits整合springboot加载自定义sql

目标:将接口配置的sql存到mysql,还要支持mybaits的标签。
思路:在mybatisplus加载完成后。从数据把sql查出来后模仿mybatis加载xml配置文件过程加载。用asm框架生成mapper的接口中的方法。这样实现了配置mybatis。
注意:因为mybatis架构中存放xml配置几乎都是hashMap,所以在加载注意后面多线程读取或加载会有线程不安全的问题。翻了mybaits源码也没找到解决方案。
代码:https://gitee.com/kingqiu/springbootmybaitsplus
效果:
在这里插入图片描述

主要代码(文末有完整代码下载)

1、加载配置主要代码

package com.example.springbootmybaitsplus.asm;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.example.springbootmybaitsplus.asm.DTO.ChartConfigDTO;
import com.example.springbootmybaitsplus.asm.DTO.MapperClassDTO;
import com.example.springbootmybaitsplus.asm.DTO.MapperMethonDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
@Primary
@Slf4j
public class TolocalNewSqlToMybaitsService {
    @Autowired
    private MapperCreate mapperCreate;


    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;


//    @Bean
//    public SqlSessionTemplate getSqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
//        log.info("构造SqlSessionTemplate");
//        return new MySqlSessionTemplate(sqlSessionFactory);
//    }


    /*
     * 执行sql
     * */
    public List<Map<String, Object>> dealChartSql(ChartConfigDTO chartConfigDTO, Object requestBody) {
        try {
            //获取mapper的class
            Class<?> mapperClass = mapperCreate.loadClass(MybaitsSqlContext.mapperPathName);
            if (ObjectUtil.isNull(mapperClass)) {
                throw new RuntimeException("图标查询未初始化!");
            }
            Method query = mapperClass.
                    getMethod(chartConfigDTO.getTheme(), HashMap.class);
            SqlSession sqlSession = getSqlSession();
            Object mapper = sqlSession.getMapper(mapperClass);
            List<Map<String, Object>> invoke = (List<Map<String, Object>>) query.invoke(mapper, requestBody);
            sqlSession.close();
            return invoke;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /*
     * 刷新配置
     *
     * */
    public void refashSqlConfig(List<ChartConfigDTO> chartConfigList) throws Exception {
        //获取所有配置
        log.info("获取到图标配置{}", chartConfigList);
        //拼接出mapper的配置文件
        String mapperXml = createMapperXML(chartConfigList);
        //获取mybatisplus配置
        SqlSession sqlSession = getSqlSession();
        Configuration configuration = sqlSession.getConfiguration();
        //加载自定义的sql
        XMLMapperBuilder mapperParser = new XMLMapperBuilder(
                new ByteArrayInputStream(mapperXml.getBytes()),
                configuration,
                "resource",
                configuration.getSqlFragments());
        mapperParser.parse();

        // 使用自定义的ClassLoader生成mapper并注册到mybatis中
        List<MapperMethonDTO> mapperMethonDTOList = chartConfigList.stream().map(item ->
                        new MapperMethonDTO(item.getTheme(),
                                MybaitsSqlContext.descriptor,
                                MybaitsSqlContext.signature)).
                collect(Collectors.toList());
        byte[] byteClassData = mapperCreate.dump(new MapperClassDTO(MybaitsSqlContext.mapperName,
                MybaitsSqlContext.mapperPath, mapperMethonDTOList));
        Class<?> mapperClass = mapperCreate.defineClass(
                MybaitsSqlContext.mapperPathName,
                byteClassData);
        configuration.addMapper(mapperClass);
        sqlSession.close();
    }


    //创建mapper的xml文件
    private String createMapperXML(List<ChartConfigDTO> chartConfigList) {
        StringBuffer mapperSqlXML = new StringBuffer();
        chartConfigList.forEach(item -> {
            mapperSqlXML.append(StrUtil.format(MybaitsSqlContext.selectInitXml,
                    item.getTheme(), item.getChartSql()));
        });
        String mapperXml = StrUtil.format(MybaitsSqlContext.mapperInitXML,
                MybaitsSqlContext.mapperPath,
                MybaitsSqlContext.mapperName,
                mapperSqlXML);
        log.info("刷新mapper配置文件{}", mapperXml);
        return mapperXml;
    }


//    /*
//     * 启动后自动加载
//     *
//     * */
//    @Override
//    public void run(ApplicationArguments args) throws Exception {
//        refashSqlConfig();
//    }

    /*
     *
     * 获取mybaits的SqlSession
     * */
    private SqlSession getSqlSession() {
        return SqlSessionUtils.getSqlSession(sqlSessionTemplate.getSqlSessionFactory(),
                sqlSessionTemplate.getExecutorType(),
                sqlSessionTemplate.getPersistenceExceptionTranslator());
    }


}

2、生成mapper接口代码

package com.example.springbootmybaitsplus.asm;

import cn.hutool.core.util.StrUtil;
import com.example.springbootmybaitsplus.asm.DTO.MapperClassDTO;
import com.example.springbootmybaitsplus.asm.DTO.MapperMethonDTO;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.springframework.stereotype.Component;

import static org.objectweb.asm.Opcodes.*;


@Component
@Slf4j
public class MapperCreate extends ClassLoader  {

    public byte[] dump(MapperClassDTO mapperClassDTO) throws Exception {
        //生成class写入流
        ClassWriter cw = new ClassWriter(0);
        //设置class的基本信息
        cw.visit(V1_8, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
                StrUtil.replace(mapperClassDTO.getPath()+"."+mapperClassDTO.getNameClase(), ".", "/"),
                null,
                "java/lang/Object",
                null);
        cw.visitSource(StrUtil.format("{}.java", mapperClassDTO.getNameClase()), null);
        //根据chart配置信息生成方法
        for (MapperMethonDTO mapperMethonDTO : mapperClassDTO.getMapperMethonDTOList()) {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT,
                    mapperMethonDTO.getNameMethon(),
                    mapperMethonDTO.getDescriptor(),
                    mapperMethonDTO.getSignature(),
                    null);
            mv.visitEnd();
        }
        cw.visitEnd();
        return cw.toByteArray();
    }

    public Class<?> defineClass(String name, byte[] b) {
        // ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
        // 所以我们需要定义一个子类将这个方法暴露出来
        return super.defineClass(name, b, 0, b.length);
    }

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值