Na empresa onde trabalho houve uma discussão sobre a possibilidade de passar um objeto Java para dentro de uma procedure ou function do Oracle, então resolvi pesquisar e aqui está uma forma bem simples de atingir este objetivo.
Este tutorial só funciona com a versão 9i do Oracle, ou superior, e usando o driver JDBC ojdbc14g, ou superior.
Primeiro precisaremos criar as tabelas, objetos e procedures. Lembrando que os tipos tbl_users e user_type deverão ser declarados como globais para funcionar, ou seja, fora de packages:
-- Criando a tabela
create table tbl_user(user_name varchar2(100), height number, b_date date);
/
--Criando o tipo user_type (nosso bean)
create or replace type user_type as object (user_name varchar2(100), height number, birth_date date);
/
--Criando o tipo arr_users, que é do tipo table of user_type (array de user_type)
create or replace type arr_users as table of user_type;
/
Criando o spec e body da package que conterá as procedures
--Spec
create or replace package PAC_BEAN is
-- REF CURSOR deve ser declarado dentro da package
type ref_cur is ref cursor;
-- Procedure usada para o insert
procedure pro_insert_user(usu in user_type);
-- Procedure usada para o select
procedure pro_select_user(usu in user_type, user_return in out arr_users);
end PAC_BEAN;
/
--Body
create or replace package body PAC_BEAN is
--A procedure de insert receberá o tipo user_type (nosso bean)
--e o cadastrará na tabela tbl_user
procedure pro_insert_user(usu in user_type) is
begin
insert into tbl_user (user_name, height, b_date)
values (usu.user_name, usu.height, usu.birth_date);
commit;
exception
when others then
rollback;
end pro_insert_user;
--Procedure de select que receberá um user_type com a informação do nome
--(cláusula where) e retornará o array arr_users (array de user_type)
procedure pro_select_user(usu in user_type, user_return in out arr_users)is
user_ref_cur ref_cur;
--Instanciando o array
users arr_users := arr_users();
begin
--Abrindo o cursor que retornará nosso array
open user_ref_cur for
select cast(
multiset(
select user_name,
height,
b_date
from tbl_user
where user_name like '%'||usu.user_name||'%'
) as arr_users
) arr
from dual;
--Jogando o retorno do cursor dentro da instância de arr_users
fetch user_ref_cur into users;
--Retornando a instância através da variável OUT
user_return := users;
end pro_select_user;
end PAC_BEAN;
/
Consulte sobre o funcionamento do CAST e MULTISET.
Construído os objetos de banco precisamos prepara o JavaBean. Ele será uma implementação de java.sql.SQLData, por isso será necessário implementar os métodos:
getSQLTypeName() – É o getter usado para obter o nome do tipo.
readSQL(SQLInput, String) – Usado para converter o objeto SQL em objeto Java.
writeSQL(SQLOutput stream) – Usado para montar o objeto SQL, usado pelo Driver JDBC.
public class TypeUser implements SQLData{
//O nome do tipo declarado no Oracle
public static final String ORACLE_OBJECT_NAME = "USER_TYPE";
//O nome do array declarado no Oracle
public static final String ORACLE_USER_ARRAY_NAME = "ARR_USERS";
//Os atributos
private String name;
private Float height;
private Date birth;
public TypeUser() {
height = 0F;
}
//Getter retorna o nome do tipo ao JDBC
public String getSQLTypeName() throws SQLException {
return ORACLE_OBJECT_NAME;
}
public void readSQL(SQLInput stream, String typeName) throws SQLException {
setName(stream.readString());
setHeight(stream.readFloat());
setBirth(stream.readDate());
}
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getName());
stream.writeFloat(getHeight());
stream.writeDate(getBirth() != null ?
new java.sql.Date(getBirth().getTime()) : null);
}
//getters e setters omitidos
}
Para definir que um tipo poderá ser enviado à procedure é necessário adicioná-lo ao mapa de tipos através de Connection.getTypeMap(). Este método retorna um Map<String,Class<?>>, onde a chave é o nome do tipo e o valor será a classe SQLData implementada, no nosso caso a TypeUser definida acima. Exemplo:
Map> typeMaps = connection.getTypeMap();
typeMaps.put(TypeUser.ORACLE_OBJECT_NAME, TypeUser.class);
A conexão ficará desta forma:
//Fazendo a conexão
Class.forName("oracle.jdbc.driver.OracleDriver");
connection = DriverManager.getConnection("jdbc:oracle:thin:@<host>:<porta>:<bd>","<usuario>","<senha>");
//Mapeando o tipo necessário
Map<String,Class<?>> typeMaps = connection.getTypeMap();
typeMaps.put(TypeUser.ORACLE_OBJECT_NAME, TypeUser.class);
Agora que temos a nossa conexão podemos fazer o método de insert, que receberá uma instância TypeUser e uma Connection:
CallableStatement cs = null;
try {
//chamando a procedure de insert
cs = conn.prepareCall("{call PAC_BEAN.PRO_INSERT_USER(?)}");
//definindo a instância de TypeUser como paramêtro "usu" da procedure
cs.setObject("usu", typeUser);
cs.execute();
} catch (SQLException e) {
e.printStackTrace();
}
Agora que temos o método de insert, partiremos para o método de select. Como este método retorna um array de objetos, é necessário inserir o tipo do array TypeMap da conexão. O nome passado como chave deve ser o nome do tipo do array no Oracle e o valor será a classe do array que esperamos, desta forma:
connection.getTypeMap().put(TypeUser.ORACLE_USER_ARRAY_NAME, TypeUser[].class);
Para a chamada da procedure e registerOutParameter:
cs = conn.prepareCall("{call PAC_BEAN.PRO_SELECT_USER(?,?)}");
cs.registerOutParameter("user_return", OracleTypes.ARRAY, TypeUser.ORACLE_USER_ARRAY_NAME);
cs.setObject("usu", typeUserQry);
cs.execute();
O método para recuperar o array deverá ser chamado desta forma:
//user_return é o nome da variável OUT da procedure
Object[] array = (Object[])cs.getArray("user_return").getArray();
Se até aqui deu tudo certo fique contente, para resgatar os valores do array é só iterá-lo e fazer cast para a classe TypeUser.
for(Object obj : array){
System.out.println("Nome: " + ((TypeUser)obj).getName());
System.out.println("Altura: " + ((TypeUser)obj).getHeight());
System.out.println("Data de Nascimento: " + sdf.format(((TypeUser)obj).getBirth()));
}
Desta forma poderemos passar e resgatar objetos simples de uma procedure/function do Oracle.
Próximo passo: Recuperando coleção de objetos de uma procedure Oracle.
Até lá!
Baixe o código fonte deste tutorial e da segunda parte aqui