Nesse artigo eu utilizei a transaction dentro da SP (Stored Procedure), mas o C# contem recursos que auxiliam a utilizar as transações na propria linguagem, mas isso fica para um proximo artigo.

Bem, vamos primeiro apresentar alguns conceitos:

  • Stored Procedure:

    É um procedimento que fica armazenado no próprio banco de dados, dando a possibilidade de trabalhar com SQL de uma maneira mais parecida com a que usamos em linguagem de programação que não seja SQL. Por exemplo podemos usar variaveis, instruções condicionais, loops, alem de podermos agrupar varios comandos SQL dentro de um só procedimeito, ai que entram as transactions.

  • Transaction:

    Podemos definir uma transaction como sendo um meio de garantir que um ou mais comandos SQL sejam executados sem erros, tendo a possibilidade de desfazer os comandos que estejam dentro da transaction caso ocorra algum problema.

    Um exemplo é onde temos duas tabelas quaisquer, onde temos que inserir dados em ambas, porem, caso a inserção na segunda tabela de algum erro nós teremos como reverter a inserção da primeira tabela. Isso ficara mais claro no decorrer do artigo. Para usar transactions é necessario que as tabelas sejam criadas com a engine InnoDB.

Para nosso caso, criaremos duas tabelas (cliente e endereco), eu utilizei o Mysql Query Browser para cria-las, utilizando um script tab, mas você pode, se quiser, utilizar a linha de comando do mysql, fica a seu critério.

Segue os comandos de criação das tabelas:

tabela cliente:
CREATE TABLE cliente (
codCliente INT NOT NULL AUTO_INCREMENT,
nome VARCHAR(100) NOT NULL,
PRIMARY KEY(codCliente)
)Engine=InnoDB;
Tabela endereco:
CREATE TABLE endereco (
codEndereco INT NOT NULL AUTO_INCREMENT,
idCliente INT NOT NULL,
rua VARCHAR(100) NOT NULL,
PRIMARY KEY(codEndereco),
FOREIGN KEY(idCliente) REFERENCES cliente(codCliente)
)Engine=InnoDB;

Como vocês puderam ver, a tabela cliente contém somente codigo e nome do cliente. A tabela endereco contem o código do endereço, o id co Cliente e a rua(ja esta bom só pra aprender xD).

Só pode existir um endereço se existir um cliente ao qual esse endereço sera apontado (campo idCliente da tabela endereco), por isso foi feito uma chave estrangeira apontando o endereco.idCliente para cliente.codCliente.

Vamos criar a stored procedure que será responsavel pela inserção dos dados nas duas tabelas. Abra um novo Script Tab e cole o conteudo a seguir. A procedure esta documentada para um fácil entendimento.
Não assuste com o tamanho da procedure, pois a maioria abaixo são comentarios.

/* Mudando o delimitador de comandos, para que possamos utilizar
o ;(ponto e virgula) no final de cada linha em nossa procedure
*/
DELIMITER $$

/* Caso ela ja exista, apagamos a procedure para depois criarmos novamente */
DROP PROCEDURE IF EXISTS `spInsere` $$

/* Aqui damos inicio à criação da procedure. A Sintaxe é a seguinte:
CREATE PROCEDURE `nomeprocedure` (lista de parametros separados por virgula)
Os parametros tem o seguinte formato:
IN nomeparametro tipo
Esse IN indica que esse parâmetro sera de entrada, ou seja, sera atravez
dele que serão passados dados para nossa procedure.
Tambem poderiamos ter ao invés de IN, um ou mais parametros OUT, caso
quisessemos enviar dados de dentro da procedure para fora, mas no caso do
arquivo utilizei somente selects mesmo
para passar as mensagens para fora da procedure.
*/
CREATE PROCEDURE `spInsere`(IN n VARCHAR(100), IN r VARCHAR(100))
/* Aqui inicia-se o corpo da procedure */
BEGIN
/* Abaixo fica a declaração da variavel excessao que sera um
inteiro pequeno e é inicializada com 0;
A segunda linha declare indica que quando ocorrer alguma excessão em
algum comando, essa variável excessao será preenchida com o valor 1, tornando
possivel verificar se houve algum problema.
*/
DECLARE excessao SMALLINT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET excessao = 1;

/* Aqui verificamos se os parâmetros não estão vazios, para não inserirmos
campos em branco no banco.
Caso ambos estejam preenchidos, inicio a transaction com o comando
START TRANSACTION.
*/
IF (n <> "" AND r <> "") THEN
START TRANSACTION;

/* aqui inserimos o nome passado por parâmetro na tabela cliente */
INSERT INTO cliente VALUES(null, n);

/* Caso a variavel contenha 1, ou seja, caso tenha ocorrido algum erro
Retornamos uma mensagem de erro, atravez da variavel Msg, e logo em
seguida executamos o comando ROLLBACK.
Esse comando que é o responsavel por desfazer toda e qualquer
alteração que tenhamos feito no banco.
*/
IF excessao = 1
THEN
SELECT 'Erro ao inserir na tabela cliente' AS Msg;
ROLLBACK;
ELSE
/* Caso excessao ainda seja 0, ele continua aqui, onde executamos
o proximo comando cuja função é retornar o ultimo ID(chave primaria)
que foi inserido na tabela cliente e o guarda na variavel @idCliente
*/
SELECT DISTINCT LAST_INSERT_ID() INTO @idCliente FROM cliente;
/* De novo, caso tenha ocorrido erro, imprime mensagem e execura
um ROLLBACK */
IF excessao = 1
THEN
SELECT 'Erro ao selecionar o ultim ID inserido' AS Msg;
ROLLBACK;
ELSE
/* Aqui iremos inserir os dados na tabela endereco, utilizando a
variavel com o id do ultimo cliente inserido, que pegamos no
comando SELECT acima, e tambem o parametro contendo a rua.
*/
INSERT INTO endereco VALUES(null, @idCliente, r);
/* caso tenha erro, mensagem e ROLLBACK */
IF excessao = 1
THEN
SELECT 'Erro ao inserir na tabela endereco' AS Msg;
ROLLBACK;
ELSE
/* Se chegamos até esse ponto da procedure, é por que todos os
comandos foram bem sucedidos então podemos executar o
comando COMMIT.
O COMMIT diz ao banco que todos os comandos foram executados
com sucesso e pode finalizar a transação.
*/
SELECT 'Cadastro efetuado com sucesso' AS Msg;
COMMIT;
END IF;
END IF;
END IF;
ELSE
/* Como estão faltando parametros, estão vazios, imprime-se a mensagem
a seguir.*/
SELECT 'Parametros necessarios' AS Msg;
END IF;

/* aqui finalizamos a procedure*/
END $$

/* Nesse pondo indicamos que agora usaremos novamente o
delimitador ;(ponto e virgula) para separar comandos
*/
DELIMITER ;

Após isso, vamos criar nosso projeto no Visual Studio. Aqui eu utilizei o VS 2008, porem acredito que o exemplo funcionará tambem com o 2005. O unico requisito é ter o conector do mysql para .NET que pode ser adiquirido no endereçp http://dev.mysql.com/downloads/connector/net/5.2.html. É só baixar a versão noinstall, descompactar, e adicinar a referencia em seu projeto ao arquivo mysql.data.dll que se encontra na pasta do conector do mysql. Logo estarei colocando algum artigo explicando em detalhes como configurar o visual studio para se conectar ao mysql.

Crie um novo projeto Windows Forms utilizando a linguagem C#. No meu caso coloquei o nome do projeto como spTransaction. Após criar o projeto, insira dois textbox, dois labels, e um botão no formulario e altere as propriedades como a seguir:

Obs: Coloque os componentes na sequencia que foi listada abaixo. De cima para baixo.

Primeiro label:
nome: lbNome;
text: Nome do cliente

Primeiro TextBox:
nome: txtNome;

Segundo label:
nome: lbEndereco;
text: Endereço do cliente

Segundo TextBox:
nome: txtEndereco;

Botao:
nome: btnSalvar;
text: Salvar Dados;

Depois de inserir os componentes e alterar os atributos acima citados, de um duplo clique no botão btnSalvar e deixe o método click dele como o a seguir. Explicarei o método nos comentários do mesmo:

private void btnSalvar_Click(object sender, EventArgs e)
{
/* variavel que contem a string de conexão que usaremos.
mude os campos da conexão conforme o seu banco de dados, colocando
o host, nome do banco, usuario e senha.
A última opção: Use Procedure Bodies=false; é usada para que
possamos usar stored procedures do mysql usando o conector
que baixamos.
*/
string strCon = "Server=localhost;Database=meubanco;" +
"Uid=meuusuario;Pwd=minhasenha;" +
"Use Procedure Bodies=false;";
/* Abaixo criamos a conexão em si, utilizando a string acima*/
MySqlConnection con = new MySqlConnection(strCon);
/* Agora é o bloco try que executara todos os comandos*/
try
{
/* abrindo a conexão */
con.Open();
/* criando o comando sql indicando a nossa conexão e a nossa
procedure */
MySqlCommand cmd = new MySqlCommand("spInsere", con);
/* aqui indicamos que usaremos stored procedure como tipo de comando*/
cmd.CommandType = CommandType.StoredProcedure;
/* aqui passamos os parametros para a procedure spInsere que criamos
de acordo com os textbox*/
cmd.Parameters.AddWithValue("n", txtNome.Text);
cmd.Parameters.AddWithValue("r", txtEndereco.Text);
/* Nessa linha, executamos nosso comando, pegando a variavel Msg
que criamos em nossa procedure que contem as mensagems
de erro ou sucesso do procedimento.
*/
string retorno = cmd.ExecuteScalar().ToString();
/* mostramos a mensagem na tela.*/
MessageBox.Show(retorno);
}
catch (Exception ex)
{
MessageBox.Show("Houve problemas. Erro: \n\n" + ex.Message);
}
finally
{
/* se a conexão esta aberta, a fechamos */
if (con.State == ConnectionState.Open) con.Close();
}
}

Pronto, agora é só dar um F5 e verificar se está tudo correto.

Tente inserir algo deixando um ou ambos os campos em branco, para ver se a validação da procedure esta valento. Um teste que podemos fazer, é alterar o nome da variavel @idCliente na inserção do endereço, para que de um erro e não seja possivel inserir nada em endereço. Fazendo assim, poderemos constatar que mesmo a inserção de cliente tendo ocorrido antes, o ROLLBACK desfez essa inserção, deixando o banco com sua integridade inicial.

Bem, é isso ai.

Espero o comentario de todos, pois com críticas melhoramos cada vez mais.

Até o proximo artigo.

Qualquer duvida, entre em contato: contato@thiagoapadua.com.br