Eu gostaria de gerar um número sequencial para ser registrado no banco. Este número tem o seguinte formato: Número do Estado + Ano + Mês + um número sequencial composto por 4 dígitos. A cada início de mês este sequencial volta ao início (0001).
Exemplo:
31 + 2012 + 09 + 0001 = 312012090001
29 + 2012 + 09 + 0002 = 292012090002
31 + 2012 + 10 + 0001 = 312012100001
Como gerar eu sei, mas como resolver o problema da concorrência? O sistema é web e tipo, se 2 usuários fazem o cadastro de dados no mesmo instante é possível que eles peguem o mesmo sequencial? Como eu poderia resolver isto? Desde já agradeço a ajuda.
Você precisa resolver isso na aplicação ou no banco?
No banco, dependendo de qual for, pode usar uma Sequence.
Se for Mysql você pode talvez emular uma sequence
( O controle de transação fará o controle de concorrência para você).
Na aplicação você pode usar a classe java.util.concurrent.atomic.AtomicInteger.
Ela te garante que você faça operações atômicas, não tendo problemas com concorrência.
Acho que o código abaixo resolveria seu problema:
AtomicInteger atomic = new AtomicInteger(0); //isse cara na verdade deveria persistir entre requests
int novoCodigo = atomic.incrementAndGet();
O único problema de utilizar as classes atômicas é se a aplicação for replicada em vários servidores. Se for colocada apenas em um pode utilizar isso guardando a variável atômica no objeto servletcontext por exemplo.
Se a aplicação for colocada em vários servidores acho que uma forma mais simples seria criar uma tabela com os campos Número do Estado,Ano,Mês e último valor gerado para a combinação anterior e a cada insert consultar o último valor, somar um e guardá-lo no campo último valor gerado (é uma das formas que o jpa pode ser configurado para gerar identificadores, por exemplo). O único problema é que na hora dessa atualização seria necessário um bloqueio pessimista nesta “tabela de chaves”, então se a quantidade de requisições para esse serviço for muito grande essa tabela pode se tornar um bottleneck para o sistema.
Outra possibilidade seria guardar essa combinação Número do Estado,Ano,Mês e chave em um campo apenas, criando uma chave única nesse campo e guardando o último valor no atomicinteger da mesma forma que na primeira opção (no servletcontext), só que capturando a exceção da constraint unique, somando um no atomicinteger e tentando inserir até o erro na constraint unique não ocorrer mais. Isso seria uma forma mais parecida com um bloqueio otimista e também iria garantir o funcionamento mesmo se a aplicação for distribuída em várias máquinas diferentes, já que eventualmente todos os atomicintegers de todos os objetos servletcontext serão sincronizados, mesmo que de uma forma “burra”. A performance seria melhor no caso de muitas requisições, pois o bootleneck da “tabela de chaves” não existiria mais porém eu acredito que a segunda opção (da “tabela de chaves”) é mais simples de implementar.
[quote=rogeriopaguilar]O único problema de utilizar as classes atômicas é se a aplicação for replicada em vários servidores.
[/quote]
Pois é, depois que postei que pensei nisso… se vc tiver vários servidores de aplicação, cada um roda num processo diferente um semáforo não resolve. Outra alternativa seria usar um arquivo de lock, talvez FileChannel.lock. Mas se teus servidores de app estiverem em máquinas diferentes, tb não resolveria…
Nesse caso, acho que o mais seguro é usar uma tabela mesmo, usando lock pessimista…
Vc espera ter muita concorrência nessa geração do número?
Obrigado pelas respostas. Realmente a aplicação irá rodar em um cluster de servidores e terá muitos acessos. Mas para minha sorte a regra da geração do número mudou e como este número é a primary key de uma tabela eu posso capiturar a excecão de violação de constraint para assim regerar outro número e tentar inserir novamente. Não sei se fui claro, mas a solução foi por aí. Entretanto, gostaria de saber como poderia resolver o problema proposto antes, caso um dia eu cruze com um problema como este. O AtomicInteger resolveria este problema? nunca trabalhei com este objeto. Mais uma vez, obrigado a todos que contribuíram. Valeu!