Devo sincronizar ou não?

7 respostas
maresp

Tenho um método em uma classe DAO que insere registro em uma tabela. Porém antes de fazer a inserção é gerada uma pk através de um “select max(campo_pk)+1” (dentro do próprio método).

A minha dúvida é se a situação abaixo pode acontecer:

Usuário 1: executa a chamada do método;

Usuário 2: executa a chamada do método;

Usuário 1: o método faz o select max();

Usuário 2: o método faz o select max(); Perceba que os dois usuários estão com a mesma chave

Usuário 1: o método insere o registro;

Usuário 2: o método insere o registro; ERRO! duplicate key

7 Respostas

Frank

Como você esta fazendo um select, não tem como você fazer um BeginTrans (com isso você não permite que outros usuários acessem a tabela até que acabe a transação do usuário que executou)… :cry:
Será que não dá para você utilizar uma flag.
Quando o usuário1 está executando o select max(), você seta a flag como 1, por exemplo, e quando o usuário2 for executar o select max() ele verifica a flag e vê:
:arrow: Se esta setada como 1, não pode executar o select max();
:arrow: Se esta setada como 0, pode executar o select max().

Vê se você consegue implementar isso …
Falow. 8)

S

O seu banco de dados não suporta seqüências não?
Veja um exemplo para Oracle:

Imagine a seguinte tabela:

SQL> CREATE TABLE TESTE (A NUMBER NOT NULL PRIMARY KEY, B VARCHAR2(64) NOT NULL);

SQL> DESC TESTE;
Nome Nulo? Tipo


A NOT NULL NUMBER
B NOT NULL VARCHAR2(64)

SQL> CREATE SEQUENCE SEQ_TESTE INCREMENT BY 1;

SQL> SELECT * FROM USER_SEQUENCES;

SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY C O CACHE_SIZE LAST_NUMBER


SEQ_TESTE 1 1,0000E+27 1 N N 20 1

Como você pode ver a sua seqüência foi criada

Agora para utilizá-la…

SQL> INSERT INTO TESTE VALUES (SEQ_TESTE.NEXTVAL, AAAAA);

SQL> INSERT INTO TESTE VALUES (SEQ_TESTE.NEXTVAL, AAAAB);

SQL> INSERT INTO TESTE VALUES (SEQ_TESTE.NEXTVAL, AAAAC);

SQL> COMMIT;

SQL> SELECT * FROM TESTE;

A B

1 AAAAA
     2 AAAAB
     3 AAAAC


Para saber o valor corrente da seqüência:

SQL> SELECT SEQ_TESTE.CURRVAL FROM DUAL;

CURRVAL

3

Para você usar o CURRVAL é necessário que a Seqüencia tenha sido submetido a pelo menos um .NEXTVAL, caso contrário você obterá a seguinte mensagem de erro:

SQL> CREATE SEQUENCE SEQ_TESTE2 INCREMENT BY 1;

SQL> SELECT SEQ_TESTE2.CURRVAL FROM DUAL;

SELECT SEQ_TESTE2.CURRVAL FROM DUAL
*
ERRO na linha 1:
ORA-08002: a seqüência SEQ_TESTE2.CURRVAL ainda não foi definido nesta sessão

Bom, acho que por enquanto é só isso.

Atenciosamente,

maresp

A minha dúvida é se pode ocorrer acessos simultâneos ao método pois isso ocasionaria a duplicate key. Acho que abrir uma transação não se aplica, pois a transação não vai inibir o acesso à tabela, ela simplesmente vai executar commit após todas operações da transação forem realizadas.
Quanto a flag de acesso, acho que o sinchronized está aí justamente pra fazer isso…

maresp

A questão não é se o banco suporta sequência… estou procurando ficar independente de banco de dados.

A

Concordo com maresp. Acho que o synchornized está aqui para isso. De qualquer maneira, a sequência que você apresentou de fato não é segura. Na minha visão o “select max(…” deve estar sincronizado com a operação de insert, pois assim um novo select só será realizado depois de inserido o registro pelo primeiro usuário.

T

Realmente usar o max nao vai resolver seu problema. o QUe eh mais negocio eh voce criar um sequenciador proprio. Irei aqui dar as diretrizes principais dessa operacao:

1 . Crie uma tabela no seu banco de dados com os seguintes campos:
sequencia : char(50)
numero: integer
2 . Crie um registro nessa tabela para cada sequencia que voce venha a precisar. contedo o numero para 1 (valor inicial)

3 . Para recuperar o proximo id disponivel, use a seguinte tecnica:
pegue uma conexao JDBC
sete o autocommit para off
realize um update seq set numero = numero + 1 where sequencia = ‘nome_sequencia’
realize um select numero from seq where sequencia=‘nome_sequencia’
commit a transacao

4 faca o uso do id gerado.

Desta forma, o update e select sao uma operacao atomica, dentro de uma transacao. Se um usuario fizer o update, e outro usuario tentar fazer o update tb, esse segundo vai ficar bloqueado ate que o primeiro faca o select e chame o commit.

Esqueci de comentar, eh obvio que o banco tem que suportar transacao e locks no update ali em cima, o minimo que se espera de um banco, neh.

maresp

Bela solução tanque, vou implementá-la…

Criado 14 de agosto de 2003
Ultima resposta 14 de ago. de 2003
Respostas 7
Participantes 5