sybn sybn-util 项目说明文档 - 基于java的跨数据库联合查询

代码分析工具

2023-06-04
sybn

痛点

历史积累代码量巨大, 上千张表, 上千条 sql 和 上百万行的 java 代码, 交接维护的压力比较大.

如果要查询: 某某功能使用了哪些sql, 或者那些表被谁使用了.

当某些接口后面依赖了十几个甚至几十个不同的 sql, 层层嵌套非常复杂.

利用 APM 可以解决部分问题, 但是有部分场景无法覆盖.

思路

将 sql, xml, java 代码. 通过 ast 语法树工具 (spoon 或 druid 等), 转化为语法树对象.

然后导出和分析他们之间的依赖关系.

比如: sql 可以分解为如下格式.

image

java 分析:

  • 代码解析

可以直接解析 java src 目录 或者 xxxx-sources.jar 源码包

SpoonClassParser parser = new SpoonClassParser();
List<SpoonFolder> resources = new ArrayList<>();
resources.add(new FileSystemFolder(new File("C:\\java\\work\\git_oristar\\tradingcenterplatform\\csm\\csm-controller\\src\\main\\java")));
resources.add(new FileSystemFolder(new File("C:\\java\\work\\git_oristar\\tradingcenterplatform\\csm\\csm-api\\src\\main\\java")));
resources.add(new FileSystemFolder(new File("C:\\java\\work\\git_oristar\\tradingcenterplatform\\csm\\csm-service\\src\\main\\java")));
resources.add(new FileSystemFolder(new File("C:\\java\\work\\git_oristar\\tradingcenterplatform\\csm\\csm-common\\src\\main\\java")));
resources.add(new FileSystemFolder(new File("C:\\java\\work\\git_oristar\\tradingcenterplatform\\csm\\csm-report-api\\src\\main\\java")));
resources.add(new ZipFolder(new File("C:\\Users\\linpengfei\\.m2\\repository\\com\\oristartech\\ori-common-mybatis\\4.0.0-RELEASE\\ori-common-mybatis-4.0.0-RELEASE-sources.jar")));
JavaCodeInfo javaCodeInfo =parser.loadJavaCode(resources).trimCalleeMap();

技术细节:

使用 spoon 解析 java 文件, 从中提取方法信息及过滤依赖关系.

使用 mybatis 的 DocumentBuilderFactory 解析 xml 文件, 导出方法名及其 sql 代码.

不同的项目需要不同的过滤策略.

  • 直接输出调用关系到 excel
方法名 上级调用 下级引用 url (controller才有) sql (mybatis xml才有) 表名 (从 sql 中提取) urls (mybatis xml才有)
com.oristartech.service.impl.SellGoodsWaresApiServiceImpl#selectSellsGoodWaresTend(RestRequest) [“com.oristartech.controller.sellgoods.SellGoodsWaresController#downloadSellGoodsSaleTend(ChartDataSearchParam,HttpServletResponse,HttpServletRequest)”,”com.oristartech.controller.sellgoods.SellGoodsWaresController#sellsGoodWaresTend(RestRequest,HttpServletRequest)”] [“com.oristartech.facade.model.indicator.IndicatorSellGoodsWares#setDateKey(String)”,”com.oristartech.service.impl.SellGoodsWaresApiServiceImpl#getSellTrendList()”,”com.oristartech.facade.vo.sellgoods.ChartWaresDataCollection#setIndicatorSellGoodsWares(List)”,”com.oristartech.service.impl.sellgoods.SellGoodsWaresServiceImpl#selectWithTable(SellGoodsDataSearchParam)”,”com.oristartech.service.impl.sellgoods.SellGoodsWaresServiceImpl#selectWithTableTend(SellGoodsDataSearchParam)”,”map#get()”,”com.oristartech.service.impl.SellGoodsWaresApiServiceImpl#buildSellGoodTrendSearchByDateType(ChartDataSearchParam)”,”com.oristartech.facade.model.indicator.IndicatorSellGoodsWares#setDateKey()”] /bigscreen/cardmember select … from sch_plan_960732 where … sch_plan_960732 当前sql被哪些接口调用

数据剪枝:

java 代码中部分调用关系没有分析价值, 在分析时会对其进行清理从而简化输出, 比如:

横向, 默认会减除 java 类库, spring 类库, apache 类库等等工具包, 以及对象 get set 方法 等内容,

纵向, 默认会将: “调用者>接口类>接口实现类” 三层关系简化为: “调用者>接口实现类” 两层关系

  • 在线可视化

地址:

https://biadmindev.oristarcloud.com/dw-api-core/js/plugin/viz/viz_java.html?target=com.oristartech.csm.fa.controller.TimeExtractFundController%23findExtractUsersByPage(String%2CInteger%2CInteger%2CString%2CString%2CString)&action=all&project=csm&exclude=&showPackage=true&showArgs=true

说明:

双击任意元素即可追踪该元素

image

  • 接口 与 xml 关系

导出每个接口依赖了哪些 mybatis xml.

url conroller 注释 sql1 sql2
/kanban/overview com.oristartech.controller.kanban.KanbanController#overView(RestRequest,HttpServletRequest) 实时大屏 <pre>KanbanTicketsMapper.xml
com.oristartech.dao.kanban.
KanbanTicketsDao#selectSumInfoDay
<select id=”selectSumInfoDay”>
SELECT SUM(brbod.box_office) AS box_office,…
</select></pre>
<pre>KanbanSellGoodsMapper.xml
com.oristartech.dao.kanban.
KanbanSellGoodDao#selectSumInfoDay
<select id=”selectSumInfoDay”>
SELECT SUM(brsgd.sales_volume) AS sales_volume,…
</select></pre>
  • xml 与 接口 关系

导出每个 mybatis xml 属于哪些接口.

xml tables url
<pre>KanbanTicketsMapper.xml
com.oristartech.dao.kanban.
KanbanTicketsDao#selectSumInfoDay
<select id=”selectSumInfoDay”>
SELECT SUM(brbod.box_office) AS box_office,..
</select></pre>
[“bi_cinema_org_info”,”bi_real_box_office_data”] /bigscreen/todayamount
/bigscreen/regionalAnalysis
/bigscreen/cardmember
/bigscreen/ticketTop5
/bigscreen/getCinema
/bigscreen/hotGoods
/bigscreen/hourTrade
/bigscreen/ticketchannel
/bigscreen/ticketOrder

sql 分析:

  • sql 注释

测试地址(3.0业务):

https://biadmindev.oristarcloud.com/dw-api-core/sql_translate.html?db_catalog=cx3#p_comment_sql

测试地址(4.0业务) 如果表重名, 需要指定数据库信息:

https://biadmindev.oristarcloud.com/dw-api-core/sql_translate.html?db_source=cx_schema#p_comment_sql

image

输入:

select pla.UID_CINEMA AS uidCinema,
    count(pla.PLAN_ID) as saleTimeNum,
    sum(hal.SEATNUM - hal.BAD_SEAT_COUNT ) as saleSeatNum
from sch_plan_321566 pla  
left join ci_hall hal on pla.UID_HALL =hal.UID 
where pla.SALE_STATUS = 'SALE' and pla.UID_CINEMA = ?
    and SHOW_TIME_START >= ?
    and SHOW_TIME_START <  ?
group by 1

添加注释:

select pla.UID_CINEMA /* 影院UID */ AS uidCinema,
    count(pla.PLAN_ID /* 主键ID */) as saleTimeNum,
    sum(hal.SEATNUM /* 座位数 */ - hal.BAD_SEAT_COUNT /* 坏座数 */ ) as saleSeatNum
from sch_plan_321566 /* 表:放映计划 */ pla  
left join ci_hall /* 表:影厅表 */ hal on pla.UID_HALL /* 影厅UID */ =hal.UID /*  */ 
where pla.SALE_STATUS /* 销售状态.SALE可售、NOT_SALE不可售、STOP暂停 */ = 'SALE' and pla.UID_CINEMA /* 影院UID */ = ?
    and SHOW_TIME_START /* 放映开始时间YYYY-MM-DD */ >= ?
    and SHOW_TIME_START <  ?
group by 1

添加注释并替换表名:

select sch_plan_321566.UID_CINEMA /* 影院UID */ AS uidCinema,
    count(sch_plan_321566.PLAN_ID /* 主键ID */) as saleTimeNum,
    sum(ci_hall.SEATNUM /* 影厅座位数 */ - ci_hall.BAD_SEAT_COUNT ) as saleSeatNum
from sch_plan_321566 /* 表:放映计划 */ pla  
left join ci_hall hal on sch_plan_321566.UID_HALL /* 影厅UID */ =hal.UID /*  */ 
where sch_plan_321566.SALE_STATUS /* 销售状态.SALE可售、NOT_SALE不可售、STOP暂停 */ = 'SALE' and sch_plan_321566.UID_CINEMA /* 影院UID */ = ?
    and SHOW_TIME_START /* 放映开始时间YYYY-MM-DD */ >= ?
    and SHOW_TIME_START <  ?
group by 1

技术细节:

提前加载表结构信息到数据库(表面,字段名,注释等), 解析sql语句提取字段名表名, 并为其添加注释.

  • 处理参数列表

输入:

[2023-04-21 14:28:30] : [DEBUG]  - SQL: select * from a in (?,?,?) Parameters: [null,2,3a]

输出(左侧输出):

select * from a in (NULL,2,'3a]');
  • sql 可视化

支持解析 sql 和大部分 mybatis xml.

测试地址(3.0业务):

https://biadmindev.oristarcloud.com/dw-api-core/js/plugin/viz/viz_join.html?table_catalog=cx3

测试地址(4.0业务):

https://biadmindev.oristarcloud.com/dw-api-core/js/plugin/viz/viz_join.html?db_source=cx_schema

输入:

select pla.UID_CINEMA AS uidCinema,
    count(pla.PLAN_ID) as saleTimeNum,
    sum(hal.SEATNUM - hal.BAD_SEAT_COUNT ) as saleSeatNum
from sch_plan_321566 pla  
left join ci_hall hal on pla.UID_HALL =hal.UID 
where pla.SALE_STATUS = 'SALE' and pla.UID_CINEMA = ?
    and SHOW_TIME_START >= ?
    and SHOW_TIME_START <  ?
group by 1

输出:

image

技术细节:

提前加载表结构信息到数据库(表面,字段名,注释等), 解析sql语句提取字段名表名 和 join 关系, 使用 viz 出图.

  • 近似表

一个很常见的问题是, 因为分库分表的原因, 当前SQL的表后缀(比如: SCH_PLAN_123456)在预置表结构数据库中不存在.

在分析代码中加上 “#近似表” 即可自动适配前缀相同数字后缀近似的表名(比如: SCH_PLAN_999056).

#近似表
select pla.UID_CINEMA AS uidCinema,
    count(pla.PLAN_ID) as saleTimeNum,
    sum(hal.SEATNUM - hal.BAD_SEAT_COUNT ) as saleSeatNum
from sch_plan_123456 pla  
left join ci_hall hal on pla.UID_HALL =hal.UID 
where pla.SALE_STATUS = 'SALE' and pla.UID_CINEMA = ?
    and SHOW_TIME_START >= ?
    and SHOW_TIME_START <  ?
group by 1
  • 变量表

一个很常见的问题是, 因为分库分表的原因, mybatis 的 sql 中不显示完成表名, 而是使用了部分变量(比如: SCH_PLAN_${tenant_id})

在分析代码中加上 “#补全$表” 即可自动适配前缀相同数字后缀近似的表名(比如: SCH_PLAN_999056).

#补全$表
select pla.UID_CINEMA AS uidCinema,
    count(pla.PLAN_ID) as saleTimeNum,
    sum(hal.SEATNUM - hal.BAD_SEAT_COUNT ) as saleSeatNum
from sch_plan_${tenant_id} pla  
left join ci_hall hal on pla.UID_HALL =hal.UID 
where pla.SALE_STATUS = 'SALE' and pla.UID_CINEMA = ?
    and SHOW_TIME_START >= ?
    and SHOW_TIME_START <  ?
group by 1

Similar Posts

上一篇 近期更新

下一篇 RedisDao 介绍

Comments

暂不开放评论! 可微信联系