Mybatis 动态执行SQL语句

简介: Mybatis 动态执行SQL语句

有很多的接口都只是执行个SQL查询之后就直接返回给前端,那么我们能不能把这些SQL保存在数据库中,调用一个固定的接口就能根据传参查询出想要的数据呢?或者当为了加减个字段就得修改代码重启服务的痛苦能不能减少点呢?下面就是方案。


调用直接传入SQL语句(可以选择存数据库)和参数,SQL语句写法和在XML内的写法保持一致即可,包括Mybatis标签等等,参数选择使用通用的Map,可以从接口接收任何参数,方法的返回值是List<Map>。

<dependency>
   <groupId>org.ow2.asm</groupId>
   <artifactId>asm</artifactId>
   <version>7.0</version>
</dependency>
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map;
//from fhadmin.cn
@Component
@Slf4j
public class SqlExecutor {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    public void parserString(String originSql,Map<String,Object> param) {
        StringBuilder builder = new StringBuilder();
        builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
        builder.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n");
        builder.append("<mapper namespace=\"cn.video.asm.TestMapper\">\r\n");
        builder.append("   <select id=\"queryById\"  resultType=\"java.util.Map\">\r\n");
        builder.append(originSql);
        builder.append("   </select>\r\n");
        builder.append("</mapper>");
        InputStream inputStream = new ByteArrayInputStream(builder.toString().getBytes());
        Configuration baseConfig = sqlSessionFactory.getConfiguration();
        // 不能使用原有的config对象加载,否则下次就不会重复加载导致传入的SQL语句不能切换
        // 也可以在这里指定数据源,从对应的数据源做查询动作
        Configuration configuration = new Configuration(baseConfig.getEnvironment());
        String resource = "resource";
        ErrorContext.instance().resource(resource);
        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,resource ,configuration.getSqlFragments());
        mapperParser.parse();
        SqlSession sqlSessionXML = new DefaultSqlSessionFactory(configuration).openSession();
        Object result = null;
        try {
            // 使用自定义的ClassLoader
            MyClassLoader loader = new MyClassLoader();
            // 生成二进制字节码
            byte[] bytes = MyClassLoader.dump();
            // 加载我们生成的 Mapper类
            Class<?> clazz = loader.defineClass("cn.video.asm.TestMapper", bytes);
            // 将生成的类对象加载到configuration中
            configuration.addMapper(clazz);
            Method query = clazz.getMethod("queryById", Map.class);
            // 这里就是通过类对象从configuration中获取对应的Mapper
            Object testMapper = sqlSessionXML.getMapper(clazz);
            result = query.invoke(testMapper, param);
        } catch (Exception e) {
            log.error("",e);
        }
        System.out.println("dyn : " + result);
    }
}
package cn.video.common;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
//from fhadmin.cn
public class MyClassLoader extends ClassLoader {
    public static byte[] dump() {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(52, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "cn/video/asm/TestMapper", null, "java/lang/Object", null);
        cw.visitSource("TestMapper.java", null);
        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "queryById", "(Ljava/util/Map;)Ljava/util/List;", "(Ljava/util/Map;)Ljava/util/List<Ljava/lang/Object;>;", null);
            mv.visitEnd();
        }
        cw.visitEnd();
        return cw.toByteArray();
    }
    public Class<?> defineClass(String name, byte[] b) {
        // ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
        // 所以我们需要定义一个子类将这个方法暴露出来
        Class<?> clazz = super.findLoadedClass(name);
        if (clazz != null) {
            return clazz;
        }
        return super.defineClass(name, b, 0, b.length);
    }
}

 

目录
相关文章
|
15天前
|
SQL XML Java
mybatis实现动态sql
MyBatis的动态SQL功能为开发人员提供了强大的工具来应对复杂的查询需求。通过使用 `<if>`、`<choose>`、`<foreach>`等标签,可以根据不同的条件动态生成SQL语句,从而提高代码的灵活性和可维护性。本文详细介绍了动态SQL的基本用法和实际应用示例,希望对您在实际项目中使用MyBatis有所帮助。
45 11
|
2月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
2月前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
3月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
61 10
|
4月前
|
SQL XML Java
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
文章介绍了MyBatis中动态SQL的用法,包括if、choose、where、set和trim标签,以及foreach标签的详细使用。通过实际代码示例,展示了如何根据条件动态构建查询、更新和批量插入操作的SQL语句。
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
|
5月前
|
SQL Java 数据库连接
Mybatis系列之 Error parsing SQL Mapper Configuration. Could not find resource com/zyz/mybatis/mapper/
文章讲述了在使用Mybatis时遇到的资源文件找不到的问题,并提供了通过修改Maven配置来解决资源文件编译到target目录下的方法。
Mybatis系列之 Error parsing SQL Mapper Configuration. Could not find resource com/zyz/mybatis/mapper/
|
4月前
|
SQL XML Java
mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法
当然,这些仅是MyBatis功能的初步介绍。MyBatis还提供了高级特性,如动态SQL、类型处理器、插件等,可以进一步提供对数据库交互的强大支持和灵活性。希望上述内容对您理解MyBatis的基本操作有所帮助。在实际使用中,您可能还需要根据具体的业务要求调整和优化SQL语句和配置。
74 1
|
5月前
|
SQL Java 数据库连接
Mybatis系列之 动态SQL
文章详细介绍了Mybatis中的动态SQL用法,包括`<if>`、`<choose>`、`<when>`、`<otherwise>`、`<trim>`和`<foreach>`等元素的应用,并通过实际代码示例展示了如何根据不同条件动态生成SQL语句。
|
5月前
|
SQL Java 关系型数据库
SpringBoot 系列之 MyBatis输出SQL日志
这篇文章介绍了如何在SpringBoot项目中通过MyBatis配置输出SQL日志,具体方法是在`application.yml`或`application.properties`中设置MyBatis的日志实现为`org.apache.ibatis.logging.stdout.StdOutImpl`来直接在控制台打印SQL日志。
SpringBoot 系列之 MyBatis输出SQL日志
|
3月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
160 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。