Controle de lock.

Delphi

18/07/2005

Boma Dia, como vocês fazem para 2 usuários não digitarem o mesmo código em um cadastro por exemplo.

Criam uma tabela de lock com uma chave do usuário e o nome do programa ?


Lynx

Lynx

Curtidas 0

Respostas

Khundalini

Khundalini

18/07/2005

Não vejo uma alternativa melhor que o evento OnReconcileError do TClientDataSet pra resolver uma questão como essa!

[]s
Rubem Rocha
Manaus, AM


GOSTEI 0
Lynx

Lynx

18/07/2005

eu só uso TQuery em cadastros...


GOSTEI 0
Lynx

Lynx

18/07/2005

se eu entendi bem, vou colocar um datasource ligado com um dataset...
e minha query liga com o DataSource.

Este evento funciona como ?

na hora do código, quando sai ?
ou na hora que inseri.

obrigado.


GOSTEI 0
Titanius

Titanius

18/07/2005

Na seção Artigos do Clube Delphi, tem um Sequenciador, muito bom, utilizo ele, e nunca deu um problema comigo...


[]s


GOSTEI 0
Lynx

Lynx

18/07/2005

O ID, da tabela eu utilizo trigger no delphi com generatores....
O Código é digitado, então nao vejo problema, no sequenciador para o ID da tabela.

No artigo mostra que tem problema, não sei se entendi direito.

obrigado


GOSTEI 0
Titanius

Titanius

18/07/2005

eh, comigo nunca deu problema, mas o codigo eh gerado e nao digitado, como o seu caso....


GOSTEI 0
Lynx

Lynx

18/07/2005

o ID da tabela é gerado...

O Código é digitado...
por isso estou preocupado, pq pode acontecer , que 2 usuários estao no mesmo código...


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

Tenho usado Triggers e Generator nos meus programas e não tenho tido problemas com eles.


GOSTEI 0
Lynx

Lynx

18/07/2005

asrsantos, você usa a trigger para o id , e o codigo é digitado certo ?
Deste modo eu tb n tenho problemas...

mas vc controla como para 2 usuários não digitar o mesmo código nos terminais ?

valeu


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

As tabelas dos meus programas eu uso um campo ID que eu deixo o banco controlar através das triggers e dos generators, e ai se necessário eu crio um campo Código para que o usuário utilize para identificar algo, como uma tabela de produtos por exemplo. Para que o usuário não digite o código de um produto igual a de outro eu faço uma pesquisa no banco com uma Query simples. Se o código já existir eu aviso o usuário e obrigo cadastrar um novo código.

Se entendi direito o que precisa, é isso. Não tem segredo.


GOSTEI 0
Lynx

Lynx

18/07/2005

isso eu também faço , exemplo vou cadastrar um produto com o código 20, e ao mesmo tempo outro usuário em outro terminal coloca o código 20, enquanto o terminal 1 ainda está digitando o produto..

entende ?

se ele ainda nao deu um commit...
como você controla está situação ?


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

Agora entendi direito sua dúvida.

Bom, este é um caso típico e acontece muito haja vista que não é possível saber o que cada usuário está digitando em cada terminal. Mas uma coisa é certa, a probabilidade de dois terminais dar um commit ao mesmo tempo é remota, ou seja, se fizer uma nova pesquisa no banco de dados antes do post vc consegue saber se o código já existe.

Normalmente eu faço duas pesquisas.

1 - Quando o usuário sai do campo onde deve digitar o código do produto, cliente etc.
2 - No momento de dar um post. Isso evita que: durante a digitação de um novo cliente, por exemplo, outro usuário entre e coloque o mesmo código de cliente. Se isso ocorreu dou uma mensagem e o cara vai ter que colocar outro código, ai então faço o commit.


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

...que colocar outro código, ai então faço o commit....


Claro, só posso dar um commit se o caso for negativo, ou seja, se o código não existe na tabela.


GOSTEI 0
Lynx

Lynx

18/07/2005

desculpe asrsantos, realmente eu expressei mau minha pergunta...

Vou fazer isso também, mas o complicado se eu dar o focu,no campo código eu estou fazendo limpar toda minha tela...

acho que vou dar uma mensagem avisando que o código já existe, e que o código vai ser um número acima que ele digitou.

exemplo:
Usuário 1, está digitando uma nota número 1, ao mesmo tempo o usuário 2 está digitando a nota 1 também...

Mas o usuário 2 deu um commit primeiro, então quando o usuário 1 dar o post.
Vou enviar uma mensagem avisando que o número 1, já existe e a nota dele será o número 2.


O Que você acha disso ?

obrigado


valeu


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

Acho boa a idéia. A única coisa


Outra forma, talvez menos eficaz e ou menos ´profissional´, seria criar uma lista de códigos que estão sendo digitados no momento. Por exemplo:

Você tem os terminais 1, 2 e 3. Quando cada terminar entrar e digitar um código vc alimenta uma tabela dentro do banco que será usada só pra isso. E na hora que um novo usuário entrar para digitar o codigo vc pesquisa nessa tabela. Se o codigo existir vc pede outro, senão mantem o codigo e atualiza esta tabela.

Quando o usuario der um post ou cancel, vc faz o commit e exclui este codigo da lista.

Tive esta idéia agora (do nada, rs), não é muito prática, mas dependendo do caso acredito que funcione.
É só uma idéia esdruxula que me passou pela cabeça.


GOSTEI 0
Lynx

Lynx

18/07/2005

com certeza asrsantos, esse é o típico lock, que eu coloquei no tópico.
O Sistema na empresa onde eu trabalhava, cada form que abria grava em uma tabela chamada lock.

A chave da tabela lock era o usuário, tinha o nome do form, e o código.
Funcionava legal, mas quando travava o micro algo parecido, ficava preso no lock e não logava mais :lol: .

Fizeram outro programa para apagar o lock, era um ´xunxo´.

Mas está sua idéia da pesquisa antes do commit é o ideal mesmo.

Mas este lock, da para saber onde o usuário está no sistema... fazer aparecer online etc.

Mas ainda acho que a melhor maneira é está da pesquisa antes do commit.


Valeu


GOSTEI 0
Titanius

Titanius

18/07/2005

amigo lynx, tenho um codigo aqui, talvez vc possa melhora-lo:
if (SPGEN <> nil) then begin
      if ((DataSource as TDatasource).DataSet as TIBDataset).State = dsInsert then begin
        (SPGEN as TIBStoredProc).ParamByName(´INCREMENTO´).AsInteger := 1;
        (SPGEN as TIBStoredProc).ExecProc;
        if (SPGEN as TIBStoredProc).ParamByName(´VALOR´).AsInteger <> (StrToInt((EditChave[0] as TEdit).Text) + 1) then begin
          (EditChave[0] as TEdit).Text := StrZero(IntToStr((SPGEN as TIBStoredProc).ParamByName(´VALOR´).AsInteger - 1), 5);
          ((DataSource as TDatasource).DataSet as TIBDataset).FieldByName(copy((EditChave[0] as TEdit).Name, 3, length((EditChave[0] as TEdit).Name) - 2)).AsString := (EditChave[0] as TEdit).Text;
          Application.MessageBox(PChar(´O código foi alterado para ´ + (EditChave[0] as TEdit).Text), ´Atenção...´, 0);
        end;
      end;
    end;
    try
      ((DataSource as TDatasource).DataSet as TIBDataset).Post;
      if ((DataSource as TDatasource).DataSet as TIBDataset).CachedUpdates then
        ((DataSource as TDatasource).DataSet as TIBDataset).ApplyUpdates;
      ((DataSource as TDatasource).DataSet as TIBDataset).Close;
         {Se Tag do transaction for <> 0 não commita}
      if (((DataSource as TDataSource).DataSet as TIBDataSet).Transaction as TIBTransaction).Tag = 0 then
        (((DataSource as TDataSource).DataSet as TIBDataSet).Transaction as TIBTransaction).Commit;
      Result := True;
    except
   (SPGEN as TIBStoredProc).ParamByName(´INCREMENTO´).AsInteger := -1;
   (SPGEN as TIBStoredProc).ExecProc;
   Result := False;
    end;


Basicamente, verifico se o numero jah existe, se existe eu modifico e informo ao usuario...


[]s


GOSTEI 0
Lynx

Lynx

18/07/2005

Titanius, obrigado pelo código, assim que eu terminar os cadastros do sistema, (tem 7 grandes) vou começar a fazer este controle.

Valeu


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

Realmente Lynx, você tem razão. Se ocorre qualquer problema com um usuário o ´lock´ fica preso. Por isso disse que não é a melhor das soluções.
Postai pra gente depois que estiver pronto o resultado. Se funcionou ou não.


GOSTEI 0
Kapak

Kapak

18/07/2005

Colegas, aproveitando o gancho, gostaria de saber como vcs fazem no caso de alteração do mesmo registro por dois usuários; ou seja, quando o primeiro altera, o segundo estará alterando dados ´irreais´.


GOSTEI 0
Lynx

Lynx

18/07/2005

segue abaixo o teste que acabei de fazer e funcionou com a dica de vocês.

procedure TfrmCadEquipamentos.FrameCad1BtnSalvarClick(Sender: TObject);
var
  codequipamento : Integer;
begin
  if not Verificacao then Exit;
  with qryCadEquipamentos do
    begin
      Close;
      SQL.Clear;
      if not status then
        begin
          SQL.Add(´insert into EQUIPAMENTOS                     ´ +
                  ´(CODEQUIPAMENTO,NOMEQUIPAMENTO,IDCODMOTOR,   ´ +
                  ´IDCODCOMPRESSOR,IDCODGAS,VOLTS,VALORUNITARIO)´ +
                  ´values (:CODEQUIPAMENTO,:NOMEQUIPAMENTO,     ´ +
                  ´:IDCODMOTOR,:IDCODCOMPRESSOR,:IDCODGAS,      ´ +
                  ´:VOLTS,:VALORUNITARIO)                       ´);
        end
      else
        begin
          SQL.Add(´update EQUIPAMENTOS set                ´ +
                  ´NOMEQUIPAMENTO = :NOMEQUIPAMENTO,      ´ +
                  ´IDCODMOTOR = :IDCODMOTOR,              ´ +
                  ´IDCODCOMPRESSOR = :IDCODCOMPRESSOR,    ´ +
                  ´IDCODGAS = :IDCODGAS,                  ´ +
                  ´VOLTS = :VOLTS,                        ´ +
                  ´VALORUNITARIO = :VALORUNITARIO         ´ +
                  ´where CODEQUIPAMENTO =:CODEQUIPAMENTO´);
        end;
      codequipamento:= StrToInt(edtCodigo.Text);
      ParamByName(´NOMEQUIPAMENTO´).AsString:= edtNome.Text;
      ParamByName(´IDCODMOTOR´).AsString:= idcodmotor;
      ParamByName(´IDCODCOMPRESSOR´).AsString:= idcodcompressor;
      ParamByName(´IDCODGAS´).AsString:= idcodgas;
      case cmbVolts.ItemIndex of
        0:ParamByName(´VOLTS´).AsInteger := 12;
        1:ParamByName(´VOLTS´).AsInteger := 24;
      end;
      vlrunitario:= TextToCurr(edtValor.Text);
      ParamByName(´VALORUNITARIO´).AsCurrency:= vlrunitario;
      {
      Verifica o Código do Equipamento, com a Dica
      do Pessoal, que frequenta o fórum (ClubeDelphi).
      }
      if not status then // Verifica somente, para inserir registro.
        begin
          qryTesteClubeDelphi.SQL.Clear;
          qryTesteClubeDelphi.SQL.Add(´select CODEQUIPAMENTO from EQUIPAMENTOS ´ +
                                  ´where CODEQUIPAMENTO = :CODEQUIPAMENTO ´);
          qryTesteClubeDelphi.ParamByName(´CODEQUIPAMENTO´).Value:=
          codequipamento;
          qryTesteClubeDelphi.Open;
          if not qryTesteClubeDelphi.IsEmpty then
            begin
              ShowMessage(´Já Existe um Registro com esse Código.        ´ +
              ´ Seu código foi Alterado para ´+IntToStr(codequipamento +1));
              codequipamento:= codequipamento+1;
            end;
        end;
      ParamByName(´CODEQUIPAMENTO´).AsInteger:= codequipamento;
      ExecSQL();
    end;



GOSTEI 0
Lynx

Lynx

18/07/2005

kapak agora pegou mesmo...
Somente com tabela de lock acho...


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

kapak,

É eu acredito que somente fazendo uma tabela para controlar isso.
Talvez exista alguma camarada aqui no fórum que tenha um controle mais preciso do que com o uso de lock.


GOSTEI 0
Kapak

Kapak

18/07/2005

Mesmo criando-se uma tabela, como funcionaria ?
Supondo que um terminal acesse o registro 123 e um outro terminal em outra sala tentar acessar o mesmo registro. Fazendo o controle por essa tabela o programa impediria o acesso ? E se o cara esquecer o registro na tela e ir almoçar, ou dar um reset na máquina ? O registro ficaria bloqueado ?


GOSTEI 0
Lynx

Lynx

18/07/2005

asrsantos paramos no lock novamente :lol:

se alguém conhece algo melhor no fórum postem aí...
Tenho que sair agora, mais tarde eu volto a observar o tópico.


valeu e obrigado a todos


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

Cada vez que o registro entrar em modo de alteção vc lança o número dele na tabela de lock. Quando o usuário efetuar um post ou cancel no registro ele sai fora da lista de lock.

Quando um usuário tentar entrar no registro e o msm estiver na lista o usuário é avisado e não pode entar como alteração. Eu colocaria como somente leitura.

Mas tem o problema de travamento e o que vc mencionou. Se o usuário for almoçar e esquecer de fechar a tela, o registro ficara bloqueado. Ou msm se a maquina desligar sozinha, travar ou algo do genero.


GOSTEI 0
Adriano Santos

Adriano Santos

18/07/2005

Então galera, eu sei que tem um jeito bastante eficaz para isso mas é programação no banco de dados. Só que não tenho a mínima idéia de como fazer ou de como funciona. É algo que diz respeito ao isolamento do registro em uma transação, um papo assim.


GOSTEI 0
Kapak

Kapak

18/07/2005

Atualmente utilizo a seguinte técnica:
Crio um campo numérico na tabela chamado Lock.
O programa de manutenção lê este campo, soma 1 e regrava juntamente com os outros campos modificados.
O lock é feito na hora da atualização do registro por TQuery.ExecSQL com a cláusula Where Lock = Lock lido.
Após o ExecSQL, testo TQuery.RowsAffected e, se retornar 0 dou uma msg dizendo que o registro foi modificado anteriormente, volto a ler e jogo os campos na tela novamente. Gostaria de saber de alguma outra técnica mais prática, pq esta força vc criar um campo adicional na tabela e atualizar por ExecSQL.


GOSTEI 0
POSTAR