項目中,有時候線下不能方便的連接項目中的數據源時刻,大部分的問題定位和處理都會存在難度,有時候,一個小工具就能實時的查詢和執行當前對應的數據源的庫.下面,就本人在項目中實際開發使用的小工具,實時的介紹開發使用過程.首先看圖:大概的操作界面,基本使用easyui組件實現,歡迎大家吐槽:
界面包含了基本的sql查詢 和 sql執行的小功能,把查詢和執行分開,也是為了后台實現的需要,以及權限控制的考慮,畢竟執行的操作,會影響到系統的數據問題.查詢和執行的菜單,是用easyui的手風琴式的菜單處理的.兩個菜單的界面都包含了執行按鈕和重置按鈕,輸入sql的文本區域,和數據源<哪些db需要操作>的tab分開展示,以及文本下方的執行結果展示信息.
首先看下查詢結果展示:
確定查詢,可以展示當前sql的查詢結果展示:結果采用分頁展示,表頭采用的是當前數據庫的表的字段名稱.
執行案例,就不展示圖片信息了.會在執行的下方提示:當前的sql執行成功的條數. 下面重點分享開發實現過程:
1,采用spring MVC的架構處理,首先來看下controller的處理類,基本的spring mvc的配置信息,在這就不多描述了. 本類主要包含了四個控制方法,
a,main方法,主要跳轉到工具管理主頁面.
b,query方法,主要是sql查詢的主方法,接收傳輸過來的sql語句,解析給交給數據庫執行
c,queryDetail方法,主要是查詢頁面的分頁查詢方法,點擊每一頁的查詢sql方法
d,excute方法,接收前台傳輸的執行sql,解析給數據庫執行
/** * 〈一句話功能簡述〉<br> * 〈功能詳細描述〉 * * @author lilin * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ @RequestMapping(value = "/tool") @Controller public class PmpTools { @Resource private IPmpToolService pmpToolService; /** * * 功能描述: <br> * 〈功能詳細描述〉工具管理主頁面 * * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @RequestMapping(value = "/main") public String main() { return "/system/tools/main.ftl"; } /** * * 功能描述: <br> * 〈功能詳細描述〉sql的查詢主方法 * * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @RequestMapping(value = "/query") public String query(HttpServletRequest request) { String findSql = request.getParameter("findSql"); String dataSource = request.getParameter("dataSource"); if (StringUtils.isNotEmpty(findSql) && StringUtils.isNotEmpty(dataSource)) { String pageSql = pmpToolService.getQuerySqlByPage(findSql, 1, 1); List<Map<String, Object>> list = pmpToolService.queryMapBySql(pageSql, dataSource); if (list != null && !list.isEmpty()) { request.setAttribute("column", list.get(0)); request.setAttribute("findSql", findSql); request.setAttribute("dataSource", dataSource); } else { request.setAttribute("errorMessage", "未查到數據!"); } } return "/system/tools/queryDetail.ftl"; } /** * * 功能描述: <br> * 〈功能詳細描述〉 * * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @ResponseBody @RequestMapping(value = "queryDetail") public Object queryDetail(HttpServletRequest request) { Map<String, Object> map = new HashMap<String, Object>(); String page = request.getParameter("page"); String rows = request.getParameter("rows"); int intPage = Integer.parseInt((page == null || page.equals("0")) ? "1" : page); int number = Integer.parseInt((rows == null || rows.equals("0")) ? "10" : rows); int start = (intPage - 1) * number + 1; String findSql = request.getParameter("findSql"); String dataSource = request.getParameter("dataSource"); String totalSql = null; List<Map<String, Object>> lists = null; int totals = 0; if (StringUtils.isNotEmpty(findSql)) { try { findSql = URLDecoder.decode(findSql, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("解析SQL報錯!"); } totalSql = pmpToolService.getTotalSql(findSql); totals = pmpToolService.queryTotalNumBySql(totalSql, dataSource); String pageSql = pmpToolService.getQuerySqlByPage(findSql, start, number); lists = pmpToolService.queryMapBySql(pageSql, dataSource); } map.put("rows", lists); map.put("total", totals); return map; } /** * * 功能描述: <br> * 〈功能詳細描述〉 * * @param request * @return * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ @ResponseBody @RequestMapping("excute") public Object excute(HttpServletRequest request) { String excuteSql = request.getParameter("excuteSql"); String dataSource = request.getParameter("dataSource"); String message = ""; Map<String, String> map = new HashMap<String, String>(); if (StringUtils.isNotEmpty(excuteSql)) { try { int flag = pmpToolService.exctueSql(excuteSql,dataSource); message = "執行成功: " + flag + "行。"; } catch (RuntimeException e) { message = "執行失敗:" + e.getMessage(); } } map.put("message", message); return map; } }
2,頁面主要是使用了freemaker+easyui的組件實現.主要頁面包含如下:
下面分享每個頁面的詳細設計實現:
a,main頁面:
<html> <head> <meta charset="UTF-8"> <title>工具頁面</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <link rel="shortcut icon" href="http://www.suning.com/favicon.ico" type="image/x-icon"/> <script type="text/javascript" src="${resRoot}/js/tool/tool.js"></script> <script type="text/javascript"> $(document).ready(function(){ $('#sqlSelect').tabs({ tools:'#tab-tools' }); $('#sqlExcute').tabs({ onSelect:tool.excuteTabSelect }); }); </script> </head> <body> <div id="p" class="easyui-panel" title="SQL小工具" style="width:1420px;height:620px;"> <div class="easyui-accordion" style="width:100%;height:560px;"> <div title="SQL<>查詢" style="overflow:auto;padding:10px;"> <div id="sqlSelect" style="width:99%;height:70px;"> <div title="WUPDB" style="text-align: center;"> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.query('uwpdb');"><span>執行</span></a> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.clear('findSql');"><span>重置</span></a> </div> <div title="PMPDB" style="text-align: center;"> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.query('pmpdb');"><span>執行</span></a> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.clear('findSql');"><span>重置</span></a> </div> </div> <textarea rows="5" name="findSql" id="findSql" style="width:1380px;" placeholder="[輸入查詢sql]" class="commInput"></textarea> <div id="detail" style="margin-top:4px;">暫無查詢結果</div> </div> <div title="SQL<>執行" style="overflow:auto;padding:10px;"> <div id="sqlExcute" style="width:99%;height:70px;"> <div title="WUPDB" style="text-align: center;"> <div> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.excute('uwpdb');"><span>執行</span></a> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.clear('excuteSql');"><span>重置</span></a> </div> </div> <div title="PMPDB" style="text-align: center;"> <div> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.excute('pmpdb');"><span>執行</span></a> <a href="javascript:void(0);" class="btn l mt10" onclick="tool.clear('excuteSql');"><span>重置</span></a> </div> </div> </div> <textarea rows="5" name="excuteSql" id="excuteSql" style="width:1380px;" placeholder="[輸入執行sql]" class="commInput"></textarea> <div id="excuteDetail" style="margin-top:4px;">暫無執行結果</div> </div> </div> </div> </body> </html>
b,queryDetail頁面如下: 主要采用了easyui的datagrid的組件:用於分頁查詢嵌入
<script type="text/javascript"> $(document).ready(function(){ var findSql = "${findSql}"; var dataSource = "${dataSource}"; findSql = encodeURI(findSql); $('#dataGrid').datagrid({ url: '${base}/tool/queryDetail.htm', method: 'post', title: '', width: 1382, height: 330, fitColumns: true, singleSelect: true, columns:[[ {field:'RN',title:'序號'}, <#if column??> <#list column?keys as col> {field:'${col}',title:'${col}'}, </#list> </#if> {field:'',title:'',hidden:true} ]], queryParams:{ findSql:findSql, dataSource : dataSource }, pagination:true, onLoadError:function(){ $.messager.alert('警告','查詢出錯!','error'); }, onClickRow: function(rowIndex, rowData){ $('#dataGrid').datagrid('unselectRow', rowIndex); } }); }); </script> <#if column??> <table id="dataGrid"></table> <#else> <p style="color: red;padding:10px">無數據</p> </#if>
3,頁面的js操作,全部提取出來了到tool.js中:主要是查詢和執行的方法:
Tool = function() { this.query = function(dataSource) { var findSql = $.trim($('#findSql').val()); if (!findSql) { $.messager.alert('警告', '請輸入查詢語句!', 'error'); return; } else if (findSql.substring(0, 6) != 'select') { $.messager.alert('警告', '查詢語句請以select開頭!', 'error'); return; } $.messager.confirm("操作提示", "您確定要執行操作嗎?", function(data) { if (data) { $.ajax({ type : "post", url : "${applicationName}/tool/query.htm", dataType : "html", async : true, data : { findSql : findSql, dataSource : dataSource }, success : function(html) { $("#detail").empty(); $("#detail").html(html); }, error : function() { $("#detail").html("查詢明細信息數據錯誤,請檢查sql語句!"); } }); } }); }; this.excute = function(dataSource) { var excuteSql = $.trim($('#excuteSql').val()); if (!excuteSql) { $.messager.alert('警告', '請輸入執行語句!', 'error'); return; } $.messager.confirm("操作提示", "您確定要執行操作嗎?", function(data) { if (data) { $.ajax({ type : "post", url : "${applicationName}/tool/excute.htm", dataType : "json", async : true, data : { excuteSql : excuteSql, dataSource : dataSource }, success : function(data) { $("#excuteDetail").empty(); $("#excuteDetail").html(data.message); }, error : function(e) { $("#excuteDetail").html("執行sql語句錯誤,請檢查sql語句!"); } }); } }); }; this.clear = function(textareaId) { $('#' + textareaId).val(''); }; this.queryTabSelect = function(title,index){ $("#detail").html('暫時么有查詢結果'); }; this.excuteTabSelect = function(title,index){ $("#excuteDetail").html('暫時么有執行結果'); }; }; var tool = new Tool();
4,service的類設計如下,接口就不做展示了,主要的方法展示:
/** * 〈一句話功能簡述〉<br> * 〈功能詳細描述〉 * * @author lilin * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ @Service public class PmpToolService implements IPmpToolService { @Resource private IPmpToolDao pmpToolDao; @Override public List<Map<String, Object>> queryMapBySql(String pageSql, String datasoure) { return pmpToolDao.findMapBySql(pageSql, datasoure); } @Override public int queryTotalNumBySql(String totalSql, String datasoure) { return pmpToolDao.queryTotalNumBySql(totalSql, datasoure); } @Override public int exctueSql(String sqlString, String datasource) { return pmpToolDao.exctueSql(sqlString, datasource); } @Override public String getQuerySqlByPage(String findSql, int start, int maxRows) { StringBuilder temp = new StringBuilder(); temp.append("SELECT * FROM ( SELECT ST.*, ROWNUMBER() OVER() AS RN FROM ("); temp.append(findSql); temp.append(") AS ST)AS PT WHERE PT.RN BETWEEN "); temp.append(start); temp.append(" AND "); temp.append(start + maxRows - 1); return temp.toString(); } @Override public String getTotalSql(String findSql) { String temp = ""; if (findSql.indexOf("order") == -1) { temp = findSql; } else { temp = findSql.substring(0, findSql.indexOf("order")); } return "select count(1) from (" + temp + ") as total"; } }
5,dao層的方法設計和service的類似,主要是連接數據源的操作:
/** * 〈一句話功能簡述〉<br> * 〈功能詳細描述〉 * * @author lilin * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ @Repository public class PmpToolDao extends CommonDao implements IPmpToolDao { private static final String TOOL_SQL = "tool.sql"; @Override public List<Map<String, Object>> findMapBySql(String pageSql, String datasource) { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("sql", pageSql); if ("uwpdb".equals(datasource)) { return getSoaDalClient().queryForList(TOOL_SQL, paramMap); } return getDalClient().queryForList(TOOL_SQL, paramMap); } @Override public int queryTotalNumBySql(String totalSql, String datasource) { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("sql", totalSql); if ("uwpdb".equals(datasource)) { return getSoaDalClient().queryForObject(TOOL_SQL, paramMap, Integer.class); } return getDalClient().queryForObject(TOOL_SQL, paramMap, Integer.class); } @Override public int exctueSql(String sqlString, String datasource) { Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put("sql", sqlString); if ("uwpdb".equals(datasource)) { return getSoaDalClient().execute(TOOL_SQL, paramMap); } return getDalClient().execute(TOOL_SQL, paramMap); } }
6,最后就是sqlMap文件的相關sql,本組件主要是前台接收sql執行查詢和執行操作,所以,sqlmap中的文件比較簡單:
<?xml version="1.0" encoding="UTF-8" ?>
<sqlMap namespace="tool">
<sql id="sql">
<![CDATA[
${sql}
]]>
</sql>
</sqlMap>
到此為止,一個簡單的sql查詢和執行小工具就完成了,可以方便的進行當前的db的簡單增刪改查操作了,只要數據源用戶的權限夠,也能執行相關的DDL操作.
有個比較棘手的問題就是:當前的庫的表中,有的字段信息存放的是xml格式的數據的時候,當前的組件展示會存在問題,不能正常的展示xml格式的文本數據,這個還在進一步的排查和解決中,希望有朋友能夠指點,一起交流.這個暫時沒有找尋到好的方法,之前打算采用字符替換的,也不能完全解決問題