André L. S. Desenvolvimento de Softwares, Tecnologia e Games

30jul/100

INNER JOIN e OUTER JOIN

Se você é um DBA ou trabalha com programação e usa muito banco de dados SQL, este tópico não é você, a menos que não conheça as cláusulas INNER JOIN e OUTER JOIN, muito comuns em SGBDs SQL.

Não sabe o que é SGBD? Os Sistemas de Gerenciamento de Banco de Dados são programas que auxiliam na manipulação dos dados armazenados. O MySQL, PostgreSQL, MS SQL Server ou Oracle, que você tem em sua casa ou empresa, são SGBDs.

Para este tutorial você precisa ter, pelo menos, o conhecimento básico de SQL.

Chega de enrolação, vamos ao que interessa!

INNER JOIN

A cláusula INNER JOIN é usada quando se quer recuperar dados em mais de uma tabela através da igualdade de suas foreign keys.

Por exemplo, pense que você quer criar um programa que sirva como agenda telefônica. Você cadastra as pessoas e os contatos telefônicos, como uma pessoa poderá ter nenhum até N números de telefones, você separa em duas entidades como neste modelo:

Olhando rapidamente você percebe que a entidade TELEFONE tem o atributo NUM_PESSOA que é o número de identificação do registro na entidade PESSOA.

Vamos supor que as tabelas estejam preenchidas com os seguintes registros:

 +----------------------------+  +---------------------------+
 |           PESSOA           |  |         CONTATO           |
 +------------+---------------+  +------------+--------------+
 | NUM_PESSOA | NOME_PESSOA   |  | NUM_PESSOA | NUM_TELEFONE |
 | 1          | José          |  | 1          | 6589-3666    |
 | 2          | Arnaldo       |  | 3          | 9888-6699    |
 | 3          | Maria         |  | 3          | 8956-6666    |
 | 4          | Elverdelando  |  | 2          | 2888-9877    |
 +------------+---------------+  +------------+--------------+

Para recuperar os telefones de uma pessoa você deve usar INNER JOIN, implicitamente ou explicitamente. Vou explicar os dois casos.

O método explícito é usar a cláusula explicitamente (duh!), que é formada por INNER JOIN <tabela filha> ON <atributos de identificação>;, no nosso exemplo:
SELECT nome_completo, num_telefone FROM pessoa INNER JOIN telefone ON pessoa.num_pessoa = telefone.num_pessoa.

Estamos dizendo: "me traga o nome e os números dos telefones da pessoa onde o número da pessoa em TELEFONE seja igual ao número da pessoa em PESSOA".

Na forma implícita você não usa a cláusula INNER JOIN, o tratamento de igualdade é feito na cláusula WHERE. Usando o mesmo exemplo:
SELECT nome_completo, num_telefone FROM pessoa, telefone WHERE pessoa.num_pessoa = telefone.num_pessoa.

Em ambos os casos o resultado será:

 +---------------------------+
 |         CONTATO           |
 +------------+--------------+
 | NOM_PESSOA | NUM_TELEFONE |
 | José       | 6589-3666    |
 | Maria      | 9888-6699    |
 | Maria      | 8956-6666    |
 | Arnaldo    | 2888-9877    |
 +------------+--------------+

"Que simples! Hummmm espera um pouco... Onde está o Elverdelando (que nominho, hein?!)."

Lembra que dissemos ao banco "me traga o nome e os números dos telefones da pessoa onde o número da pessoa em TELEFONE seja igual ao número da pessoa em PESSOA"? Então! O Elverdelando não esta na tabela TELEFONE, por isso não foi retornado no resultado.

Quando você precisar retornar os dados, mesmo não estando na tabela filha, usamos o OUTER JOIN.

OUTER JOIN

A clásula OUTER JOIN tem duas variantes, LEFT OUTER JOIN e RIGHT OUTER JOIN, sendo que estas cláusula devem ser declaradas sempre de maneira explícita. Alguns SGBDs facilitam a utilização do OUTER JOIN te polpando de declarar toda a sintaxe, como é o caso do Oracle que utiliza o símbolo (+) na identificação fraca, mas NÃO É ANSI, ou seja, não funcionará em qualquer SGBD.

Para que apareçam todas as pessoas, inclusive as que não têm número de telefone cadastrado usamos LEFT OUTER JOIN ou RIGHT OUTER JOIN.

A sintaxe é parecida com a INNER JOIN, mudando apenas o nome da cláusula: LEFT[RIGHT] OUTER JOIN <tabela filha> ON <atributos de identificação>;

Assim, faremos...
SELECT nome_completo, num_telefone FROM pessoa LEFT OUTER JOIN telefone ON pessoa.num_pessoa = telefone.num_pessoa
ou então
SELECT nome_completo, num_telefone FROM telefone RIGHT OUTER JOIN pessoa ON pessoa.num_pessoa = telefone.num_pessoa

Em ambos os casos o resultado será:

 +-----------------------------+
 |         CONTATO             |
 +--------------+--------------+
 | NOM_PESSOA   | NUM_TELEFONE |
 | José         | 6589-3666    |
 | Maria        | 9888-6699    |
 | Maria        | 8956-6666    |
 | Arnaldo      | 2888-9877    |
 | Elverdelando |              |
 +--------------+--------------+

Como você pôde perceber, a única diferença entre o LEFT OUTER JOIN e o RIGHT OUTER JOIN é só a indicação de qual é o lado forte do relacionamento, ou seja, em qual lado da cláusula está a tabela que pode não ter valores na tabela filha. No nosso caso, ambos apontando para a tabela PESSOA.

INNER JOIN é muito usado para consulta em banco de dados, quase que totalmente em seu método implícito. Já as cláusulas OUTER JOIN também são usadas, mas não com tanta frequência, posso dizer que é raro encontra-las por aí.

Espero que este tutorial os ajude! ;)

22fev/100

Inserindo arquivos em campos BLOB de uma base de dados

Para inserir um arquivo, seja ele de qualquer formato, é necessário utilizar o método setBinaryStream implementado pelo PreparedStatement.

PreparedStatemente.setBinaryStream(int índice, Inputstream is, int tamanho);

Para o exemplo definimos uma tabela chamada arquivo que contém um campo BLOB chamado ARQ.

//Conexão normal, como qualquer JDBC
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@<IP>:<PORTA>:<SID>","<USUARIO>","<SENHA>");

//Acessando o arquivo a ser enviado e recuperando um InputStream
File arquivo = new File("<CAMINHO_COMPLETO_DO_ARQUIVO>");
FileInputStream fis = new FileInputStream(arquivo);

//Preparando a chamada normalmente
PreparedStatement ps = conn.prepareStatement("INSERT INTO ARQUIVO (arq) VALUES(?)");

//Informando o inputstream e o tamanho do arquivo a ser enviado
ps.setBinaryStream(1, fis, (int)arquivo.length());

ps.execute();

ps.close();
conn.close();

No meu teste utilizei a base de dados Oracle 8i. Como não tenho à disposição uma base MySQL/PostgreSQL/MS SQL Server, deixo vocês encarregados de testar e me enviar os resultados, OK? ;)

[]'s e até a próxima!

17jan/100

Recuperando cursor de uma procedure do Oracle no Java

Percebi que muitas pessoas acabam chegando aqui pesquisando uma forma de recuperar cursores através de procedures do Oracle, para estas pessoas eu coloco à disposição uma maneira de fazê-lo.

Para que o cursor possa ser retornado é preciso declara-lo como REF CURSOR no spec da Package.

  --Criando o tipo REF CURSOR que será o cursor
  type g_cursor is ref cursor;

Em ambos, spec e body, você precisa declarar uma variável out do tipo do REF CURSOR mencionado acima.

  procedure PRO_RETORNA_LISTA_CARROS(
    i_id     in     tbl_car.car_id%type,
    o_cursor in out g_cursor);

Para devolver o cursor com os resultados (caso haja), é necessário abri-lo no body da procedure, desta forma:

open o_cursor for
          select car_id, company, model, color, hp, price
          from tbl_car
          where car_id = i_id;

A Package completa ficará assim:

create or replace package PAC_CURSOR is
  --Criando o tipo REF CURSOR que será o cursor
  type g_cursor is ref cursor;

  --Procedure que retornará o cursor
  procedure PRO_RETORNA_LISTA_CARROS(
    i_id     in     tbl_car.car_id%type,
    o_cursor in out g_cursor); -- Nosso cursor

end PAC_CURSOR;
/

create or replace package body PAC_CURSOR is
  procedure PRO_RETORNA_LISTA_CARROS(
    i_id     in     tbl_car.car_id%type,
    o_cursor in out g_cursor) is

       begin
        --Abrindo o cursor para retornar os valores
        open o_cursor for
          select car_id, company, model, color, hp, price
          from tbl_car
          where car_id = i_id;

  end PRO_RETORNA_LISTA_CARROS;

end PAC_CURSOR;

Temos o lado do Oracle pronto, agora precisamos tratar a chamada no Java.

Como o cursor está sendo retornado por uma procedure, usaremos um java.sql.CallableStatement.

CallableStatement cs = conn.prepareCall("{call PAC_CURSOR.PRO_RETORNA_LISTA_CARROS(?,?)}");

O registerOutParameter receberá o tipo oracle.jdbc.OracleTypes.CURSOR e retornará um java.sql.ResultSet. Iteraremos o ResultSet do mesmo modo que iteramos um Iterator.
Cada coluna retornada pelo SELECT será representado como um mapa, usando o getter correpondente. Por exemplo, chamaremos o método getString(<nome coluna>) quando retornar um varchar, getDate(<nome coluna>) quando retornar um date e etc.

O código completo fica assim:

//Chamando o procedure
CallableStatement cs = conn.prepareCall("{call PAC_CURSOR.PRO_RETORNA_LISTA_CARROS(?,?)}");

//Definindo o tipo do retorno, no caso o cursor
cs.registerOutParameter("o_cursor", OracleTypes.CURSOR);
cs.setLong("i_id", id);

cs.execute();//Executando a chamada

//Recuperando o cursor como um Resultset
ResultSet rs = (ResultSet)cs.getObject("o_cursor");

//Iterando as linhas retornadas
while(rs.next()){
	//Obtendo o valor das colunas
	System.out.println("ID: " + rs.getLong("car_id"));
	System.out.println("Marca: " + rs.getString("company"));
	System.out.println("Modelo: " + rs.getString("model"));
	System.out.println("Cor: " + rs.getString("color"));
	System.out.println("HP: " + rs.getString("hp"));
	System.out.println("Preco: " + rs.getFloat("price"));
}

No final você conseguirá obter qualquer valor retornado em um SELECT.

Até a próxima!

21set/090

Recuperando coleção de objetos de uma procedure Oracle

Como prometido há muito tempo atrás (e bota tempo nisso) no tópico "Passando Objetos Java para uma Procedure do Oracle", mostrarei como recuperar objetos Java contendo uma coleção de outro objetos através de uma procedure do Oracle. Para quem não leu, é altamente recomendado ler o post anterior.

Para este tutorial, precisamos incluir a tabela TBL_CLASS e adicionar sua primary key como foreign key na TBL_USER.

--num class será a PK e o desc_class será a descrição
create table TBL_CLASS (num_class number, desc_class varchar(100));
alter table TBL_CLASS add primary key(num_class);

alter table TBL_USER add num_class number;
alter table TBL_USER add constraint FK_CLASS foreign key(num_class) references tbl_class(num_class);

Agora precisamos incluir os novos tipos:

create or replace type class_type as object (num_class number, desc_class varchar2(100), users arr_users);
/
create or replace type arr_class as table of class_type;
/

O tipo class_type será o objeto. Repare que na sua assinatura incluímos o tipo arr_users, que será nossa coleção de user_type (vide post anterior), o tipo arr_class será uma coleção de class_type.

Agora incluiremos a procedure responsável por retornar nossa coleção de class_type.

procedure pro_select_class(clas in class_type, class_return in out arr_class)is
  class_ref_cur ref_cur;
  --Coleção de classes
  classes arr_class := arr_class();

  begin
    open class_ref_cur for
      select cast(
                multiset(
                  select num_class,
                         desc_class,
                         (select cast(
                                  multiset(
                                    select user_name,
                                           height,
                                           b_date
                                    from tbl_user
                                    --Fazendo o JOIN com a TBL_USER
                                    where tbl_user.num_class = tbl_class.num_class
                                  ) as arr_users)
                          from dual) users
                  from tbl_class
                  --Usando o atributo num_class do parâmetro de entrada
                  where num_class = clas.num_class) as arr_class
      ) classes
    from dual;

    --incluindo ao retorno no array
    fetch class_ref_cur into classes;
    --passando o array para a variável out
    class_return := classes;
end pro_select_class;

Repare que a procedure recebe o tipo class_type como parâmetro in e retorna o tipo arr_class.

Separando trecho responsável por resgatar e montar nossos objetos teremos:

--Montará a coleção de retorno
select cast(
        multiset(

          --Retornará os objetos class_type e seus atributos
          select num_class,
                 desc_class,

                 --Populará a coleção com user_type
                 (select cast(
                          multiset(
                            select user_name,
                                   height,
                                   b_date
                            from tbl_user
                            where tbl_user.num_class = tbl_class.num_class
                          ) as arr_users)

                  from dual) users

          from tbl_class
          where num_class = clas.num_class) as arr_class
) classes
from dual;

Os objetos de banco estão prontos, agora para o Java!

Criaremos o objeto que será interpretado pelo Oracle. Vamos chamá-lo de TypeClass:

public class TypeClass implements SQLData{
	public static final String ORACLE_OBJECT_NAME = "CLASS_TYPE"; //Nome do tipo no Oracle
	public static final String ORACLE_CLASS_ARRAY_NAME = "ARR_CLASS"; //Nome do array no Oracle

        //campos criados na tabela TBL_CLASS
	private Long number;
	private String desc;
	private Array users; //Esta será a coleção de user_type (ou TypeUser no Java)

	public String getSQLTypeName() throws SQLException {
		return ORACLE_OBJECT_NAME;
	}

	public void readSQL(SQLInput stream, String typeName) throws SQLException {
		setNumber(stream.readLong());
		setDesc(stream.readString());
		setUsers(stream.readArray());//Usado pelo JDBC driver para ler a coleção
	}

	public void writeSQL(SQLOutput stream) throws SQLException {
		stream.writeLong(getNumber());
		stream.writeString(getDesc());
		stream.writeArray(getUsers());//Usado pelo JDBC driver para armazenar a coleção
	}
	//Getters e setters omitidos
}

Precisamos mapear os dois tipos já que ambos serão interpretados na requisição, desta forma:

Map> typeMaps = connection.getTypeMap();
typeMaps.put(TypeUser.ORACLE_OBJECT_NAME, TypeUser.class);
typeMaps.put(TypeClass.ORACLE_OBJECT_NAME, TypeClass.class);

Também precisaremos mapear os dois arrays:

typeMaps.put(TypeClass.ORACLE_CLASS_ARRAY_NAME, TypeClass[].class);//retornado pela procedure
typeMaps.put(TypeUser.ORACLE_USER_ARRAY_NAME, TypeUser[].class);//retornado pela coleção de class_type

Para a requisição faremos:

cs = conn.prepareCall("{call PAC_BEAN.PRO_SELECT_CLASS(?,?)}");
//registrando o tipo de saída, que será um array de TypeClass
cs.registerOutParameter("class_return", OracleTypes.ARRAY, TypeClass.ORACLE_CLASS_ARRAY_NAME);

//passando o objeto de parâmetros da query
cs.setObject("clas", classQry);

cs.execute();
//recuperando e iterando o array de TypeClass
Object[] array = (Object[])cs.getArray("class_return").getArray();

for(Object obj : array){
	TypeClass objClass = ((TypeClass)obj);

	System.out.println("Description: "+objClass.getDesc());

        //Aqui pegamos a coleção (ou array) de user_type(TypeUser) retornada pela query.
	Object[] userArray = (Object[])objClass.getUsers().getArray();
	for(Object user : userArray){
		System.out.println("\tName: " + ((TypeUser)user).getName());
		System.out.println("\tHeight: " + ((TypeUser)user).getHeight());
		System.out.println("\tBirth: " + sdf.format(((TypeUser)user).getBirth())+ "\r\n");
	}
}

O final você terá um TypeClass que contém um java.sql.Array de TypeUser no atributo getUsers().

E aqui cumpro minha promessa. Baixe o código fonte com o exemplo contendo este e o post anterior aqui.

Até a próxima!

4jul/092

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!