Tag: Oracle

Tutorial iBatis, aprendendo o básico

Geralmente quando se fala sobre frameworks de persistência logo vem à cabeça Hibernate/JPA. Há pouco tempo fui apresentado ao iBatis, um framework que eu, particularmente, acho muito fácil de instalar, configurar e usar. Você pode baixa-lo através do site de sua mantenedora, a Apache, clicando aqui.

Neste tutorial usarei a versão 2.3.4 build 726.

Configurando o iBatis

Ao contrário dos demais frameworks, para configurar o iBatis você só precisa de um arquivo XML de configuração, chamado SqlMapConfig.

As principais seções do xml são:

<properties resource="tuto/ibatis/config/SqlMap.properties"/>

Este código é opcional e serve para indicar o arquivo properties com as variável de configuração que serão usada no XML.

<typeAlias alias="car" type="tuto.ibatis.beans.Car"/>

Indica o Bean utilizado e qual será seu aliás. Você pode configurar várias linhas, tudo depende da complexidade e necessidade da sua modelagem.
No nosso exemplo, usaremos o bean Car abaixo:

public class Car {
	private Long carId;
	private String company;
	private String model;
	private String color;
	private Integer	hp;
	private Float price;

	//Setters e getters omitidos
}
<transactionManager type="JDBC">
    <dataSource type="SIMPLE">
        <property name="JDBC.Driver" value="${driver}"/>
        <property name="JDBC.ConnectionURL" value="${url}"/>
        <property name="JDBC.Username" value="${username}"/>
        <property name="JDBC.Password" value="${password}"/>
    </dataSource>
</transactionManager>

Parâmetros usados na conexão com o banco. As variáveis ${driver}, ${url}, ${username} e ${password} estão definidas no arquivo .properties indicado na seção  properties. Se você preferir, também pode colocar os valores diretamente nos campos, sem precisar defini-los em outro local.

Veja como será arquivo completo:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
        "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
    <properties resource="tuto/ibatis/config/SqlMap.properties"/>

    <settings
        cacheModelsEnabled="true"
        enhancementEnabled="true"
        lazyLoadingEnabled="true"
        maxRequests="32"
        maxSessions="10"
        maxTransactions="5"
        useStatementNamespaces="false" />

    <typeAlias alias="car" type="tuto.ibatis.beans.Car"/>

    <transactionManager type="JDBC">
        <dataSource type="SIMPLE">
            <property name="JDBC.Driver" value="${driver}"/>
            <property name="JDBC.ConnectionURL" value="${url}"/>
            <property name="JDBC.Username" value="${username}"/>
            <property name="JDBC.Password" value="${password}"/>
        </dataSource>
    </transactionManager>

    <sqlMap resource="tuto/ibatis/sqlmaps/CarSqlMap.xml"/>
</sqlMapConfig>

O properties tem o seguinte conteúdo:

driver=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@<host>:<porta>:<sid>
username=<login>
password=<senha>

Em seguida devemos configurar nosso SqlMap. Este XML conterá as querys utilizadas na aplicação e deverá ter o nome do descrito na seçao sqlMap do SqlMapConfig, no nosso caso será CarSqlMap.xml.

No nosso exemplo apenas veremos a utilização das tags select, insert, update e delete.

<select id="getCars" resultClass="tuto.ibatis.beans.Car"
	parameterClass="java.lang.Long">
    SELECT COMPANY  as company,
           MODEL    as model,
           COLOR    as color,
           HP       as hp,
           PRICE    as price
    FROM TBL_CAR
    WHERE CAR_ID = #var#
</select>

Executa o select podendo retornar uma única linha ou uma coleção, o tipo retornado é o mesmo especificado no atributo resultClass, o parameterClass é o tipo passado para executar a query e o id é a identificação para chamada da query.

Usaremos o SqlMap abaixo:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
	"http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Car">
    <select id="getCars" resultClass="tuto.ibatis.beans.Car"
    parameterClass="java.lang.Long">
        SELECT COMPANY  as company,
               MODEL    as model,
               COLOR    as color,
               HP       as hp,
               PRICE    as price
        FROM TBL_CAR
        WHERE CAR_ID = #var#
    </select>

    <insert id="addCar" parameterClass="tuto.ibatis.beans.Car">
        INSERT INTO TBL_CAR (CAR_ID, COMPANY, MODEL, COLOR, HP, PRICE)
        VALUES (#carId#, #company#, #model#, #color#, #hp#, #price#)
    </insert>

    <delete id="delCar" parameterClass="java.lang.Long">
        DELETE FROM TBL_CAR WHERE CAR_ID = #var#
    </delete>

    <update id="updCar" parameterClass="tuto.ibatis.beans.Car">
        UPDATE TBL_CAR
          SET COMPANY = #company#,
              MODEL = #model#,
              COLOR = #color#,
              HP = #hp#,
              PRICE = #price#
        WHERE CAR_ID = #carId#
    </update>
</sqlMap>

Agora que já configuramos o acesso ao banco de dados e o mapeamento dos objetos, vamos implementar a classe singleton que usaremos como SqlMapClient, aqui nós a chamaremos de OracleMapConfig.

package tuto.ibatis.connection;

import java.io.Reader;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;

public class OracleMapConfig {
	private static final SqlMapClient sqlMapClient;

	static{
		try{
			//Definindo o caminho para o SqlMapConfig e criando o reader
			String res = "tuto/ibatis/config/SqlMapConfig.xml";
			Reader reader = Resources.getResourceAsReader(res);

			//Recuperando o client para o SqlMap
			sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
		} catch(Exception e){
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	//Método usado para recuperar o client
	public static SqlMapClient getSqlMapClient(){
		return sqlMapClient;
	}
}

O client é responsável por executar as querys configuradas no SqlMap e nos fornecer os resultados.

Executando Querys e tratando retornos

Para chamar uma das querys é muito simples, apenas chame o cliente e o método correspondente de cada uma.

O select pode ser chamado da seguinte forma:

OracleMapConfig.getSqlMapClient().queryForObject("<id>", <parâmetro>);

O iddeve ser o id correspondente no SqlMap

O código acima tráz apenas uma linha retornada pela query, para trazer todas apenas troque o método para o queryForList, desta forma:

OracleMapConfig.getSqlMapClient().queryForList("<id>", <parâmetro>);

Assim, será retornado uma Collection contendo os objetos.

Vamos aos exemplos:
Select

try{
	Car car = (Car)OracleMapConfig.getSqlMapClient().queryForObject("getCars",
		new Long(readKeyboard()));

	System.out.println("Marca: "+car.getCompany());
	System.out.println("Modelo: "+car.getModel());
	System.out.println("Cor: "+car.getColor());
	System.out.println("HP: "+car.getHp());
	System.out.println("Preço: "+car.getPrice());
}catch (Exception e) {
	e.printStackTrace();
}

O id “getCars” é o que definimos nos atributos do select do SqlMap, passando um Long e recuperando o tipo Car, ambos também definidos na linha <select id=”getCars” resultClass=”tuto.ibatis.beans.Car” parameterClass=”java.lang.Long“>.

Insert

try{
	OracleMapConfig.getSqlMapClient().insert("addCar", newCar);
}catch (Exception e) {
	e.printStackTrace();
}

Agora passamos como parâmetros o próprio tipo Car, <insert id=”addCar” parameterClass=”tuto.ibatis.beans.Car“> e chamamos os métodos usando cerquilha (#, ou jogo-da-velha se preferirem), desta forma:

INSERT INTO TBL_CAR (CAR_ID, COMPANY, MODEL, COLOR, HP, PRICE)
VALUES (#carId#, #company#, #model#, #color#, #hp#, #price#)

Delete

try{
	int lines = OracleMapConfig.getSqlMapClient().delete("delCar",
		new Long(readKeyboard()));

	System.out.println(lines + " linhas excluídas");
}catch (Exception e) {
	e.printStackTrace();
}

O método delete do client retorna um tipo int que representa a quantidade de linhas excluídas.

Update

try{
	int lines = OracleMapConfig.getSqlMapClient().update("updCar", car);

	System.out.println(lines + " carros incluídos");
}catch (Exception e) {
	e.printStackTrace();
}

O Update retorna um tipo int, que informa a quantidade de linhas afetadas pelo update.

Como você pode ver, com apenas três arquivos XML e três classes conseguimos montar um sistema de manutenção de estoque de consulta de preço basico.

Você pode fazer o download com os fontes deste tutorial clicando aqui.

Até a próxima!


Passando Objetos Java para uma procedure do Oracle

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


  • AdSense

  • Copyright © 1996-2010 André L. S.. All rights reserved.
    iDream theme by Templates Next | Powered by WordPress