質量是產品的生命線,代碼檢查是軟體開發過程中至關重要的一環,它可以幫助我們發現並糾正潛在的錯誤,提高軟體質量,降低維護成本。 在袋鼠雲產品中也存在這個問題,由於離線數據開發人員 SQL 水平不一,導致代碼書寫混亂、SQL 代碼運行問題較多。本文將介紹在離線產品中如何利用 SQL 檢查規則規範化 SQ ...
質量是產品的生命線,代碼檢查是軟體開發過程中至關重要的一環,它可以幫助我們發現並糾正潛在的錯誤,提高軟體質量,降低維護成本。
在袋鼠雲產品中也存在這個問題,由於離線數據開發人員 SQL 水平不一,導致代碼書寫混亂、SQL 代碼運行問題較多。本文將介紹在離線產品中如何利用 SQL 檢查規則規範化 SQL 代碼,對代碼書寫問題進行攔截,便於統一管理,用於預防引入需要治理的問題。
通過本文的介紹,我們希望您能夠認識到代碼檢查的重要性,並瞭解如何通過最佳實踐來提高代碼質量和開發效率。
何時進行代碼規則檢查?
SQL 任務在離線產品界面開發完成之後,點擊運行的按鈕,會先經過代碼規則檢查,如果代碼規則不滿足則會提示到用戶具體的原因。
數據資產模塊內置了 5 種代碼檢查規則,用戶可以根據需要選擇性開啟。
開啟後在離線項目管理中可以選擇使用的代碼規則檢查項、生效範圍和 SQL 任務類型。
在離線 SQL 任務中去運行一條 SQL 前會根據選擇的規則先進行代碼檢查,如果代碼檢查不通過則會反饋給用戶,用戶可以根據實際需要判斷要不要執行該 SQL。
在數據資產的代碼檢查時間中可以看到已經觸發的檢查歷史以及相應的統計數據。
如何實現代碼檢查規則?
在 CodeCheck 包下定義了通用的代碼規則檢查的介面。
public interface ICheck {
Result codeCheck(String checkContent, String defaultDb, Integer dataSourceType, Long tenantId, SqlParseInfo sqlParseInfo);
CodeCheckType getCheckType();
}
以分區表查詢必須帶分區規則為例,會先調用 SQLParser 組件進行 SQL 解析,SQLParseInfo 即為 SQL 解析結果,檢查時會先判斷 SQL 語句是不是查詢語句,如果是則判斷查詢的表是不是分區表,再判斷是否有查詢條件,最後判斷查詢條件中是否包含分區欄位來判斷是否檢查通過。
public class CodeCheckImplType01 extends AbstractCheck {
private static final Logger LOGGER = LoggerFactory.getLogger(CodeCheckImplType01.class);
@Autowired
private DataTableColumnThirdService dataTableColumnThirdService;
@Autowired
private DataTableThirdService dataTableThirdService;
@Override
public Result codeCheck(String checkContent, String defaultDb, Integer dataSourceType, Long tenantId, SqlParseInfo sqlParseInfo) {
if (!isQuery(sqlParseInfo.getSqlType())) {
return Result.buildSuccessResult();
}
try {
MetadataSearchParam searchParam = new MetadataSearchParam();
searchParam.setDbName(sqlParseInfo.getMainTable().getDb());
searchParam.setTableName(sqlParseInfo.getMainTable().getName());
searchParam.setDataSourceType(dataSourceType);
searchParam.setTenantId(tenantId);
List<TableDTO> tableDTOS = dataTableThirdService.tableList(searchParam);
// 獲取表信息
for (TableDTO tableDTO : tableDTOS) {
List<DataTableColumn> tableColumns = dataTableColumnThirdService.listColumnByTableIds(Lists.newArrayList(tableDTO.getTableId()));
if (CollectionUtils.isEmpty(tableColumns)) {
continue;
}
List<String> partitionColumnNameList = tableColumns.stream()
.filter(Objects::nonNull)
.filter(t -> HavePartitionEnum.have_partition.getPartitionValue().equals(t.getIsPartition()))
.map(DataTableColumn::getColumnName)
.collect(Collectors.toList());
// 非分區表直接返回
if (CollectionUtils.isEmpty(partitionColumnNameList)) {
continue;
}
if (CollectionUtils.isEmpty(sqlParseInfo.getColumnIdentifierList())) {
// 沒有查詢條件則校驗失敗
return Result.buildFailedResult(String.format(getCheckType().getCheckResultFormat(), searchParam.getTableName()));
}
List<String> columnList = sqlParseInfo.getColumnIdentifierList().stream()
.filter(c -> StringUtils.equals(c.getDb(), searchParam.getSchemaName()) && StringUtils.equals(c.getTable(), searchParam.getTableName()))
.map(ColumnIdentifier::getColumn).collect(Collectors.toList());
boolean disjoint = Collections.disjoint(partitionColumnNameList, columnList);
if (disjoint) {
return Result.buildFailedResult(String.format(getCheckType().getCheckResultFormat(), searchParam.getTableName()));
}
}
} catch (Exception e) {
// 異常情況先通過
LOGGER.error("code check error, check content: {}, defaultDb: {}, checkType: {}", checkContent, defaultDb, getCheckType().name());
}
return Result.buildSuccessResult();
}
@Override
public CodeCheckType getCheckType() {
return CodeCheckType.TYPE_01;
}
}
如何自定義代碼檢查規則?
如果內置的代碼檢查規則不滿足客戶的使用場景,客戶可以通過上傳 jar 的方式自定義代碼檢查規則。
自定義代碼檢查規則使用 SPI 機制載入用戶上傳的自定義 jar,併在代碼檢測時調用 CodeCheck 方法,在資源關閉時調用 close 方法,用戶需要將配置文件說明中的 jar 依賴自己的項目中。具體如下:
● 創建一個類實現介面
創建一個類實現介面 com.dtstack.assets.spi.codecheck.ICodeCheckClient 並實現 CodeCheck 和 close 方法,書寫相關邏輯代碼,如果校驗通過需要將 CheckResult 對象中 success 設置為 true,失敗時設置 success 欄位為 false 並設置校驗不通過的理由。
package com.dtstack.assets.spi.codecheck;
import java.util.Map;
public interface ICodeCheckClient {
/**
* 代碼檢查
*
* @param checkContent 檢查內容
* @param extMap 擴展配置
* @return 檢查結果
*/
CheckResult codeCheck(String checkContent, Map<String, Object> extMap);
/**
* 釋放資源, 調用時需要關閉所使用的資源
*/
void close();
}
· 入參欄位解釋
– checkContent 為單條 SQL 信息
– extMap 會設置一些平臺的屬性,包含任務名稱、任務類型等
· 出參欄位解釋
– success 為是否校驗通過,必須設置
– checkResult 為校驗結果,校驗不通過時不能為空
package com.dtstack.demo;
import com.dtstack.assets.spi.codecheck.CheckResult;
import java.util.Map;
public class CodeCheckImpl implements com.dtstack.assets.spi.codecheck.ICodeCheckClient{
@Override
public CheckResult codeCheck(String checkContent, Map<String, Object> extMap) {
// 代碼檢查相關邏輯
CheckResult checkResult = new CheckResult();
checkResult.setSuccess(false);
checkResult.setCheckResult("校驗不通過的理由");
return checkResult;
}
@Override
public void close() {
// 關閉相關資源
}
}
● 在 resource 目錄下創建 META-INF/services 目錄
● 在 META-INF/services 目錄下創建文件
文件名稱為 com.dtstack.assets.spi.codecheck.ICodeCheckClient ,文件內容為實現 ICodeCheckClient 介面類的許可權定類名。
文件名稱和內容示例:
● 打包當前工程併在數據資產頁面註冊代碼校驗規則
不符合條件的 jar 會給出提示。
如何載入自定義代碼規則對應的 jar ?
我們會為上傳的每個規則對應的 jar 初始化一個唯一的自定義 classloader,該 classloader 繼承 URLClassLoader 並保證子類載入器優先載入。
在第一次調用時進行載入並緩存對應的 client。
在用戶重新上傳或者編輯規則後清除舊的 classloader 和載入的 client 並釋放資源。
《數棧產品白皮書》:https://www.dtstack.com/resources/1004?src=szsm
《數據治理行業實踐白皮書》下載地址:https://www.dtstack.com/resources/1001?src=szsm
想瞭解或咨詢更多有關袋鼠雲大數據產品、行業解決方案、客戶案例的朋友,瀏覽袋鼠雲官網:https://www.dtstack.com/?src=szbky
同時,歡迎對大數據開源項目有興趣的同學加入「袋鼠雲開源框架釘釘技術qun」,交流最新開源技術信息,qun號碼:30537511,項目地址:https://github.com/DTStack