Escolar Documentos
Profissional Documentos
Cultura Documentos
The Club Paradox
The Club Paradox
Introdução
Quando trabalhávamos com tabelas paradox nos acostumamos com o comportamento padrão do BDE de travar um
registro quando o mesmo já fosse colocado em edição por outro usuário do sistema ou mesmo outra aplicação. Assim que
começamos migrar nossas aplicações de tabelas paradox para um banco relacional, começamos a fazer comparações entre a
nossa antiga forma de gerenciar a base de dados e a nova que estaríamos começando a utilizar. Uma das primeiras observações
realizadas foi a detecção da falta de um travamento de registro automatizado pelo banco de dados. Embora muitos não tenham
o costume ou achem desnecessário o travamento de registro, em alguns casos especiais isto pode ser útil, quando um mesmo
registro pode ser concorrido por dois ou mais usuários para sua edição, ou em aplicações Web onde o numero escritas
concorrentes seja elevada. Neste artigo vou falar a respeito do travamento de registro ou lock de registro com banco de dados
Firebird usando DBExpress.
Procedimento Padrão (antes da versão Firebird 1.5)
Logo que o Firebird foi lançado ainda não tínhamos um controle especifico para “travar” um registro, apenas
poderíamos travar um determinado registro caso fizéssemos um update neste registro dentro de uma transação, onde devido
ao nível de isolamento usado nesta transação o registro não ficaria disponível para visualização. Como é esta técnica: faríamos
um update, um delete de um registro (optimistic locking) ou conjunto de registro (pessimistic locking) quando não se sabe ao
certo qual registro irá ser alterado, sendo que a instrução usada para travar um determinado registro seria:
Observe que neste caso não alteramos o valor do campo código, mas o registro entrou na transação uma vez que foi
executado o update sobre ele. Para liberar o registro ou um conjunto de registros basta executar o COMMIT ou ROLLBACK na
transação corrente.
Com cláusula WITH LOCK
Na versão 1.5 do firebird entre as diversas novidades que foram lançadas, uma delas foi a cláusula WITH LOCK, com
esta instrução podemos travar um conjunto de registros a partir de um simples select. Esta cláusula realiza um lock pessimista
explicito de registros, assim sugiro que sempre a use em conjunto com a cláusula WHERE a fim de filtrar a seleção de registros.
A seguir veja um exemplo de sua utilização:
Exemplo
Neste exemplo irei exemplificar o travamento de apenas um registro no momento de sua edição, trabalharemos com o
banco EMPLOYEE, mais precisamente com a tabela CUSTOMER, onde na edição do registro dos dados do cliente iremos travar o
registro para que nenhum outro usuário consiga editar o mesmo registro simultaneamente.
No Delphi crie uma nova aplicação e chame de LockRegistro.dpr, e um formulário principal que iremos salvar como
unPrincipal.pas, alterando o nome do formulário para frmPrincipal, este formulário principal terá como objetivo apenas listar os
registros da tabela para sua seleção e edição. Neste formulário (Imagem 1) adicione e configure os seguintes componentes:
TSQLConnection
(propriedade=valor)
Name = sqlConexao
ConnectionName = 'LOCKREGISTRO'
DriverName = 'Interbase'
Params.Strings = (
'DriverName=Interbase'
'Database= EMPLOYEE.GDB'
'RoleName=RoleName'
'User_Name=sysdba'
'Password=masterkey'
'ServerCharSet='
'SQLDialect=3'
theclub.com.br/Restrito/Revistas/200901/lock0901.aspx 1/6
17/08/2020 Artigos
'BlobSize=-1'
'CommitRetain=False'
'WaitOnLocks=False'
'ErrorResourceFile='
'LocaleCode=0000'
'Interbase TransIsolation=ReadCommited'
'Trim Char=False')
Connected = True
TSQLDataSet
(propriedade=valor)
Name = sqlCustomers
CommandText = 'select * from CUSTOMER'
SQLConnection = sqlConexao
TDataSetProvider
(propriedade=valor)
Name = dspCustomers
DataSet = sqlCustomers
TClientDataSet
(propriedade=valor)
Name = cdsCustomers:
ProviderName = 'dspCustomers'
Active = True
TDataSource
(propriedade=valor)
Name = dsCustomers:
DataSet = cdsCustomers
TDBNavigator
(propriedade=valor)
DataSource = dsCustomers
VisibleButtons = [nbFirst, nbPrior, nbNext, nbLast, nbCancel, nbRefresh]
TDBGrid
(propriedade=valor)
Name = dbGridCustomers
DataSource = dsCustomers
ReadOnly = True
Nas configurações dos componentes listados acima, a mais importante no contexto deste artigo é a alteração do
parâmetro WaitOnLocks do TSQLConnection para False, este parâmetro tem como objetivo controlar a espera ou não da
aplicação quando lock de registro for identificado, ou seja com esta propriedade como True, quando um determinado registro
já locado for ser selecionado a aplicação irá ficar travada aguardando a liberação do registro, para que possa fazer o “fetch”
deste registro. Este é um comportamento desagradável que não desejamos em nossa aplicação, assim iremos alterar esta
propriedade para False, onde quando lock de registro for identificado, irá ser retornada uma mensagem de erro de deadlock
que iremos tratar. Não se esqueça também de adicionar os TFields no ClientDataSet
theclub.com.br/Restrito/Revistas/200901/lock0901.aspx 2/6
17/08/2020 Artigos
Imagem 1
Agora crie um formulário que iremos usar para edição do registro e salve como unCustomer.pas e altere a propriedade
name do formulário para frmCustomer. Adicione nesta unit a referência da unit unPrincipal.pas com Alt+F11. Neste formulário
(Imagem 1) adicione e configure os seguintes componentes:
TSQLDataSet
(propriedade=valor)
Name = sdsCustomer
CommandText = 'select * from CUSTOMER'
SQLConnection = frmPrincipal.sqlConexao
TDataSetProvider
(propriedade=valor)
Name = dspCustomer
DataSet = sdsCustomer
Options = [poAllowCommandText]
UpdateMode = upWhereKeyOnly
TDataSource
(propriedade=valor)
dsCustomer
DataSet = cdsCustomer
TClientDataSet
(propriedade=valor)
cdsCustomer
ProviderName = 'dspCustomer'
Adicione também os campos (TFields) tanto no SqlDataset quanto no ClientDataset, e configure os ProviderFlags do
campo chave primária CUST_NO como: [pfInUpdate, pfInWhere, pfInKey], e para os demais campos: [pfInUpdate]. Também
coloque os respectivos DBEdit para cada campo e um DBNavigator.
theclub.com.br/Restrito/Revistas/200901/lock0901.aspx 3/6
17/08/2020 Artigos
Imagem 2
Como a idéia é no formulário principal ao dar um duplo-clique no DBgrid abrir a janela de edição (frmCustomers),
vamos adicionar o evento DblClick no dbGridCustomers, não se esquecendo de adicionar a unit unCustomer.pas no uses da
unit principal, coloque a seguinte instrução :
implementation
uses unCustomer;
{$R *.dfm}
end.
Dica: Observe que na criação do frmCustomer criamos um segundo parâmetro para informar para o formulário o valor
do campo CUST_NO. Para isto faremos um overload do constructor da classe do formulário, sobrescrevendo o create do
constructor, onde neste evento iremos capturar o valor do parâmetro e atribuir para uma variável privada na mesma classe, veja
código abaixo o código correspondente:
{...}
private
_CUST_NO: Integer;
public
constructor Create(AOwner: TComponent; CUST_NO: Integer); overload;
end;
theclub.com.br/Restrito/Revistas/200901/lock0901.aspx 4/6
17/08/2020 Artigos
var
frmCustomer: TfrmCustomer;
TD: TTransactionDesc;
implementation
uses unPrincipal;
{$R *.dfm}
Vamos adicionar o evento OnCreate no form frmCustomer, neste evento iremos iniciar um transação a partir do
SqlConnection que esta no form principal, somente após a transação iniciada iremos fazer um select filtrando pelo valor chave
da tabela CUSTOMER para que assim apenas um registro seja retornado e travado. Caso o registro já esteja travado por outro
usuário irá ser retornado uma exceção no memento da execução de open, isto por termos alterado a propriedade WaitOnLocks
dos parâmetros de conexão para false, caso contrário como já disse anteriormente, o aplicação ficaria travada aguardando o
momento em que o registro fosse liberado.
Observe que caso haja a exceção faço um tratamento na mensagem de erro, onde se ela retornar 'SQL Server Error: lock
conflict on no wait transaction deadlock update conflicts with concurrent update’, indicará que o registro está em uso, assim
exibo uma mensagem mais amigável, e então executo o método PostMessage(self.handle, WM_CLOSE, 0, 0); para fechar o form
frmCustomer antes que o mesmo fosse exibido. Veja o código abaixo:
Para aplicar as modificações deve ser executado após o post o método Applyupdates, assim adicione o evento
OnAfterPost no Clientdataset cdsCustomer, e insira a seguinte instrução:
O mesmo deve ser feito para o caso de deleção, para isto apenas ligue o evento OnAfterDelete ao método
cdsCustomerAfterPost através do Object Inspector, para fazer o reaproveitamento do código.
theclub.com.br/Restrito/Revistas/200901/lock0901.aspx 5/6
17/08/2020 Artigos
Finalmente iremos adicionar no evento OnClose do form as instruções para confirmar a gravação, ou seja executar o
commit na transação, e no caso de uma falha no processo realizar o rollback da transação, veja o código abaixo:
Conclusão
With lock deve ser usado com bastante consciência, pois serão pouquíssimos os casos em que ele é realmente
necessário, o seu uso inadequado pode causar o aumento da mensagem de erro de deadlock, inclusive o nosso exemplo foi
feito apenas com o objetivo de instruir de como utilizar esta cláusula, mas dependendo da aplicação pode não ser viável a sua
utilização para a mesma situação. Considerando o uso apropriado desta cláusula, ele irá facilitar o processo e controle de
travamento de registro.
Download do Exemplo:
http://www.theclub.com.br/REVISTA/rev0109/LockRegistro.zip
Download do video aula explicativo:
http://www.theclub.com.br/REVISTA/rev0109/LockRegistroVA.zip
Leia mais sobre a cláusula WITH LOCK no artigo Novidades do FireBird 1.5
http://www.theclub.com.br/REVISTA/NOVI0504.ASPX
Sobre o Autor
E-mail: suporte@theclub.com.br
theclub.com.br/Restrito/Revistas/200901/lock0901.aspx 6/6