Introdução ao SQLExpr
O SQLExpr no Druid é a interface raiz para todas as representações de expresões SQL. Ele encapsula desde campos simples até subconsultas complexas, condições e operações.
Hierarquia de Herança do SQLExpr
A estrutura de classes segue um padrão onde SQLExpr serve como base para todos os tipos de expressões:
SQLObject
└── SQLExpr
├── SQLIdentifierExpr // Identificadores como colunas
├── SQLIntegerExpr, SQLCharExpr, SQLNullExpr, SQLDecimalExpr // Literais
├── SQLBinaryOpExpr // Operações binárias (ex: =, >, AND)
├── SQLUnaryExpr // Operações unárias (ex: NOT, -)
├── SQLInExpr // Listagem IN
├── SQLInSubQueryExpr // IN com subconsulta
├── SQLExistsExpr // Cláusula EXISTS
├── SQLAnyExpr, SQLAllExpr // Quantificadores ANY/ALL
├── SQLBetweenExpr // BETWEEN
├── SQLMethodInvokeExpr // Chamadas de funções
├── SQLCaseExpr // Expressões CASE
├── SQLArrayExpr // Arrays
├── SQLVariantRefExpr // Variáveis ou placeholders
└── SQLPropertyExpr // Propriedades com prefixo (ex: alias.campo)
Subclasses Principais e Exemplos
SQLIdentifierExpr
Representa nomes de campos ou tabelas. Exemplo básico:
SQLIdentifierExpr campoId = new SQLIdentifierExpr("user_id");
Expressões Literais
SQLIntegerExpr numero = new SQLIntegerExpr(42);
SQLCharExpr texto = new SQLCharExpr("Exemplo");
SQLNullExpr nulo = SQLNullExpr.INSTANCE;
SQLDecimalExpr decimal = new SQLDecimalExpr("3.14");
SQLBinaryOpExpr
Usado para operações como comparações e lógica. Exemplo alterado:
SQLBinaryOpExpr condicao = new SQLBinaryOpExpr(
new SQLIdentifierExpr("idade"),
SQLBinaryOperator.GreaterThanOrEquals,
new SQLIntegerExpr(25)
);
SQLUnaryExpr
Operações unárias como negação:
SQLUnaryExpr negacao = new SQLUnaryExpr(
SQLUnaryOperator.Not,
new SQLIdentifierExpr("ativo")
);
SQLInExpr
Expressão IN com lista de valores:
SQLInExpr listaIn = new SQLInExpr();
listaIn.setExpr(new SQLIdentifierExpr("categoria"));
listaIn.addItem(new SQLCharExpr("A"));
listaIn.addItem(new SQLCharExpr("B"));
SQLInSubQueryExpr
IN com subconsulta interna. Atributos: expr (campo esquerdo) e subQuery (subconsulta).
SQLInSubQueryExpr inSubConsulta = new SQLInSubQueryExpr();
inSubConsulta.setExpr(new SQLIdentifierExpr("produto_id"));
inSubConsulta.setSubQuery(consultaFilha); // Objeto SQLSelect representando a subconsulta
SQLExistsExpr
Representa EXISTS ou NOT EXISTS. O atributo not controla a negação.
SQLExistsExpr existe = new SQLExistsExpr();
existe.setSubQuery(outraConsulta);
existe.setNot(true); // NOT EXISTS
Outras Subclasses
Exemplos simplificados para variedade:
// SQLAnyExpr: idade > ANY (SELECT idade FROM usuarios)
SQLAnyExpr anyExpr = new SQLAnyExpr(consultaUsuarios);
// SQLBetweenExpr: salario BETWEEN 3000 AND 8000
SQLBetweenExpr entre = new SQLBetweenExpr(
new SQLIdentifierExpr("salario"),
new SQLIntegerExpr(3000),
new SQLIntegerExpr(8000)
);
// SQLMethodInvokeExpr: SUM(vendas)
SQLMethodInvokeExpr soma = new SQLMethodInvokeExpr("SUM");
soma.addParam(new SQLIdentifierExpr("vendas"));
// SQLCaseExpr: CASE WHEN status='ativo' THEN 'Sim' ELSE 'Não' END
SQLCaseExpr caso = new SQLCaseExpr();
caso.addWhenNode(
SQLUtils.toSQLExpr("status='ativo'"),
new SQLCharExpr("Sim")
);
caso.setElseExpr(new SQLCharExpr("Não"));
SQLPropertyExpr
Campo com prefixo de tabela ou alias. Demonstração com análise de SQL:
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.*;
import com.alibaba.druid.sql.ast.statement.*;
import com.alibaba.druid.util.JdbcConstants;
public class AnalisadorPropriedades {
public static void main(String[] args) {
String sql = "SELECT t.codigo, t.descricao, preco FROM produtos t WHERE t.id = 5 AND ativo = 1";
SQLStatement declaracao = SQLUtils.parseSingleStatement(sql, JdbcConstants.MYSQL);
SQLSelectStatement selectDecl = (SQLSelectStatement) declaracao;
SQLSelectQueryBlock bloco = selectDecl.getSelect().getQueryBlock();
System.out.println("Campos no SELECT:");
for (SQLSelectItem item : bloco.getSelectList()) {
extrairCampo(item.getExpr());
}
System.out.println("\nCondições no WHERE:");
SQLExpr ondeExpr = bloco.getWhere();
if (ondeExpr instanceof SQLBinaryOpExpr) {
percorrerBinario((SQLBinaryOpExpr) ondeExpr);
}
}
private static void extrairCampo(SQLExpr expr) {
if (expr instanceof SQLPropertyExpr) {
SQLPropertyExpr prop = (SQLPropertyExpr) expr;
System.out.println("Campo com prefixo: " + prop.getOwnerName() + "." + prop.getName());
} else if (expr instanceof SQLIdentifierExpr) {
SQLIdentifierExpr id = (SQLIdentifierExpr) expr;
System.out.println("Campo simples: " + id.getName());
}
}
private static void percorrerBinario(SQLBinaryOpExpr binario) {
if (binario.getLeft() instanceof SQLBinaryOpExpr) {
percorrerBinario((SQLBinaryOpExpr) binario.getLeft());
} else {
extrairCampo(binario.getLeft());
}
if (binario.getRight() instanceof SQLBinaryOpExpr) {
percorrerBinario((SQLBinaryOpExpr) binario.getRight());
} else {
extrairCampo(binario.getRight());
}
}
}
Saída esperada:
Campo com prefixo: t.codigo
Campo com prefixo: t.descricao
Campo simples: preco
Campo com prefixo: t.id
Campo simples: ativo
Exemplos Avançados
Parse de SQLInSubQueryExpr
String consulta = "SELECT * FROM pedidos WHERE cliente_id IN (SELECT id FROM clientes WHERE ativo=1)";
SQLStatement decl = SQLUtils.parseSingleStatement(consulta, JdbcConstants.MYSQL);
SQLSelectQueryBlock bloco = ((SQLSelectStatement) decl).getSelect().getQueryBlock();
SQLExpr condicao = bloco.getWhere();
if (condicao instanceof SQLInSubQueryExpr) {
SQLInSubQueryExpr inSub = (SQLInSubQueryExpr) condicao;
System.out.println("Campo esquerdo: " + inSub.getExpr());
System.out.println("Subconsulta: " + inSub.getSubQuery());
}
Parse de SQLExistsExpr
String sqlExists = "SELECT * FROM funcionarios WHERE EXISTS (SELECT 1 FROM projetos WHERE projetos.lider = funcionarios.id)";
SQLStatement stmtExists = SQLUtils.parseSingleStatement(sqlExists, JdbcConstants.MYSQL);
SQLExpr exprWhere = ((SQLSelectStatement) stmtExists).getSelect().getQueryBlock().getWhere();
if (exprWhere instanceof SQLExistsExpr) {
SQLExistsExpr existeExpr = (SQLExistsExpr) exprWhere;
System.out.println("Subconsulta: " + existeExpr.getSubQuery());
System.out.println("Negado: " + existeExpr.isNot());
}
Construção de Condições Complexas
SQLExpr condA = SQLUtils.toSQLExpr("departamento IN (10,20)");
SQLExpr condB = SQLUtils.toSQLExpr("EXISTS (SELECT 1 FROM avaliacoes WHERE avaliacoes.func = funcionario.id)");
SQLExpr condicaoCompleta = SQLBinaryOpExpr.and(condA, condB);
System.out.println("Condição gerada: " + SQLUtils.toSQLString(condicaoCompleta));
Verificação de Tipo de Expressão
Um método utilitário para identificar o tipo de uma expressão:
public static void classificarExpressao(SQLExpr expr) {
if (expr instanceof SQLIdentifierExpr) {
System.out.println("Tipo: Identificador");
} else if (expr instanceof SQLInSubQueryExpr) {
System.out.println("Tipo: IN com Subconsulta");
} else if (expr instanceof SQLExistsExpr) {
System.out.println("Tipo: EXISTS");
} else if (expr instanceof SQLInExpr) {
System.out.println("Tipo: IN com Lista");
} else if (expr instanceof SQLBinaryOpExpr) {
System.out.println("Tipo: Operação Binária");
} else if (expr instanceof SQLSubqueryExpr) {
System.out.println("Tipo: Subconsulta Genérica");
} else if (expr instanceof SQLCaseExpr) {
System.out.println("Tipo: Expressão CASE");
} else {
System.out.println("Tipo Desconhecido: " + expr.getClass().getSimpleName());
}
}