O problema de ter tanta configuração em XML é que ele sequer costuma diminuir a quantidade de código. Costuma haver, isso sim, uma troca. Troca-se Java por XML. Ou SQL por XML. Qual será a vantagem de trocar uma linguagem com tantos controles por algo que só é verificado em tempo de execução?
Pois, eu argumento que as consultas são melhor escritas em SQL e colocadas em procedures. E meu argumento principal é o de que isso torna muito mais fácil a vida do DBA. Estando as consultas em procedures e functions, ele pode facilmente encontrar e corrigir problemas de desempenho. Além disso, as consultas podem ser compartilhadas por diferentes sistemas; dados costumam viver mais que os sistemas que os utilizam (e raramente um banco de dados é usado por apenas uma aplicação).
Tendo tudo isso em mente, fiquei muito feliz quando Java finalmente adotou métodos com número variável de parâmetros (varargs) e autoboxing. Com a JDK 1.5, finalmente foi possível escrever uma interface realmente simples e direta para invocar procedures e functions. Para resolver um problema, é muito melhor criar uma boa abstração do que iludir-se com XML.
Considere as duas classes abstratas abaixo:
public abstract class Procedure {
public abstract void call(Object... args)
throws SQLException, IOException;
public abstract void close();
public String getString(int columnIndex) throws SQLException {
return getCallable().getString(columnIndex);
}
public String getString(String columnName) throws SQLException {
return getCallable().getString(columnName);
}
//demais gets omitidos
}
public abstract class Function {
public abstract Object call(Object... args) throws SQLException;
public abstract void close();
//gets omitidos
}
Eu não usei interfaces, porque os gets não dependem da implementação específica e, portanto, podem ser codificados na superclasse e aproveitadas na implementação para cada banco de dados.
O que eu almejei foi poder escrever algo como:
Procedure p=null;
try {
//BUSCAR_PESSOA_PROC(
// P_CODIGO IN NUMBER,
// P_CURSOR OUT SYS_REFCURSOR
//);
p=new OracleProcedure("java:comp/env/jdbc/data",
"PESSOAS_PKG.BUSCAR_PESSOA_PROC");
p.call(123, null);
ResultSet rs=p.getCursor(1);
while(rs.next()) {
//percorrer cursor
}
} catch(Exception e) {
e.printStackTrace();
} finally {
p.close();
}
O construtor de OracleProcedure recebe dois parâmetros:
- O nome de uma datasource;
- O nome de uma procedure (com ou sem package);
Para o método call(), passo os parâmetros exigidos pela procedure. Como o segundo parâmetro é de saída (OUT), uso null naquela posição. As instâncias de Procedure e Function têm todos os gets de java.sql.CallableStatement, de tal sorte que é possível pegar os parâmetros de saída sem dificuldades.
Na primeira vez que uma procedure é invocada, OracleProcedure (OracleFunction faz o mesmo) recupera os metadados (tipos dos parâmetros) e os guarda num Map, para não ter que repetir esse passo. Em seguida, o código monta a consulta (neste caso, "{call PESSOAS_PKG.BUSCAR_PESSOA_PROC(?,?)}") e também a guarda. Um CallableStatement é criado, os parâmetros são atribuídos (é fácil escolher o setXXX() adequado com base no tipo do parâmetro) e a consulta, finalmente, é executada.
Nas primeiras versões dessas classes, eu também guardava a referência à datasource, para evitar ter que fazer outra pesquisa JNDI. Desisti de fazer isso porque as implementações do JNDI já são rápidas o suficiente para tornar a economia de tempo desprezível. Além disso, era impossível alterar ou reiniciar a datasource sem reiniciar a aplicação.
Com isso, tenho uma maneira simples de invocar procedures e functions. As camadas em Java ficam bastante mais simples e livres de XML. O código de acesso a dados fica restrito ao banco e pode ser compartilhado com outras aplicações.
Tu tem essas classes implementadas prontas pra uso? De repente eu ponho elas em uso no que eu estou fazendo aqui.
ResponderExcluir