Se você colocar o @Transactional
nas DAO
classes e / ou os seus métodos ou é melhor para anotar as classes de serviço que estão chamando usando os objetos DAO? Ou faz sentido anotar as duas "camadas"?
Se você colocar o @Transactional
nas DAO
classes e / ou os seus métodos ou é melhor para anotar as classes de serviço que estão chamando usando os objetos DAO? Ou faz sentido anotar as duas "camadas"?
Respostas:
Eu acho que as transações pertencem à camada Serviço. É quem conhece as unidades de trabalho e os casos de uso. É a resposta certa se você tiver vários DAOs injetados em um Serviço que precisam trabalhar juntos em uma única transação.
Em geral, concordo com os outros afirmando que as transações geralmente são iniciadas no nível de serviço (dependendo da granularidade necessária, é claro).
Entretanto, nesse meio tempo, também comecei a adicionar @Transactional(propagation = Propagation.MANDATORY)
à minha camada DAO (e outras camadas que não têm permissão para iniciar transações, mas exigem as existentes), porque é muito mais fácil detectar erros nos quais você esqueceu de iniciar uma transação no chamador ( por exemplo, o serviço). Se o seu DAO for anotado com propagação obrigatória, você receberá uma exceção informando que não há transação ativa quando o método é chamado.
Também tenho um teste de integração em que verifico todos os beans (processador de pós-bean) para esta anotação e falho se houver um @Transactional
anotação com propagação diferente de Obrigatória em um bean que não pertença à camada de serviços. Dessa forma, garanto que não iniciamos transações na camada errada.
@Transactional
a classe de implementação de Serviço e a implementação de classe @Transactional(propagation = MANDATORY)
DAO (repositório)?
As anotações transacionais devem ser colocadas em torno de todas as operações que são inseparáveis.
Por exemplo, sua chamada é "alterar senha". Isso consiste em duas operações
Portanto, acima, se a auditoria falhar, a alteração da senha também falhará? Nesse caso, a transação deve ser em torno de 1 e 2 (na camada de serviço). Se o email falhar (provavelmente deve ter algum tipo de segurança contra falhas para não falhar), deve reverter a senha de alteração e a auditoria?
Esse é o tipo de pergunta que você precisa fazer ao decidir onde colocar o arquivo @Transactional
.
A resposta correta para as arquiteturas tradicionais do Spring é colocar a semântica transacional nas classes de serviço, pelos motivos que outros já descreveram.
Uma tendência emergente no Spring é o DDD ( Domain-driven Design ). O Spring Roo exemplifica bem a tendência. A idéia é tornar os POJOs de objetos de domínio muito mais ricos do que nas arquiteturas típicas do Spring (geralmente são anêmicos ) e, em particular, colocar semântica de transação e persistência nos próprios objetos de domínio. Nos casos em que tudo o que é necessário é simples operações CRUD, os controladores da Web operam diretamente nos POJOs de objetos de domínio (eles estão funcionando como entidades nesse contexto) e não há camada de serviço. Nos casos em que há algum tipo de coordenação necessária entre objetos de domínio, você pode ter um bean de serviço que, com@Transaction
conforme a tradição. Você pode definir a propagação da transação nos objetos do domínio para algo como, REQUIRED
para que os objetos do domínio usem transações existentes, como transações iniciadas no bean de serviço.
Tecnicamente, essa técnica utiliza o AspectJ e <context:spring-configured />
. O Roo usa definições entre tipos do AspectJ para separar a semântica da entidade (transações e persistência) das coisas do objeto do domínio (basicamente campos e métodos de negócios).
O caso normal seria fazer anotações no nível da camada de serviço, mas isso realmente depende dos seus requisitos.
Fazer anotações em uma camada de serviço resultará em transações mais longas que anotações no nível DAO. Dependendo do nível de isolamento da transação que pode causar problemas, as transações simultâneas não verão as mudanças uma da outra, por exemplo. LEITURA REPETÍVEL.
Fazer anotações nos DAOs manterá as transações o mais curto possível, com a desvantagem de que a funcionalidade que sua camada de serviço está expondo não será feita em uma única transação (reversível).
Não faz sentido anotar as duas camadas se o modo de propagação estiver definido como padrão.
Coloco o @Transactional
na @Service
camada e defino rollbackFor
qualquer exceção e readOnly
para otimizar ainda mais a transação.
Por padrão @Transactional
, apenas procurará RuntimeException
(Exceções não verificadas). Ao definir a reversão para Exception.class
(Exceções verificadas), a reversão será feita para qualquer exceção.
@Transactional(readOnly = false, rollbackFor = Exception.class)
Consulte Exceções verificadas versus não verificadas .
Ou faz sentido anotar as duas "camadas"? - não faz sentido anotar a camada de serviço e a dao - se alguém quiser garantir que o método DAO seja sempre chamado (propagado) a partir de uma camada de serviço com propagação "obrigatória" no DAO. Isso forneceria alguma restrição para os métodos DAO serem chamados da camada de interface do usuário (ou controladores). Além disso - ao testar a unidade DAO em particular - a anotação do DAO também garantirá que ela seja testada quanto à funcionalidade transacional.
propagation=Propagation.REQUIRES_NEW
. Caso contrário, na maioria dos casos, incluindo a propagação = obrigatória, o DAO participará apenas da transação existente iniciada pela camada de serviço.
Além disso, o Spring recomenda usar apenas a anotação em classes concretas e não em interfaces.
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Para transação no nível do banco de dados
usei principalmente @Transactional
no DAO apenas no nível do método, portanto a configuração pode ser específica para um método / usando o padrão (obrigatório)
O método do DAO que obtém a busca de dados (selecione ..) - não precisa
@Transactional
disso, pode levar a uma sobrecarga por causa do interceptador de transações / e do proxy AOP que precisam ser executados também.
Os métodos do DAO que inserem / atualizam receberão @Transactional
muito bom blog sobre transicional
Para o nível do aplicativo -
estou usando a lógica transacional para negócios, gostaria de poder reverter em caso de erro inesperado
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
Transactional
inJava
Geralmente, deve-se colocar uma transação na camada de serviço.
Mas, como afirmado anteriormente, a atomicidade de uma operação é o que nos diz onde uma anotação é necessária. Portanto, se você usar estruturas como o Hibernate, em que uma única operação "salvar / atualizar / excluir / ... modificação" em um objeto tem o potencial de modificar várias linhas em várias tabelas (por causa da cascata no gráfico de objetos), de Claro, também deve haver gerenciamento de transações nesse método DAO específico.
@Transactional
As anotações devem ser colocadas em torno de todas as operações que são inseparáveis. O uso da @Transactional
propagação de transações é tratado automaticamente. Nesse caso, se outro método for chamado pelo método atual, esse método terá a opção de ingressar na transação em andamento.
Então, vamos dar um exemplo:
Temos 2 modelos ie Country
e City
. O mapeamento relacional Country
e o City
modelo é como se você Country
pudesse ter várias cidades; portanto, o mapeamento é como,
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
Aqui, o país foi mapeado para várias cidades com sua busca Lazily
. Então, aqui vem a função de @Transactinal
quando recuperamos o objeto País do banco de dados, obteremos todos os dados do objeto País, mas não obteremos Conjunto de cidades porque estamos buscando cidades LAZILY
.
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
Quando queremos acessar o conjunto de cidades a partir do objeto país, obteremos valores nulos nesse conjunto porque o objeto do conjunto criado apenas este conjunto não é inicializado com os dados para obter valores do conjunto que usamos @Transactional
, ou seja,
//with @Transactional
@Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
Object object = country.getCities().size();
}
Então, basicamente, o @Transactional
Serviço pode fazer várias chamadas em uma única transação sem fechar a conexão com o ponto final.
@Transactional
realmente é
A @Transactional
deve ser usado em camada de serviço, uma vez que contém a lógica de negócios. A camada DAO geralmente possui apenas operações CRUD de banco de dados.
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
Documento da primavera: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
A camada de serviço é o melhor local para adicionar @Transactional
anotações, pois a maioria das lógicas de negócios presentes aqui contém o comportamento dos casos de uso no nível de detalhe.
Suponha que o adicionemos ao DAO e, a partir do serviço, estamos chamando duas classes do DAO, uma com falha e outra com êxito, neste caso se @Transactional
não no serviço, um banco de dados será confirmado e outro será revertido.
Portanto, minha recomendação é usar esta anotação com sabedoria e usar apenas na camada Serviço.
Primeiro de tudo, vamos definir onde devemos usar a transação ?
Eu acho que a resposta correta é: quando precisamos garantir que a sequência de ações seja concluída em conjunto como uma operação atômica ou que nenhuma alteração será feita, mesmo que uma das ações falhe.
É uma prática bem conhecida colocar lógica de negócios em serviços. Portanto, os métodos de serviço podem conter ações diferentes que devem ser executadas como uma única unidade lógica de trabalho. Se sim - então esse método deve ser marcado como Transacional . Obviamente, nem todo método exige essa limitação, portanto você não precisa marcar todo o serviço como transacional .
E ainda mais - não se esqueça de levar em consideração que o @Transactional obviamente pode reduzir o desempenho do método. Para ter uma visão geral, é necessário conhecer os níveis de isolamento da transação. Saber que isso pode ajudá-lo a evitar o uso do @Transactional quando não for necessariamente necessário.
É melhor manter @Transactional em uma camada intermediária separada entre o DAO e a Camada de Serviço. Como a reversão é muito importante, você pode colocar toda a manipulação do banco de dados na camada intermediária e escrever a lógica comercial na Camada de Serviço. A camada intermediária irá interagir com suas camadas DAO.
Isso o ajudará em muitas situações, como ObjectOptimisticLockingFailureException - essa exceção ocorre somente após o término da sua transação. Portanto, você não pode capturá-lo na camada intermediária, mas pode capturar sua camada de serviço agora. Isso não seria possível se você tiver @Transactional na camada Service. Embora você possa capturar o Controller, o Controlador deve estar o mais limpo possível.
Se você estiver enviando correio ou sms em um segmento separado após concluir todas as opções de salvar, excluir e atualizar, poderá fazer isso no serviço após a conclusão da transação na camada intermediária. Novamente, se você mencionar @Transactional na camada de serviço, o e-mail será enviado mesmo que sua transação falhe.
Portanto, ter uma camada @Transaction intermediária ajudará a tornar seu código melhor e fácil de manusear. Caso contrário, se você usar a camada DAO, talvez não seja possível reverter todas as operações. Se você usa a camada Serviço, pode ter que usar AOP (Aspect Oriented Programming) em certos casos.
Idealmente, a camada de serviço (Manager) representa sua lógica de negócios e, portanto, deve ser anotada com @Transactional
. A camada de serviço pode chamar DAO diferente para executar operações de banco de dados. Vamos assumir uma situação em que você tem N número de operações DAO em um método de serviço. Se sua primeira operação do DAO falhar, outras ainda poderão ser transmitidas e você acabará com o estado inconsistente do banco de dados. Anotar a camada Serviço pode salvá-lo de tais situações.
Eu prefiro usar @Transactional
na camada de serviços no nível do método.