Mybatis 动态执行SQL语句

悠悠 2022-09-02 00:52 348阅读 0赞

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

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N0cmVhbV9uYW1l_size_16_color_FFFFFF_t_70

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

  1. <dependency>
  2. <groupId>org.ow2.asm</groupId>
  3. <artifactId>asm</artifactId>
  4. <version>7.0</version>
  5. </dependency>
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.apache.ibatis.builder.xml.XMLMapperBuilder;
  8. import org.apache.ibatis.executor.ErrorContext;
  9. import org.apache.ibatis.session.Configuration;
  10. import org.apache.ibatis.session.SqlSession;
  11. import org.apache.ibatis.session.SqlSessionFactory;
  12. import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.stereotype.Component;
  15. import java.io.ByteArrayInputStream;
  16. import java.io.InputStream;
  17. import java.lang.reflect.Method;
  18. import java.util.Map;
  19. //from fhadmin.cn
  20. @Component
  21. @Slf4j
  22. public class SqlExecutor {
  23. @Autowired
  24. private SqlSessionFactory sqlSessionFactory;
  25. public void parserString(String originSql,Map<String,Object> param) {
  26. StringBuilder builder = new StringBuilder();
  27. builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
  28. builder.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n");
  29. builder.append("<mapper namespace=\"cn.video.asm.TestMapper\">\r\n");
  30. builder.append(" <select id=\"queryById\" resultType=\"java.util.Map\">\r\n");
  31. builder.append(originSql);
  32. builder.append(" </select>\r\n");
  33. builder.append("</mapper>");
  34. InputStream inputStream = new ByteArrayInputStream(builder.toString().getBytes());
  35. Configuration baseConfig = sqlSessionFactory.getConfiguration();
  36. // 不能使用原有的config对象加载,否则下次就不会重复加载导致传入的SQL语句不能切换
  37. // 也可以在这里指定数据源,从对应的数据源做查询动作
  38. Configuration configuration = new Configuration(baseConfig.getEnvironment());
  39. String resource = "resource";
  40. ErrorContext.instance().resource(resource);
  41. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration,resource ,configuration.getSqlFragments());
  42. mapperParser.parse();
  43. SqlSession sqlSessionXML = new DefaultSqlSessionFactory(configuration).openSession();
  44. Object result = null;
  45. try {
  46. // 使用自定义的ClassLoader
  47. MyClassLoader loader = new MyClassLoader();
  48. // 生成二进制字节码
  49. byte[] bytes = MyClassLoader.dump();
  50. // 加载我们生成的 Mapper类
  51. Class<?> clazz = loader.defineClass("cn.video.asm.TestMapper", bytes);
  52. // 将生成的类对象加载到configuration中
  53. configuration.addMapper(clazz);
  54. Method query = clazz.getMethod("queryById", Map.class);
  55. // 这里就是通过类对象从configuration中获取对应的Mapper
  56. Object testMapper = sqlSessionXML.getMapper(clazz);
  57. result = query.invoke(testMapper, param);
  58. } catch (Exception e) {
  59. log.error("",e);
  60. }
  61. System.out.println("dyn : " + result);
  62. }
  63. }
  64. package cn.video.common;
  65. import jdk.internal.org.objectweb.asm.ClassWriter;
  66. import jdk.internal.org.objectweb.asm.MethodVisitor;
  67. import static jdk.internal.org.objectweb.asm.Opcodes.*;
  68. //from fhadmin.cn
  69. public class MyClassLoader extends ClassLoader {
  70. public static byte[] dump() {
  71. ClassWriter cw = new ClassWriter(0);
  72. cw.visit(52, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "cn/video/asm/TestMapper", null, "java/lang/Object", null);
  73. cw.visitSource("TestMapper.java", null);
  74. {
  75. 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);
  76. mv.visitEnd();
  77. }
  78. cw.visitEnd();
  79. return cw.toByteArray();
  80. }
  81. public Class<?> defineClass(String name, byte[] b) {
  82. // ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
  83. // 所以我们需要定义一个子类将这个方法暴露出来
  84. Class<?> clazz = super.findLoadedClass(name);
  85. if (clazz != null) {
  86. return clazz;
  87. }
  88. return super.defineClass(name, b, 0, b.length);
  89. }
  90. }

发表评论

表情:
评论列表 (有 0 条评论,348人围观)

还没有评论,来说两句吧...

相关阅读

    相关 mybatis动态SQL语句

    三、动态SQL语句         有些时候,sql语句where条件中,需要一些安全判断,例如按性别检索,如果传入的参数是空的,此时查询出的结果很可能是空的,也许我们需

    相关 Mybatis 动态执行SQL语句

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

    相关 MyBatis动态sql语句

    1.什么是动态sql?            动态sql是MyBatis特性之一,可以根据用户传入的条件,     借助xml标签对sql语句进行拼接,生成符合条件的s

    相关 MyBatis动态SQL语句

    在Mapper配置文件中,有时候需要根据一些查询条件来选择不同的SQL语句,或者将一些使用频率高的SQL语句单独配置,在需要的地方引用。MyBatis提供了一种可以根据条件动态