[quote=brunohansen][quote=sergiotaborda]
O que é o “registra limpo” ?
A utilidade do quê ? (do “registra limpo” ou do Unidade de Trabalho)
[/quote]
É um método da Unidade de Trabalho! (registrarLimpo(Objeto))
A utilidade do registra limpo.
[/quote]
Desculpa , não estou familiarizado com a tradução do padrão. Não entendi que te referias ao registerClean();
O registerClean() , registrarLimpo() , é chamado quando o objeto veio do banco e está sendo usado na unidade. normalmente o método ou não faz nada ou apenas verifica que o objeto tem uma chave válida (todos os objetos que vêm do banco devem ter uma) e não é dirty.
Ao contrário do registerNew() que signfica que o objeto foi criado agora ( e portanto não tem chave ainda).
A sua utilidade é prover validação dos objetos que lês do banco ( se les algum) no inicio do trabalho.
[quote=brunohansen][quote=sergiotaborda]
A sua utilidade é prover validação dos objetos que lês do banco ( se les algum) no inicio do trabalho.
[/quote]
Como assim? Validação? :shock: [/quote]
Validação no sentido de que o objeto é realmente limpo. Ou seja, não é dirty, nem é new , nem é deleted.
Ou seja, já foi persistido antes, não foi alterado nem removido.
// obtem o objeto para maniupulação
Cliente c = ...
unit.registerClean(c);
c.setXY("oi"); //agora está dirty
unit.registerDirty(c);
Ahhh saquei(Eu acho)! Mas essa validação so acontece na unidade de trabalho atual ou ele verifica se o objeto pode estar sujo ou new em outras unidades de trabalho?
[quote=brunohansen]Complementando a pergunta acima.
Como minha unidade de trabalho vai manter o ACID? è ela que vai manter isso?
Vejo que as alterações em uma Unidade de trabalho pode conflitar com alterações feitas e ainda não confirmadas em outras unidades de trabalho.
Outra dúvida é:
Na minha aplicação eu devo manter uma unica instância para cada entity?
Se a resposta for afirmativa como vou controlar alterações concorrentes?
[/quote]
Vc tem que se lembrar que uma unidade de trabalho (UT) sempre corre dentro de uma transação. É a transação que vai ser ACID.
A UT tem um comando commit(). Este comando tem um significado semelhante ao commit da transação, mas é diferente.
A UT serve para establecer uma fronteira transacional OO , enquanto que uma transação normal (digamos uma JTA) estabelece uma fronteira global, não necessáriamente OO.
A UT vai manter a atomicidade porque ou o commit deu certo ou não deu.
Mas o resto é com a transação global.
Quanto à concorrência, a UT não é um padrão para isso. Eu apliquei o seguinte conceito para resolver isso: Cada UT corre num contexto. Cada contexto é executado por um mesmo executor. O executor controla a execução das UT no contexto.O contexto permite que recursos sejam travados (lock) , mas normalmente isso não é necessário. Este controle é feito sobre a presunção otimista. O que significa que se parte do principio que nunca haverá concorrencia, e quando houver, uma exceção será lançada e pronto. O processo chamador poderá tentar invocar a UT de novo, se quiser.O lock serve para encontrar essas exceções.
O contexto permite também isolar as alterações de cada UT. Repare que as alterações feitas dentro da UT não acontecem até que o comando commit seja invocado. Até lá elas representam intenções. Então, não ha real alteração do banco até ao commit da UT. E o executor apenas permite um commit por vez. Isso estabelece que a primeira UT a gravar os dados é a que está valendo.
Tem funcionado para mim, mas eu uso um esquema diferente do Fowler. As minhas UT tem acesso a um DAO como o resto do codigo , só que através do contexto. Este DAO é na realidade um memorizador que vai guardado o que terá que ser feito no commit do contexto. Quando dou commit no UT o DAO memorizador é lido e tudo é executado num DAO verdadeiro baixo uma transação normal. É como se fosse um batch. A UT é a logica que cria o batch e o commit significa “agora execute”
A implementação pode ser bem mais sofisticada, por exemplo criando prioridades, e formas de saber se os contextos são independentes (se forem, podem ser executados em paralelo sem problema) mas até agora não precisei disso , então não sei até que ponto isso é um problema real.
Poxa, mas como vai resolver aquele probleminha básico de débito em conta? Onde lê se o saldo para verificar se possui saldo suficiente e depois faz o débito. Quando tem concorrência na verificação de suficiência de saldo.
Poxa acho isso um pouco ruim! Imagina o usuário do sistema fazendo várias alterações, chega na hora de finalizar o commit da pau porque alguém comitou uma alteração menor na frente. Eu no lugar do usuário ficaria muito puto com isso.
[quote=brunohansen][quote=sergiotaborda]
Vc tem que se lembrar que uma unidade de trabalho (UT) sempre corre dentro de uma transação. É a transação que vai ser ACID.
A UT tem um comando commit(). Este comando tem um significado semelhante ao commit da transação, mas é diferente.
A UT serve para establecer uma fronteira transacional OO , enquanto que uma transação normal (digamos uma JTA) estabelece uma fronteira global, não necessáriamente OO.
[/quote]
Poxa, mas como vai resolver aquele probleminha básico de débito em conta? Onde lê se o saldo para verificar se possui saldo suficiente e depois faz o débito. Quando tem concorrência na verificação de suficiência de saldo.
[/quote]
é para isso que serve o lock.
É mais ou menos assim:
faz lock do recurso “saldo de conta”
consulta o saldo
decide
altera o saldo
commit
5.1) o commit libera os locks automaticamente
Se alguem tentar ler o saldo uma OptimisticConcurrencyException é lançada porque esse recurso esta´sob lock. Ou , em altertiva, vc enfileira as UT que manipulam o saldo e deixa uma de cada vez. Então o saldo que elas vêm será sempre o verdadeiro.
pode parecer ruim ,mas num sistema com pouca concorrência não é um problema. Se o seu programa tem muita concorrencia esqueça UT logo à partida. Se bem que tecnologias como EJB tb usam o principio da concurrencia otimista (porque a sua implementação é a mais simples)
Não são escludentes. Eu uso um mecanismo de fabrica de DAO. No programa todo o DAO pode ser obtido a qq momento da fabrica.
Dentro da UT o DAO é obtido da mesma forma, só que dentro da fábrica tem um controle especial que retorna a DAO da UT (na realidade da thread corrente) e não a DAO normal.
Ou seja, o programador da UT e o programador da , digamos UI, não sabem qual DAO estão manipulando. É a mesma interface de DAO e isso chega. Internamente a infra controla o uso de uma e de outra.
Outra coisa, um usuário humana não interage com uma UT. A UT é como um batch. Ela é chamada por um agente do sistema ( um serviço, por exemplo) que foi estimulado por um gesto final do usuário, como um save.
Então a UT é auto-contida. Ela faz um trabalho, e um trabalho só. Várias UT diferentes são necessárias no sistema. A infra apenas oferece mecanismos para controlar a execução da UT de forma transparente e unificada ( o tal executor). Esta foi a forma que eu encontrei de implementar trnasações OO com base no pardrão do Fowler, mas várias alterações tiveram que ser feitas porque o que o Fowler mostra é bastante simplista. Ou seja, pegue as ideias e faça a sua ppr implementação. Teste, bastante e depois use.
internamente o contexto sabe qual é a UT e qual é o executor.
O conexto delega para o executor o lock daquele recurso, naquela UT.
O trabalho do executor é simples. Verifica se o recurso já está lock para outra UT, se está lança exceção. Se não, faz o lock para esta UT.
Isto é feito com um Map que mapeia recursos para UT, sendo que o recurso é a chave. Ou seja, o lock é apenas um Map. Se está no map está lock, se não está , está livre.