草庐IT

Java实现在线SQL编程【完整版】

派 大 星. 2023-04-21 原文

前言:
由于前段时间,项目组长分配的任务是要完成一个在线编写SQL并要实现查询功能的需求,最终需要将查询到的数据以JSON格式显示到响应数据的区域,以供操作者进行查看,一开始拿到需求时想着直接使用Mybatis进行操作不就可以了,完全没必要大费周章,因为在MyBatis中有个拼接SQL的语法,可以使用${sql}来进行执行输入的SQL语句,但是实际操作起来并不是想象中的那么简单,因为使用MyBatis会将数据源固定在本项目所使用的数据库,而不可以进行数据源之间的切换,无法进行其他数据源中表的查询操作 ❌,所以在实现过程也是相当艰难曲折…

🏡  博客首页:派 大 星

⛳️  欢迎关注  ❤️ 点赞  🎒 收藏  ✏️ 留言

🎢  本文由派大星原创编撰

🚧  系列专栏:项目从0搭建

🎈  本系列项目从设计到实现源码全部开源免费学习使用,一起追向理想,欢迎各位大佬监督打卡开发!



文章目录

🍉 难度分析

在线执行SQL语句的查询主要的难点分为以下几点:其一则是对JDBC的部分的API理解的不够透彻,导致在实现某些工鞥是并不是想象中的顺利;其二在于对于查询部分属性的SQL语句该如何使用Java进行实现,是将输入的字符串进行分割再拼接还是使用直接整条语句的查询操作;其三则是用户可以动态的切换数据源,并且对相应数据源下的表进行查询操作,如果使用Mybatis进行SQL的查询操作则无法进行数据源的切换,只能查询所在微服务项目所连接的数据库进行查询,否则无法进行相应的操作,即会出现该数据库下并不存在所查询的某张数据库表的错误信息 ❌

🍓 项目回顾(在线编写SQL查询)

🍅 最终效果演示

🍓 技术选型:

  • 🥒 SpringBoot
  • 🍠 MyBatis-Plus
  • 🌽 JDBC
  • 🥥 Vue
  • 🍇 代码编辑器monaco-editor

🍌 需求分析

使用者在页面可以选择切换需要操作的数据源,并在编辑区域内输入SQL语句进行查询,查询语句可以是全表查询表中所有字段或者根据所需查询指定某几列字段对应的值,点击查询之后即可在响应数据之后以JSON的格式将查询到的每一条数据封装一个对象最后显示到响应数据区域以供操作者进行观看以达到可视化工具的效果。

🍊 项目搭建

① 引入项目所需要的相关Maven依赖
<!--SpringBoot相关依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.6</version>
</dependency>
<!--servlet-api相关依赖-->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
  <scope>provided</scope>
</dependency>
<!--MySQL驱动相关依赖-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.28</version>
</dependency>
② 编写配置文件
server:
  port: 8080
spring:
  datasource:
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/data_source?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
③ 创建Controller前端控制器
	@PostMapping("/dynamic-query")
	@ApiOperation(value = "动态执行SQL")
	public Result<?> dynamicQuery(@RequestBody DynamicQuery dynamicQuery){
		List<HashMap<Object,Object>> res = null;
		BladeVisualDb visualDb = bladeVisualDbService.getOne(new QueryWrapper<BladeVisualDb>().eq("id", dynamicQuery.getId()));
		List<HashMap<String,Object>> result = ConnectBySql.connAndExecSql(visualDb.getUrl(), visualDb.getDriverClass(), visualDb.getUsername(), visualDb.getPassword(),dynamicQuery.getSql());
		return Result.ok(result);
	}
④ 探究 JDBC中ResultSet中的方法
  • next()

解释: 将光标从其当前位置向前移动一行。 ResultSet 游标最初位于第一行之前;第一次调用 next 方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推。实则对查询到的数据进行遍历操作

  • getString(String columnLabel)

解释: 根据表中对应的列名称来查询到该列对应到的数据

  • getMetaData()

解释: 获取某张表中列的数量、类型和列名称

  • findColumn(String columnLabel)

解释: 返回某个列的索引值即下标

⑤ 获取所有的数据源

因为使用者是可以动态的选择数据源从而来对表中的数据进行相关操作,所以首先要查询出可供连接的数据源

	 /**
	  * 下拉数据源列表
	  * @return
	  */
	@ApiOperation(value = "下拉列表")
	@GetMapping("/db-list")
	public Result<?> list(){
		List<BladeVisualDb> bladeVisualDbs = bladeVisualDbService.list();
		return Result.ok(bladeVisualDbs);
	}
⑥ 实现执行查询逻辑

首先执行查询有两种方式:所有字段的全查询即select * from xx 或者 查询指定字段即select xx from xx

  • 所有字段的全查询即select * from xx

在进行所有字段的查询时,由于无法得知需要查询的表中有什么字段,所以首先需要对输入的SQL字符串进行判断是否是全字段查询,然后即可获取查询表中的所有字段,然后再一一的进行查询出字段对应的值即可,此操作也是需要对输入SQL字符串进行分割的,拿出所有插叙的表名即可。

ResultSet resultSet = stmt.executeQuery(sql);
//如果输入的SQL属于select * 操作
if (sql.contains("*")){
  List<String> list = new ArrayList<>();
  //获取SQL中需要查询的表的结构
  ResultSetMetaData metaData = resultSet.getMetaData();
  int columnCount = metaData.getColumnCount();
  for (int i = 1; i <= columnCount; i++) {
    log.info("属性列   ======>{}",metaData.getColumnName(i));
    //获取表中的所有列
    list.add(metaData.getColumnName(i));
  }
  while (resultSet.next()){
    HashMap<String,Object> hashMap = new HashMap<>();
    for (String index : list) {
      //根据列名称查询出该列对应的数据
      String rs = resultSet.getString(index);
      //将其放入列和值存放HashMap中
      hashMap.put(index,rs);
      //将多个HashMap合并成一个Map
      hashMap.putAll(hashMap);
    }
    res.add(hashMap);
  }
}
  • 查询指定字段即select xx from xx

在使用JDBC进行指定字段查询时需要对输入的SQL字符串进行分割后将所需要查询到的字段再使用JDBC中的getString(String columnName)进行查询字段对应的值

    /**
     * 将SQL语句进行拆分
     * @param sql
     * @return
     */
    @NotNull
    private static List<String> getString(String sql) {
        List<String> list = new ArrayList<>();
        String str = sql.substring(0, sql.indexOf("from"));
        String realSql = str.replace("select", "").trim();
        if (realSql.contains(",")){
            String[] split = realSql.split(",");
            for (String s : split) {
                list.add(s);
            }
        }else {
            list.add(realSql);
        }
        return list;
    }

随后将分割完成的SQL存放到List集合中再进行查询操作

ResultSet resultSet = stmt.executeQuery(sql);
while (resultSet.next()){
  HashMap<String,Object> hashMap = new HashMap<>();
  List<String> str = getString(sql);
  for (String index : str) {
    String rs = resultSet.getString(index);
    hashMap.put(index,rs);
    hashMap.putAll(hashMap);
  }
  res.add(hashMap);
}

经过对操作者输入的SQL即可完成对表的查询操作,由于所要实现的是可动态切换数据源从而进行相关的查询操作,所以在此操作逻辑中首先需要连接数据库,后再对输入的SQL进行分割查询等操作。合并后的完整代码:

    /**
     * 连接数据库并根据输入的SQL语句查询数据
     * @param url
     * @param driverClass
     * @param username
     * @param password
     * @param sql
     * @return
     */
    public static List<HashMap<String,Object>> connAndExecSql(String url, String driverClass, String username, String password, String sql) {
        Boolean result = false;
        Connection conn = null;
        Statement stmt = null;
        List<HashMap<String,Object>> res = new ArrayList<>();

        try {
            Class.forName(driverClass);
            System.out.println("------------连接数据库-----------");
            conn = DriverManager.getConnection(url,username,password);
            stmt = conn.createStatement();

            ResultSet resultSet = stmt.executeQuery(sql);
            //如果输入的SQL属于select * 操作
            if (sql.contains("*")){
                List<String> list = new ArrayList<>();
                //获取SQL中需要查询的表的结构
                ResultSetMetaData metaData = resultSet.getMetaData();
                int columnCount = metaData.getColumnCount();
                for (int i = 1; i <= columnCount; i++) {
                    log.info("属性列   ======>{}",metaData.getColumnName(i));
                    //获取表中的所有列
                    list.add(metaData.getColumnName(i));
                }
                while (resultSet.next()){
                    HashMap<String,Object> hashMap = new HashMap<>();
                    for (String index : list) {
                        //根据列名称查询出该列对应的数据
                        String rs = resultSet.getString(index);
                        //将其放入列和值存放HashMap中
                        hashMap.put(index,rs);
                        //将多个HashMap合并成一个Map
                        hashMap.putAll(hashMap);
                    }
                    res.add(hashMap);
                }
            }else {
                while (resultSet.next()){
                    HashMap<String,Object> hashMap = new HashMap<>();
                    List<String> str = getString(sql);
                    for (String index : str) {
                        String rs = resultSet.getString(index);
                        hashMap.put(index,rs);
                        hashMap.putAll(hashMap);
                    }
                    res.add(hashMap);
                }
            }
            //完成连接数据库
            stmt.close();
            conn.close();
            System.out.println("查询连接结束");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (SQLException e){
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 将SQL语句进行拆分
     * @param sql
     * @return
     */
    @NotNull
    private static List<String> getString(String sql) {
        List<String> list = new ArrayList<>();
        String str = sql.substring(0, sql.indexOf("from"));
        String realSql = str.replace("select", "").trim();
        if (realSql.contains(",")){
            String[] split = realSql.split(",");
            for (String s : split) {
                list.add(s);
            }
        }else {
            list.add(realSql);
        }
        return list;
    }
⑦ Vue前端引入monaco-editor组件进行编写SQL语句

🌟 项目总结

在使用JDBC来进行数据库的操作,在使用时由于对其方法并不是全部了解,所以需要边看源码边改善功能,因此遇到问题时才会感觉到脑中知识储备的不足,目前只是实现了查询操作,还未完善CRUD全部过程的操作。

🔔 🔔 🔔 E n d i n g 🔔 🔔 🔔 🔔 🔔 🔔 Ending 🔔 🔔 🔔 🔔🔔🔔Ending🔔🔔🔔

有关Java实现在线SQL编程【完整版】的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  4. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  5. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  6. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  9. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  10. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

随机推荐