Hệ thống SQLASTVisitor hoàn chỉnh
I. Kiến trúc cấp cao nhất (Druid 1.2.x)
SQLASTVisitor (interface định nghĩa tất cả phương thức visit)
↑ Implements
SQLASTVisitorAdapter (adapter trừu tượng: empty implementation, class cha quan trọng nhất)
↑ Extends
├─ SQLASTOutputVisitor → Xuất SQL string
│ ├─ SQLFormatVisitor → Định dạng SQL
│ └─ SQLParameterizedOutputVisitor → Thay thế bằng tham số
│
├─ SchemaStatVisitor → Trích xuất bảng/cột/điều kiện/hàm (được dùng nhiều nhất)
│ ├─ MySqlSchemaStatVisitor
│ ├─ OracleSchemaStatVisitor
│ └─ ...các dialect khác
│
├─ SQLWallVisitor → Phòng chống SQL injection, kiểm tra bảo mật
│ └─ MySqlWallVisitor etc.
│
└─ Visitor tùy chỉnh của bạn (kế thừa SQLASTVisitorAdapter)
1. SQLASTVisitor (Interface)
Package: com.alibaba.druid.sql.visitor.SQLASTVisitor
Công dụng: Định nghĩa tất cả các phương thức để duyệt qua AST:
visit(SQLSelectQueryBlock)visit(SQLExprTableSource)visit(SQLJoinTableSource)visit(SQLSelectItem)visit(SQLWhereClause)visit(SQLBinaryOpExpr)- ... hàng trăm phương thức
Bạn sẽ không bao giờ implement trực tiếp interface này!
2. SQLASTVisitorAdapter (Class quan trọng nhất!)
Package: com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter
Vị trí: Class cha thực sự của tất cả Visitor = Adapter với empty implementation = Bắt buộc phải extend khi viết Visitor tùy chỉnh
Đặc điểm:
- Implements
SQLASTVisitor - Tất cả phương thức default return true (tiếp tục duyệt các node con)
- Bạn chỉ cần override những node quan tâm
II. Các class con quan trọng
1. SchemaStatVisitor (Được dùng nhiều nhất)
Công dụng
Trích xuất từ SQL:
- Tất cả bảng
- Tất cả cột
- Tất cả điều kiện
- Tất cả hàm
- Data lineage
Tên class thực tế
SchemaStatVisitor
↑
MySqlSchemaStatVisitor (được sử dụng thực tế)
Thuộc tính quan trọng
Map<String, TableStat> tables; // Bảng
Set<Column> columns; // Cột
List<Condition> conditions; // Điều kiện
Set<String> functions; // Hàm
Set<String> variables; // Biến
Phương thức quan trọng
visitor.getTables() // Lấy tất cả bảng
visitor.getColumns() // Lấy tất cả cột
visitor.getConditions() // Lấy điều kiện WHERE/JOIN
visitor.getFunctions() // Lấy hàm
Ví dụ hoàn chỉnh
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.util.JdbcConstants;
public class StatVisitorDemo {
public static void main(String[] args) {
String sql = "SELECT u.id, u.username FROM users u JOIN department d ON u.dept_id=d.id WHERE u.age>20";
SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
stmt.accept(visitor);
// Bảng
System.out.println("=== Tables ===");
for (TableStat.Name name : visitor.getTables().keySet()) {
System.out.println(name);
}
// Cột
System.out.println("\n=== Columns ===");
visitor.getColumns().forEach(System.out::println);
// Điều kiện
System.out.println("\n=== Conditions ===");
visitor.getConditions().forEach(System.out::println);
}
}
2. SQLASTOutputVisitor (Xuất SQL)
Công dụng
Chuyển AST thành chuỗi SQL = Implementation bên dưới của SQLUtils.toSQLString()
Class cha
SQLASTVisitorAdapter
↑
SQLASTOutputVisitor
Thuộc tính quan trọng
protected StringBuilder appender;
protected String dbType;
protected boolean prettyFormat;
Phương thức quan trọng
visit(...) // Duyệt và xuất
print(String) // Xuất chuỗi
println() // Xuống dòng
Ví dụ
String sql = "select * from employees";
SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
StringBuilder out = new StringBuilder();
SQLASTOutputVisitor visitor = new SQLASTOutputVisitor(out, JdbcConstants.MYSQL);
stmt.accept(visitor);
System.out.println(out);
// SELECT * FROM employees
3. SQLFormatVisitor (Định dạng SQL)
Công dụng
SQL美化, thụt lề, viết hoa keyword
Kế thừa
SQLASTOutputVisitor
↑
SQLFormatVisitor
Phương thức quan trọng
setUpperCase(boolean) // Viết hoa keyword
setIndent(int) // Thiết lập thụt lề
Ví dụ
String sql = "select id,name from employees where salary>5000";
SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
StringBuilder sb = new StringBuilder();
SQLFormatVisitor visitor = new SQLFormatVisitor(sb, JdbcConstants.MYSQL);
visitor.setUpperCase(true);
visitor.setIndent(4);
stmt.accept(visitor);
System.out.println(sb);
Kết quả:
SELECT
id,
name
FROM employees
WHERE salary > 5000
4. SQLParameterizedOutputVisitor (Tham số hóa)
Công dụng
Chuyển giá trị thành ?
salary>5000 → salary>?
Ví dụ
String sql = "SELECT * FROM employees WHERE age>25 AND department_id=3";
SQLStatement stmt = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
StringBuilder sb = new StringBuilder();
SQLParameterizedOutputVisitor visitor = new SQLParameterizedOutputVisitor(sb, JdbcConstants.MYSQL);
stmt.accept(visitor);
System.out.println(sb);
Kết quả:
SELECT * FROM employees WHERE age>? AND department_id=?
5. SQLWallVisitor (Bảo mật SQL)
Công dụng
Kiểm tra SQL có chứa:
- DROP
- TRUNCATE
- SQL injection độc hại
- Vi phạm quyền
Ví dụ
SQLWallVisitor visitor = new MySqlWallVisitor();
stmt.accept(visitor);
if (visitor.getViolations().isEmpty()) {
System.out.println("An toàn");
} else {
System.out.println("Vi phạm: " + visitor.getViolations());
}
III. Tạo Visitor tùy chỉnh (Kế thừa SQLASTVisitorAdapter)
Bạn có thể lắng nghe:
- Bảng
- Cột
- WHERE
- JOIN
- Hàm
- ORDER BY
- LIMIT
- Subquery
Ví dụ: In tất cả bảng
import com.alibaba.druid.sql.ast.statement.SQLExprTableSource;
import com.alibaba.druid.sql.visitor.SQLASTVisitorAdapter;
public class TableLoggerVisitor extends SQLASTVisitorAdapter {
@Override
public boolean visit(SQLExprTableSource x) {
System.out.println("Bảng: " + x.getExpr() + " | Alias: " + x.getAlias());
return true;
}
}
Sử dụng:
stmt.accept(new TableLoggerVisitor());
IV. Các node thường được lắng nghe nhất
// Bảng
@Override
public boolean visit(SQLExprTableSource x) { }
// JOIN
@Override
public boolean visit(SQLJoinTableSource x) { }
// Subquery
@Override
public boolean visit(SQLSubqueryTableSource x) { }
// Trường trong SELECT
@Override
public boolean visit(SQLSelectItem x) { }
// WHERE
@Override
public boolean visit(SQLWhereClause x) { }
// Biểu thức so sánh a=b, a>b
@Override
public boolean visit(SQLBinaryOpExpr x) { }
// Hàm
@Override
public boolean visit(SQLMethodInvokeExpr x) { }
// ORDER BY
@Override
public boolean visit(SQLOrderBy x) { }
// LIMIT
@Override
public boolean visit(SQLLimit x) { }
V. Tổng kết
1. Kiến trúc thực tế
SQLASTVisitor (interface)
↑
SQLASTVisitorAdapter (adapter bạn phải kế thừa)
↑
├─ SchemaStatVisitor Trích xuất bảng/cột/điều kiện
├─ SQLASTOutputVisitor Xuất SQL
├─ SQLFormatVisitor Định dạng
├─ SQLParameterizedVisitor Tham số hóa
└─ SQLWallVisitor Chống injection
2. Sử dụng trong 90% cases
- Trích xuất bảng/cột →
SchemaStatVisitor - Duyệt/rewrite tùy chỉnh → Kế thừa
SQLASTVisitorAdapter - Xuất SQL →
SQLASTOutputVisitor - Định dạng →
SQLFormatVisitor - Chống injection →
SQLWallVisitor