Java實(shí)現(xiàn)在線SQL編程最新完整版
前言: 由于前段時(shí)間,項(xiàng)目組長分配的任務(wù)是要完成一個(gè)在線編寫SQL
并要實(shí)現(xiàn)查詢功能的需求,最終需要將查詢到的數(shù)據(jù)以JSON
格式顯示到響應(yīng)數(shù)據(jù)的區(qū)域,以供操作者進(jìn)行查看,一開始拿到需求時(shí)想著直接使用Mybatis
進(jìn)行操作不就可以了,完全沒必要大費(fèi)周章,因?yàn)樵?code>MyBatis中有個(gè)拼接SQL
的語法,可以使用${sql}
來進(jìn)行執(zhí)行輸入的SQL
語句,但是實(shí)際操作起來并不是想象中的那么簡單,因?yàn)槭褂?code>MyBatis會(huì)將數(shù)據(jù)源固定在本項(xiàng)目所使用的數(shù)據(jù)庫,而不可以進(jìn)行數(shù)據(jù)源之間的切換,無法進(jìn)行其他數(shù)據(jù)源中表的查詢操作 ?,所以在實(shí)現(xiàn)過程也是相當(dāng)艱難曲折...
?? 難度分析
在線執(zhí)行SQL
語句的查詢主要的難點(diǎn)分為以下幾點(diǎn):其一則是對JDBC
的部分的API
理解的不夠透徹,導(dǎo)致在實(shí)現(xiàn)某些工鞥是并不是想象中的順利;其二在于對于查詢部分屬性的SQL語句該如何使用Java
進(jìn)行實(shí)現(xiàn),是將輸入的字符串進(jìn)行分割再拼接還是使用直接整條語句的查詢操作;其三則是用戶可以動(dòng)態(tài)的切換數(shù)據(jù)源,并且對相應(yīng)數(shù)據(jù)源下的表進(jìn)行查詢操作,如果使用Mybatis
進(jìn)行SQL
的查詢操作則無法進(jìn)行數(shù)據(jù)源的切換,只能查詢所在微服務(wù)項(xiàng)目所連接的數(shù)據(jù)庫進(jìn)行查詢,否則無法進(jìn)行相應(yīng)的操作,即會(huì)出現(xiàn)該數(shù)據(jù)庫下并不存在所查詢的某張數(shù)據(jù)庫表的錯(cuò)誤信息 ?
?? 項(xiàng)目回顧(在線編寫SQL查詢)
?? 最終效果演示
?? 技術(shù)選型:
- ?? SpringBoot
- ?? MyBatis-Plus
- ?? JDBC
- ?? Vue
- ?? 代碼編輯器monaco-editor
?? 需求分析
使用者在頁面可以選擇切換需要操作的數(shù)據(jù)源,并在編輯區(qū)域內(nèi)輸入SQL
語句進(jìn)行查詢,查詢語句可以是全表查詢表中所有字段或者根據(jù)所需查詢指定某幾列字段對應(yīng)的值,點(diǎn)擊查詢之后即可在響應(yīng)數(shù)據(jù)之后以JSON
的格式將查詢到的每一條數(shù)據(jù)封裝一個(gè)對象最后顯示到響應(yīng)數(shù)據(jù)區(qū)域以供操作者進(jìn)行觀看以達(dá)到可視化工具的效果。
?? 項(xiàng)目搭建
① 引入項(xiàng)目所需要的相關(guān)Maven
依賴
<!--SpringBoot相關(guān)依賴--> <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相關(guān)依賴--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <!--MySQL驅(qū)動(dòng)相關(guān)依賴--> <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
③ 創(chuàng)建Controller前端控制器
@PostMapping("/dynamic-query") @ApiOperation(value = "動(dòng)態(tài)執(zhí)行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()
解釋: 將光標(biāo)從其當(dāng)前位置向前移動(dòng)一行。 ResultSet
游標(biāo)最初位于第一行之前;第一次調(diào)用 next
方法使第一行成為當(dāng)前行;第二次調(diào)用使第二行成為當(dāng)前行,依此類推。實(shí)則對查詢到的數(shù)據(jù)進(jìn)行遍歷操作
getString(String columnLabel)
解釋: 根據(jù)表中對應(yīng)的列名稱來查詢到該列對應(yīng)到的數(shù)據(jù)
getMetaData()
解釋: 獲取某張表中列的數(shù)量、類型和列名稱
findColumn(String columnLabel)
解釋: 返回某個(gè)列的索引值即下標(biāo)
⑤ 獲取所有的數(shù)據(jù)源
因?yàn)槭褂谜呤强梢詣?dòng)態(tài)的選擇數(shù)據(jù)源從而來對表中的數(shù)據(jù)進(jìn)行相關(guān)操作,所以首先要查詢出可供連接的數(shù)據(jù)源
/** * 下拉數(shù)據(jù)源列表 * @return */ @ApiOperation(value = "下拉列表") @GetMapping("/db-list") public Result<?> list(){ List<BladeVisualDb> bladeVisualDbs = bladeVisualDbService.list(); return Result.ok(bladeVisualDbs); }
⑥ 實(shí)現(xiàn)執(zhí)行查詢邏輯
首先執(zhí)行查詢有兩種方式:所有字段的全查詢即select * from xx
或者 查詢指定字段即select xx from xx
- 所有字段的全查詢即
select * from xx
在進(jìn)行所有字段的查詢時(shí),由于無法得知需要查詢的表中有什么字段,所以首先需要對輸入的SQL
字符串進(jìn)行判斷是否是全字段查詢,然后即可獲取查詢表中的所有字段,然后再一一的進(jìn)行查詢出字段對應(yīng)的值即可,此操作也是需要對輸入SQL
字符串進(jìn)行分割的,拿出所有插敘的表名即可。
ResultSet resultSet = stmt.executeQuery(sql); //如果輸入的SQL屬于select * 操作 if (sql.contains("*")){ List<String> list = new ArrayList<>(); //獲取SQL中需要查詢的表的結(jié)構(gòu) 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) { //根據(jù)列名稱查詢出該列對應(yīng)的數(shù)據(jù) String rs = resultSet.getString(index); //將其放入列和值存放HashMap中 hashMap.put(index,rs); //將多個(gè)HashMap合并成一個(gè)Map hashMap.putAll(hashMap); } res.add(hashMap); } }
- 查詢指定字段即
select xx from xx
在使用JDBC
進(jìn)行指定字段查詢時(shí)需要對輸入的SQL
字符串進(jìn)行分割后將所需要查詢到的字段再使用JDBC
中的getString(String columnName)
進(jìn)行查詢字段對應(yīng)的值
/** * 將SQL語句進(jìn)行拆分 * @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
集合中再進(jìn)行查詢操作
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); }
經(jīng)過對操作者輸入的SQL
即可完成對表的查詢操作,由于所要實(shí)現(xiàn)的是可動(dòng)態(tài)切換數(shù)據(jù)源從而進(jìn)行相關(guān)的查詢操作,所以在此操作邏輯中首先需要連接數(shù)據(jù)庫,后再對輸入的SQL
進(jìn)行分割查詢等操作。合并后的完整代碼:
/** * 連接數(shù)據(jù)庫并根據(jù)輸入的SQL語句查詢數(shù)據(jù) * @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("------------連接數(shù)據(jù)庫-----------"); 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中需要查詢的表的結(jié)構(gòu) 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) { //根據(jù)列名稱查詢出該列對應(yīng)的數(shù)據(jù) String rs = resultSet.getString(index); //將其放入列和值存放HashMap中 hashMap.put(index,rs); //將多個(gè)HashMap合并成一個(gè)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); } } //完成連接數(shù)據(jù)庫 stmt.close(); conn.close(); System.out.println("查詢連接結(jié)束"); } catch (ClassNotFoundException e) { e.printStackTrace(); }catch (SQLException e){ e.printStackTrace(); } return res; } /** * 將SQL語句進(jìn)行拆分 * @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組件進(jìn)行編寫SQL語句
?? 項(xiàng)目總結(jié)
在使用JDBC
來進(jìn)行數(shù)據(jù)庫的操作,在使用時(shí)由于對其方法并不是全部了解,所以需要邊看源碼邊改善功能,因此遇到問題時(shí)才會(huì)感覺到腦中知識儲備的不足,目前只是實(shí)現(xiàn)了查詢操作,還未完善CRUD
全部過程的操作。
到此這篇關(guān)于Java實(shí)現(xiàn)在線SQL編程的文章就介紹到這了,更多相關(guān)java SQL編程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring通過c3p0配置bean連接數(shù)據(jù)庫
這篇文章主要為大家詳細(xì)介紹了Spring通過c3p0配置bean連接數(shù)據(jù)庫,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08SpringCloud Feign隔離與降級詳細(xì)分析
Feign是Netflix公司開發(fā)的一個(gè)聲明式的REST調(diào)用客戶端; Ribbon負(fù)載均衡、 Hystrⅸ服務(wù)熔斷是我們Spring Cloud中進(jìn)行微服務(wù)開發(fā)非?;A(chǔ)的組件,在使用的過程中我們也發(fā)現(xiàn)它們一般都是同時(shí)出現(xiàn)的,而且配置也都非常相似2022-11-11springcloud使用feign調(diào)用服務(wù)時(shí)參數(shù)內(nèi)容過大問題
這篇文章主要介紹了springcloud使用feign調(diào)用服務(wù)時(shí)參數(shù)內(nèi)容過大問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03springboot整合xxl-job的實(shí)現(xiàn)示例
本文主要介紹了springboot整合xxl-job的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù)
這篇文章主要介紹了基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05