Java實(shí)現(xiàn)在線SQL編程最新完整版
前言: 由于前段時(shí)間,項(xiàng)目組長(zhǎng)分配的任務(wù)是要完成一個(gè)在線編寫SQL并要實(shí)現(xiàn)查詢功能的需求,最終需要將查詢到的數(shù)據(jù)以JSON格式顯示到響應(yīng)數(shù)據(jù)的區(qū)域,以供操作者進(jìn)行查看,一開(kāi)始拿到需求時(shí)想著直接使用Mybatis進(jìn)行操作不就可以了,完全沒(méi)必要大費(fèi)周章,因?yàn)樵?code>MyBatis中有個(gè)拼接SQL的語(yǔ)法,可以使用${sql}來(lái)進(jìn)行執(zhí)行輸入的SQL語(yǔ)句,但是實(shí)際操作起來(lái)并不是想象中的那么簡(jiǎn)單,因?yàn)槭褂?code>MyBatis會(huì)將數(shù)據(jù)源固定在本項(xiàng)目所使用的數(shù)據(jù)庫(kù),而不可以進(jìn)行數(shù)據(jù)源之間的切換,無(wú)法進(jìn)行其他數(shù)據(jù)源中表的查詢操作 ?,所以在實(shí)現(xiàn)過(guò)程也是相當(dāng)艱難曲折...
?? 難度分析
在線執(zhí)行SQL語(yǔ)句的查詢主要的難點(diǎn)分為以下幾點(diǎn):其一則是對(duì)JDBC的部分的API理解的不夠透徹,導(dǎo)致在實(shí)現(xiàn)某些工鞥是并不是想象中的順利;其二在于對(duì)于查詢部分屬性的SQL語(yǔ)句該如何使用Java進(jìn)行實(shí)現(xiàn),是將輸入的字符串進(jìn)行分割再拼接還是使用直接整條語(yǔ)句的查詢操作;其三則是用戶可以動(dòng)態(tài)的切換數(shù)據(jù)源,并且對(duì)相應(yīng)數(shù)據(jù)源下的表進(jìn)行查詢操作,如果使用Mybatis進(jìn)行SQL的查詢操作則無(wú)法進(jìn)行數(shù)據(jù)源的切換,只能查詢所在微服務(wù)項(xiàng)目所連接的數(shù)據(jù)庫(kù)進(jìn)行查詢,否則無(wú)法進(jìn)行相應(yīng)的操作,即會(huì)出現(xiàn)該數(shù)據(jù)庫(kù)下并不存在所查詢的某張數(shù)據(jù)庫(kù)表的錯(cuò)誤信息 ?
?? 項(xiàng)目回顧(在線編寫SQL查詢)
?? 最終效果演示

?? 技術(shù)選型:
- ?? SpringBoot
- ?? MyBatis-Plus
- ?? JDBC
- ?? Vue
- ?? 代碼編輯器monaco-editor
?? 需求分析
使用者在頁(yè)面可以選擇切換需要操作的數(shù)據(jù)源,并在編輯區(qū)域內(nèi)輸入SQL語(yǔ)句進(jìn)行查詢,查詢語(yǔ)句可以是全表查詢表中所有字段或者根據(jù)所需查詢指定某幾列字段對(duì)應(yīng)的值,點(diǎn)擊查詢之后即可在響應(yīng)數(shù)據(jù)之后以JSON的格式將查詢到的每一條數(shù)據(jù)封裝一個(gè)對(duì)象最后顯示到響應(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í)則對(duì)查詢到的數(shù)據(jù)進(jìn)行遍歷操作
getString(String columnLabel)

解釋: 根據(jù)表中對(duì)應(yīng)的列名稱來(lái)查詢到該列對(duì)應(yīng)到的數(shù)據(jù)
getMetaData()

解釋: 獲取某張表中列的數(shù)量、類型和列名稱
findColumn(String columnLabel)

解釋: 返回某個(gè)列的索引值即下標(biāo)
⑤ 獲取所有的數(shù)據(jù)源
因?yàn)槭褂谜呤强梢詣?dòng)態(tài)的選擇數(shù)據(jù)源從而來(lái)對(duì)表中的數(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í),由于無(wú)法得知需要查詢的表中有什么字段,所以首先需要對(duì)輸入的SQL字符串進(jìn)行判斷是否是全字段查詢,然后即可獲取查詢表中的所有字段,然后再一一的進(jìn)行查詢出字段對(duì)應(yīng)的值即可,此操作也是需要對(duì)輸入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ù)列名稱查詢出該列對(duì)應(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í)需要對(duì)輸入的SQL字符串進(jìn)行分割后將所需要查詢到的字段再使用JDBC中的getString(String columnName)進(jìn)行查詢字段對(duì)應(yīng)的值
/**
* 將SQL語(yǔ)句進(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)過(guò)對(duì)操作者輸入的SQL即可完成對(duì)表的查詢操作,由于所要實(shí)現(xiàn)的是可動(dòng)態(tài)切換數(shù)據(jù)源從而進(jìn)行相關(guān)的查詢操作,所以在此操作邏輯中首先需要連接數(shù)據(jù)庫(kù),后再對(duì)輸入的SQL進(jìn)行分割查詢等操作。合并后的完整代碼:
/**
* 連接數(shù)據(jù)庫(kù)并根據(jù)輸入的SQL語(yǔ)句查詢數(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ù)庫(kù)-----------");
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ù)列名稱查詢出該列對(duì)應(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ù)庫(kù)
stmt.close();
conn.close();
System.out.println("查詢連接結(jié)束");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e){
e.printStackTrace();
}
return res;
}
/**
* 將SQL語(yǔ)句進(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語(yǔ)句

?? 項(xiàng)目總結(jié)
在使用JDBC來(lái)進(jìn)行數(shù)據(jù)庫(kù)的操作,在使用時(shí)由于對(duì)其方法并不是全部了解,所以需要邊看源碼邊改善功能,因此遇到問(wèn)題時(shí)才會(huì)感覺(jué)到腦中知識(shí)儲(chǔ)備的不足,目前只是實(shí)現(xiàn)了查詢操作,還未完善CRUD全部過(guò)程的操作。
到此這篇關(guān)于Java實(shí)現(xiàn)在線SQL編程的文章就介紹到這了,更多相關(guān)java SQL編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring通過(guò)c3p0配置bean連接數(shù)據(jù)庫(kù)
這篇文章主要為大家詳細(xì)介紹了Spring通過(guò)c3p0配置bean連接數(shù)據(jù)庫(kù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
SpringCloud Feign隔離與降級(jí)詳細(xì)分析
Feign是Netflix公司開(kāi)發(fā)的一個(gè)聲明式的REST調(diào)用客戶端; Ribbon負(fù)載均衡、 Hystrⅸ服務(wù)熔斷是我們Spring Cloud中進(jìn)行微服務(wù)開(kāi)發(fā)非?;A(chǔ)的組件,在使用的過(guò)程中我們也發(fā)現(xiàn)它們一般都是同時(shí)出現(xiàn)的,而且配置也都非常相似2022-11-11
springcloud使用feign調(diào)用服務(wù)時(shí)參數(shù)內(nèi)容過(guò)大問(wèn)題
這篇文章主要介紹了springcloud使用feign調(diào)用服務(wù)時(shí)參數(shù)內(nèi)容過(guò)大問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
springboot整合x(chóng)xl-job的實(shí)現(xiàn)示例
本文主要介紹了springboot整合x(chóng)xl-job的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
解決Maven中的依賴導(dǎo)包問(wèn)題(組合技巧)
自從我開(kāi)始接觸了以spring為框架的項(xiàng)目學(xué)習(xí)后,這個(gè)maven導(dǎo)包老是出現(xiàn)問(wèn)題,每次在這個(gè)上面花費(fèi)好多時(shí)間,于是乎打算寫一個(gè)秘籍出來(lái),這篇文章主要介紹了解決Maven中的依賴導(dǎo)包問(wèn)題,需要的朋友可以參考下2023-11-11
基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù)
這篇文章主要介紹了基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05

