Java使用JSqlParser解析SQL語句應(yīng)用場景
簡述
Java解析SQL語句有很多工具都可以做到,比如Mybatis、Druid、目前用來用去最全面的仍然是Jsqlparser,它是一個Github上的開源項目,JSqlParser是一個用于解析SQL語句的Java庫,它可以幫助開發(fā)者分析和操作SQL語句的結(jié)構(gòu)。無論是從事數(shù)據(jù)庫開發(fā)、SQL性能優(yōu)化,還是需要解析SQL語句以進行其他操作,JSqlParser都能提供強大的支持
特點
支持多種SQL方言:JSqlParser支持多種數(shù)據(jù)庫的SQL方言,包括MySQL、Oracle、PostgreSQL等,這使得在不同數(shù)據(jù)庫之間進行SQL語句解析變得更加方便。
靈活的操作:JSqlParser以多種方式操作SQL語句,例如修改查詢條件、提取表名、列名等,甚至整個SQL語句中使用到的函數(shù),從而滿足各種需求。
易于集成:JSqlParser可以輕松集成到您的Java項目中,只需將其作為依賴項添加到項目中即可。
社區(qū)支持:JSqlParser擁有一個活躍的社區(qū),許多開發(fā)者為其提供貢獻,使得這個工具不斷得到改進和優(yōu)化,它的主要操刀人manticore-projects (github.com) 也非常負責(zé)并愿意解答各種問題和參與討論
環(huán)境準備
將Jsqlparser直接添加到項目中
Maven:
<dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>4.9</version> </dependency>
Gradle:
implementation("com.github.jsqlparser:jsqlparser:4.9")
快速使用
使用原則
假設(shè)現(xiàn)在有一條簡單的SQL語句需要拿來解析,首先應(yīng)該保證這個SQL在結(jié)構(gòu)上是沒有問題的,最好是放在數(shù)據(jù)庫中可以直接運行的,不夾雜不應(yīng)該的標點符號,那樣解析起來才不會出錯
使用案例:
以下是一個簡單的SQL語句,并且這句SQL沒有違反原則,是一條基本可以正常運行的SQL語句
SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= "劉"
解析SQL語句
接下來使用Jsqlparser去解析語句,其中第二行則是最基本的,將SQL語句字符串拿來解析,如果這句SQL語句違反了原則,例如存在特殊標點符號或者不符合SQL語句,那么在第二行就會產(chǎn)生異常
String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '劉'"; // Parse SQL Statement statement = CCJSqlParserUtil.parse(sql); Select selectStatement = (Select) statement; log.info("==> JsqlParser SQL: {}", selectStatement.toString());
正常情況下,將得到一個包含各種屬性的statement,這意味著一條SQL成功被解析,并被賦予到一個對象的各個屬性中
認識Statement
熟悉JDBC的程序員一般都知道Statement,其實就是語句的意思,不過在Jsqlparser中Statement已經(jīng)面向?qū)ο螅辉O(shè)計成了一個interface,之所以設(shè)計成interface大概都可以猜到,因為Jsqlparser既然要去解析SQL,那必然要對SQL語句做區(qū)分,到底是Select、還是Insert、還是Delete、甚至是Create,而Jsqlparser對每種語句都做了一個封裝,它們都繼承了Statement
所以一條SQL語句,根據(jù)不同情況,都有適配的對象,例如Select語句對應(yīng)著 net.sf.jsqlparser.statement.select.Select對象,而Insert也有自己的對象,所以我們都可以通過將Statement強轉(zhuǎn)為它所對應(yīng)的對象來獲取或改變其中的屬性,這也是解析SQL的一大目的
其實在Jsqlparser成功解析SQL語句之后,statement就已經(jīng)有了它的類型
String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '劉'"; // Parse SQL Statement statement = CCJSqlParserUtil.parse(sql); if(statement instanceof Select){ Select selectStatement = (Select) statement; log.info("==> JsqlParser SQL: {}", selectStatement.toString()); } if(statement instanceof Insert){ Insert insertStatement = (Insert) statement; log.info("==> JsqlParser SQL: {}", insertStatement.toString()); } if(statement instanceof Update){ Update updateStatement = (Update) statement; log.info("==> JsqlParser SQL: {}", updateStatement.toString()); } if (statement instanceof Delete) { Delete deleteStatement = (Delete) statement; log.info("==> JsqlParser SQL: {}", statement.toString()); }
分析語句
查詢語句
在statement成功解析SQL語句之后,通過PlainSelect就可以拿到SQL語句中的各個元素
String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '劉'"; // Parse SQL Statement statement = CCJSqlParserUtil.parse(sql); if(statement instanceof Select){ Select selectStatement = (Select) statement; log.info("==> JsqlParser SQL: {}", selectStatement.toString()); PlainSelect plainSelect = selectStatement.getPlainSelect(); log.info("==> FromItem: {}", plainSelect.getFromItem()); log.info("==> SelectItem: {}",plainSelect.getSelectItems()); log.info("==> Where: {}",plainSelect.getWhere()); }
運行結(jié)果:
==> JsqlParser SQL: SELECT id, name, nickname, age, job, department FROM staff_member WHERE nickname = '劉'
==> FromItem: staff_member
==> SelectItem: [id, name, nickname, age, job, department]
==> Where: nickname = '劉'
PlainSelect常用方法:
- 獲取和設(shè)置表(From子句):
FromItem getFromItem()
: 獲取FROM子句中的表或子查詢。void setFromItem(FromItem fromItem)
: 設(shè)置FROM子句中的表或子查詢。
- 獲取和設(shè)置選擇項(SelectItems):
List<SelectItem> getSelectItems()
: 獲取SELECT子句中的選擇項列表。void setSelectItems(List<SelectItem> selectItems)
: 設(shè)置SELECT子句中的選擇項列表。
- 獲取和設(shè)置WHERE子句:
Expression getWhere()
: 獲取WHERE子句的條件表達式。void setWhere(Expression where)
: 設(shè)置WHERE子句的條件表達式。
- 獲取和設(shè)置GROUP BY子句:
List<Expression> getGroupByColumnReferences()
: 獲取GROUP BY子句中的列引用列表。void setGroupByColumnReferences(List<Expression> groupByColumnReferences)
: 設(shè)置GROUP BY子句中的列引用列表。
- 獲取和設(shè)置ORDER BY子句:
List<OrderByElement> getOrderByElements()
: 獲取ORDER BY子句中的排序元素列表。void setOrderByElements(List<OrderByElement> orderByElements)
: 設(shè)置ORDER BY子句中的排序元素列表。
- 獲取和設(shè)置LIMIT子句:
Limit getLimit()
: 獲取LIMIT子句。void setLimit(Limit limit)
: 設(shè)置LIMIT子句。
- 獲取和設(shè)置DISTINCT關(guān)鍵字:
boolean isDistinct()
: 檢查SELECT語句是否使用了DISTINCT關(guān)鍵字。void setDistinct(boolean distinct)
: 設(shè)置SELECT語句是否使用DISTINCT關(guān)鍵字。
- 獲取和設(shè)置INTO子句(用于SELECT INTO語句):
SubSelect getIntoTables()
: 獲取INTO子句中的表。void setIntoTables(SubSelect intoTables)
: 設(shè)置INTO子句中的表。
- 獲取和設(shè)置HAVING子句:
Expression getHaving()
: 獲取HAVING子句的條件表達式。void setHaving(Expression having)
: 設(shè)置HAVING子句的條件表達式。
- 獲取和設(shè)置別名:
String getAlias()
: 獲取SELECT語句的別名。void setAlias(String alias)
: 設(shè)置SELECT語句的別名。
- 獲取和設(shè)置子查詢(SubSelect):
SubSelect getSubSelect()
: 獲取子查詢。void setSubSelect(SubSelect subSelect)
: 設(shè)置子查詢。
- 獲取和設(shè)置聯(lián)合查詢(Union):
List<PlainSelect> getUnion()
: 獲取聯(lián)合查詢的SELECT語句列表。void setUnion(List<PlainSelect> union)
: 設(shè)置聯(lián)合查詢的SELECT語句列表。
新增語句
新增語句和查詢語句一樣,只不過由于Insert沒有Select語句那么復(fù)雜,所以Jsqlparsert并沒有專門設(shè)計一個類似PlainSelect extend Select這樣一個類,而是直接通過Insert對象就可以獲取和操作,Insert語句中的內(nèi)容
String sql = "INSERT INTO employees (employee_id, employee_name, department) VALUES (1, 'John Doe', 'Human Resources')"; // Parse SQL Statement statement = CCJSqlParserUtil.parse(sql); if (statement instanceof Insert) { Insert insertStatement = (Insert) statement; log.info("==> JsqlParser SQL: {}", insertStatement.toString()); log.info("==> Table: {}", insertStatement.getTable()); log.info("==> Columns: {}", insertStatement.getColumns()); log.info("==> ItemsList: {}", insertStatement.getValues()); }
運行結(jié)果:
==> JsqlParser SQL: INSERT INTO employees (employee_id, employee_name, department) VALUES (1, 'John Doe', 'Human Resources')
==> Table: employees
==> Columns: employee_id, employee_name, department
==> ItemsList: VALUES (1, 'John Doe', 'Human Resources')
Insert常用方法
Table getTable()
: 獲取插入語句中的目標表。List<Column> getColumns()
: 獲取插入語句中要插入的列的列表。ItemsList getValues()
: 獲取插入語句中的值列表,可以是單個值或者子查詢。String getPrefix()
: 獲取INSERT關(guān)鍵字前的前綴,如INSERT INTO
或者INSERT IGNORE
。void setTable(Table table)
: 設(shè)置插入語句中的目標表。void setColumns(List<Column> columns)
: 設(shè)置插入語句中要插入的列的列表。void setValues(ItemsList values)
: 設(shè)置插入語句中的值列表。void setPrefix(String prefix)
: 設(shè)置INSERT關(guān)鍵字前的前綴。
更新語句
Update和Insert是一樣的,內(nèi)容相對于Select較為簡單,通過Update對象即可獲得相關(guān)內(nèi)容
String sql = "UPDATE employees SET department = 'Human Resources' WHERE employee_id = 1"; // Parse SQL Statement statement = CCJSqlParserUtil.parse(sql); if (statement instanceof Update) { Update updateStatement = (Update) statement; log.info("==> JsqlParser SQL: {}", updateStatement.toString()); Table table = updateStatement.getTable(); log.info("Table Name: {}", table.getName()); log.info("==> Columns: {}", updateStatement.getColumns()); // 獲取更新項 List<UpdateSet> updateSets = updateStatement.getUpdateSets(); for (UpdateSet updateSet : updateSets) { for (Expression expression : updateSet.getColumns()) { log.info("==> Expression: {}", expression.toString()); } } log.info("==> ItemsList: {}", updateStatement.getExpressions()); Expression where = updateStatement.getWhere(); log.info("==> Where: {}", where.toString()); }
運行結(jié)果
==> JsqlParser SQL: UPDATE employees SET department = 'Human Resources' WHERE employee_id = 1
Table Name: employees
==> Columns: department
==> Expression: department
==> ItemsList: 'Human Resources'
==> Where: employee_id = 1
刪除語句
String sql = "DELETE FROM table_name WHERE condition"; Statement statement = CCJSqlParserUtil.parse(sql); if (statement instanceof Delete) { Delete deleteStatement = (Delete) statement; // 獲取要刪除的表 Table table = deleteStatement.getTable(); System.out.println("Table Name: " + table.getName()); // 獲取WHERE條件 Expression where = deleteStatement.getWhere(); System.out.println("Where Condition: " + where.toString()); }
運行結(jié)果:
Table Name: table_name
Where Condition: condition
從SQL語句中提取表名
Statement statement = CCJSqlParserUtil.parse("SELECT * FROM MY_TABLE1"); Select selectStatement = (Select) statement; TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); List<String> tableList = tablesNamesFinder.getTableList(selectStatement);
最終tableList里將存入所有給出的SQL語句中的表名,以上案例只有一個表名
為SQL語句各個字段表達式添加別名
String sql = "SELECT id,name,nickname,age,job,department FROM staff_member WHERE nickname= '劉'"; // Parse SQL Statement statement = CCJSqlParserUtil.parse(sql); if(statement instanceof Select ){ Select selectStatement = (Select) statement; final AddAliasesVisitor instance = new AddAliasesVisitor(); instance.setPrefix("t"); selectStatement.accept(instance); log.info("==> JSqlParser finalSQL: {}", selectStatement); }
動態(tài)加字段加表達式加條件
使用SelectUtils,為一個Select語句,增加查詢的字段
Select select = (Select) CCJSqlParserUtil.parse("select mydate from mytable"); SelectUtils.addExpression(select, new Column("mylocation"));
增加一個表達式
Select select = (Select) CCJSqlParserUtil.parse("select a from mytable"); SelectUtils.addExpression(select, new Column("b")); assertEquals("SELECT a, b FROM mytable", select.toString()); Addition add = new Addition(); add.setLeftExpression(new LongValue(5)); add.setRightExpression(new LongValue(6)); SelectUtils.addExpression(select, add); assertEquals("SELECT a, b, 5 + 6 FROM mytable", select.toString());
增加一個Join
動態(tài)添加Join,可以為Join增加表達式,以及設(shè)置Join的表,并且通過setLeft()、setRight()、setInner()可以設(shè)置join的方向,最終它會生成對應(yīng)的SQL語句
Select select = (Select) CCJSqlParserUtil.parse("select a from mytable"); final EqualsTo equalsTo = new EqualsTo(); equalsTo.setLeftExpression(new Column("a")); equalsTo.setRightExpression(new Column("b")); Join addJoin = SelectUtils.addJoin(select, new Table("mytable2"), equalsTo); addJoin.setLeft(true); assertEquals("SELECT a FROM mytable LEFT JOIN mytable2 ON a = b", select.toString());
用SelectUtils構(gòu)建一個SQL語句
下面是SelectUtils里面的一些方法,可以看到不光是為查詢語句增加表達式、Join和分組,其次還可以使用build等方法去構(gòu)建一個SQL語句
這里是一個案例,構(gòu)建了一個查詢語句,其中也使用到了addGroupBy
Select select = SelectUtils.buildSelectFromTableAndExpressions(new Table("mytable"), new Column("a"), new Column("b")); SelectUtils.addExpression(select, new Column("c")); final EqualsTo equalsTo = new EqualsTo(); equalsTo.setLeftExpression(new Column("id")); equalsTo.setRightExpression(new Column("1")); SelectUtils.addGroupBy(select, new Column("d")); log.info("==> JsqlParser Build SQL: {}", select.toString());
輸出結(jié)果:
==> JsqlParser Build SQL: SELECT a, b, c FROM mytable GROUP BY d
簡短的總結(jié)
上面的代碼雖然不少,但實際上真正需要熟悉的只有一個,就是直接調(diào)用CCJSqlParserUtil.parse(sql);去獲得Statement,然后通過Statement去操作和獲取解析后的SQL中的內(nèi)容,非常簡單方便
實際應(yīng)用場景
說了那么多JSQLPARSER的使用,或許很多朋友并不能聯(lián)想到有哪些具體可以用到它的地方,實際上想要開發(fā)一個優(yōu)秀的軟件產(chǎn)品,那么細節(jié)是少不了的,SQL是BS軟件的本質(zhì)之一,那么針對SQL,我們能做的還有很多,以下列舉幾個常見的場景
SQL審計和分析:
審計SQL語句,檢查是否包含潛在的安全漏洞,如SQL注入。分析SQL語句的性能,檢查是否存在可以優(yōu)化的查詢條件。數(shù)據(jù)庫遷移和同步:
在遷移數(shù)據(jù)庫時,使用JSqlParser解析源數(shù)據(jù)庫的SQL語句,并生成目標數(shù)據(jù)庫的相應(yīng)語句。數(shù)據(jù)庫同步工具可以使用JSqlParser來解析和生成SQL語句,以實現(xiàn)數(shù)據(jù)的同步。動態(tài)SQL生成:
應(yīng)用程序需要生成動態(tài)SQL語句以執(zhí)行不同的操作,JSqlParser可以用來解析這些動態(tài)生成的SQL語句。SQL測試和驗證:
在開發(fā)過程中,使用JSqlParser來驗證SQL語句的正確性。單元測試中,使用JSqlParser來解析和執(zhí)行測試用例中的SQL語句。SQL注入防護:
在應(yīng)用程序中,使用JSqlParser來解析和分析用戶輸入的SQL查詢,以防止SQL注入攻擊。數(shù)據(jù)庫管理工具:
數(shù)據(jù)庫管理工具可以使用JSqlParser來解析和顯示SQL語句的結(jié)構(gòu),幫助開發(fā)者理解查詢的邏輯。代碼生成:
在生成數(shù)據(jù)庫訪問層代碼時,使用JSqlParser來解析SQL語句,并生成相應(yīng)的數(shù)據(jù)訪問對象(DAO)或查詢對象(DTO)。SQL格式化:
使用JSqlParser來格式化SQL語句,使其更易于閱讀和理解。SQL優(yōu)化:
通過分析SQL語句的結(jié)構(gòu),可以提出性能優(yōu)化建議。數(shù)據(jù)處理工具:
在數(shù)據(jù)處理和轉(zhuǎn)換工具中,使用JSqlParser來解析和生成SQL語句,以實現(xiàn)數(shù)據(jù)的導(dǎo)入和導(dǎo)出。
在Springboot+Mybaits中使用
如果使用純原生Mybatis那么我們需要手動在maven中加入Jsqlparser的支持,但如果使用Mybatis plus,那么就無需自己再引用,Mybaits plus自帶Jsqlparser
上面舉的很多例子都很簡單,拿一個SQL語句解析而已,這種情況是手動化的,通常見于單元測試等情況,但如果在項目中想要通過被動的方式,讓項目自己去解析SQL語句,就需要分析項目的具體情況,例如在Mybatis中通過Interceptor就可以獲得到項目中真正去執(zhí)行的SQL語句,詳見:mybatis 自定義實現(xiàn)攔截器插件Interceptor示例
通過Mybatis的攔截器,我們拿到了項目執(zhí)行的SQL語句,再通過Jsqlparser去解析,并做一定的處理,例如以上提到的那些實際應(yīng)用場景
高級特性(很實用)
Jsqlparser在解析SQL語句的過程中,每一個節(jié)點都會被解析成一個叫SimpleNode的對象,它包含著各個節(jié)點的屬性,這仿佛就像Dom4j解析XML的時候所有的元素都視為Node一樣,解析之后的內(nèi)容都是節(jié)點,而循環(huán)這些節(jié)點,Jsqlparser給出了相應(yīng)的方法,提供了用于遍歷節(jié)點的接口CCJSqlParserVisitor,而它的默認實現(xiàn)則是CCJSqlParserDefaultVisitor,在這里創(chuàng)建一個自己的類,并通過繼承 CCJSqlParserDefaultVisitor 重寫它的 visit 方法,便可以實現(xiàn)自己的策略,更加方便的去操作解析內(nèi)容
public class SQLModifier extends CCJSqlParserDefaultVisitor { @Override public Object visit(SimpleNode node, Object data) { Object value = node.jjtGetValue(); switch (node.getId()) { case CCJSqlParserTreeConstants.JJTTABLENAME: break; case CCJSqlParserTreeConstants.JJTCOLUMN: break; case CCJSqlParserTreeConstants.JJTFUNCTION: break; default: break; } return super.visit(node, data); } }
調(diào)用自定義的Visitor
String originalSql = "select * from user where id = 1"; CCJSqlParser parser = CCJSqlParserUtil.newParser(originalSql); Statement statement = parser.Statement(); parser.getASTRoot().jjtAccept(sqlTestModifier, null);
以上代碼做了一個自定義的visitor,重寫的visit方法中可以看到形參SimpleNode,而調(diào)用這個自定義的Visitor之后,語句則會被拆解,依次進入到visit方法中,通過node.jjtGetValue可以獲得節(jié)點信息,而node.getId()實則是獲取節(jié)點的類型,而Switch-case中的常量分別代表了在解析SQL語句時,生成的抽象語法樹AST (abstract syntax tree)中不同類型的節(jié)點,每個節(jié)點對應(yīng)一個特定的SQL構(gòu)造,如SELECT、FROM、WHERE等。下面是對這些常量代表的SQL構(gòu)造的簡要說明:
- JJTSTATEMENT: 代表一個SQL語句。
- JJTVOID: 可能代表一個空語句或者不返回結(jié)果的語句。
- JJTBLOCK: 代表一個語句塊,可能包含多個語句。
- JJTSTATEMENTS: 代表一個包含多個語句的列表。
- JJTCOLUMN: 代表一個列名。
- JJTTABLENAME: 代表一個表名。
- JJTSELECT: 代表一個SELECT查詢。
- JJTPARENTHESEDSELECT: 代表被括號包圍的SELECT查詢。
- JJTLATERALVIEW: 代表LATERAL VIEW子句,常用于Hive SQL。
- JJTFORCLAUSE: 代表FOR子句。
- JJTLATERALSUBSELECT: 代表LATERAL子查詢。
- JJTPLAINSELECT: 代表一個簡單的SELECT查詢(不包含UNION等)。
- JJTSETOPERATIONLIST: 代表一個集合操作列表,比如UNION, EXCEPT, INTERSECT。
- JJTWITHITEM: 代表WITH子句中的單個項。
- JJTSELECTITEM: 代表SELECT子句中的一個項,可能是列名、表達式等。
- JJTJOINEREXPRESSION: 代表JOIN操作的表達式。
- JJTLIMITWITHOFFSET: 代表LIMIT和OFFSET子句。
- JJTPLAINLIMIT: 代表一個簡單的LIMIT子句。
- JJTEXPRESSION: 代表一個表達式。
- JJTREGULARCONDITION: 代表一個常規(guī)條件(如WHERE子句中的條件)。
- JJTINEXPRESSION: 代表IN表達式。
- JJTLIKEEXPRESSION: 代表LIKE表達式。
- JJTSIMILARTOEXPRESSION: 代表SIMILAR TO表達式。
- JJTISDISTINCTEXPRESSION: 代表IS DISTINCT FROM表達式。
- JJTEXPRESSIONLIST: 代表一個表達式列表。
- JJTPRIMARYEXPRESSION: 代表一個主要表達式。
- JJTCONNECTBYROOTOPERATOR: 代表CONNECT BY ROOT操作符。
- JJTCASEWHENEXPRESSION: 代表CASE WHEN表達式。
- JJTFUNCTION: 代表一個函數(shù)調(diào)用。
- JJTSEQUENCE: 代表一個序列。
- JJTSYNONYM: 代表一個同義詞。
Visit常見應(yīng)用場景
目前我們知道,通過Mybatis 的 interceptor可以攔截到所有執(zhí)行的SQL語句,而在 自定義的interceptor中調(diào)用自定義的visit,就可以對項目中所有運行的SQL做一個攔截并處理,那么具體可以做哪些騷操作呢
- SQL語句重寫: 在某些數(shù)據(jù)庫系統(tǒng)中,為了優(yōu)化性能或滿足特定的需求,可能需要重寫SQL語句。通過自定義訪問者,可以在AST(abstract syntax tree)層面進行這些操作
- 元數(shù)據(jù)提取: 自定義訪問者可以用來提取SQL語句中的元數(shù)據(jù),比如查詢涉及的所有表名、列名、函數(shù)等,這些信息可以用于構(gòu)建數(shù)據(jù)庫的概要圖或進行數(shù)據(jù)治理。
- 數(shù)據(jù)屏蔽: 在需要對敏感數(shù)據(jù)進行屏蔽的應(yīng)用中,可以通過自定義訪問者來識別并修改涉及敏感數(shù)據(jù)的查詢,以確保在查詢結(jié)果中不會暴露敏感信息。
- 動態(tài)查詢構(gòu)建: 在需要動態(tài)構(gòu)建SQL查詢的應(yīng)用中,可以通過自定義訪問者來解析模板SQL語句,并根據(jù)實際參數(shù)動態(tài)替換模板中的占位符,從而構(gòu)建出完整的SQL語句。
- 緩存策略決策: 根據(jù)SQL查詢的特征,可以通過自定義訪問者來判斷查詢結(jié)果是否適合緩存,以及應(yīng)該使用什么樣的緩存策略。
總結(jié)
Jsqlparser非常容易上手使用,而它也解決了解析SQL語句的問題,結(jié)合Springboot 和 mybatis,可以設(shè)計自定義插件,就像Mybatis plus的分頁插件那樣,可以開發(fā)自己系統(tǒng)需求的業(yè)務(wù)處理功能,方便項目業(yè)務(wù)的時間,甚至可以拿來提高效率,畢竟總有一些時候,對SQL的解析是繞不開的。
到此這篇關(guān)于Java使用JSqlParser解析SQL語句總結(jié)的文章就介紹到這了,更多相關(guān)Java使用JSqlParser內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Maven 搭建 Spring MVC 本地部署Tomcat的詳細教程
這篇文章主要介紹了使用Maven 搭建 Spring MVC 本地部署Tomcat,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08復(fù)雜JSON字符串轉(zhuǎn)換為Java嵌套對象的實現(xiàn)
這篇文章主要介紹了復(fù)雜JSON字符串轉(zhuǎn)換為Java嵌套對象的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09Spring Boot構(gòu)建優(yōu)雅的RESTful接口過程詳解
這篇文章主要介紹了spring boot構(gòu)建優(yōu)雅的RESTful接口過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08關(guān)于java String中intern的深入講解
這篇文章主要給大家介紹了關(guān)于java String中intern的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04