Respostas:
Boa pergunta, embora não seja trivial de responder.
Define como as transações se relacionam. Opções comuns:
Required
: O código sempre será executado em uma transação. Cria uma nova transação ou reutiliza uma, se disponível.Requires_new
: O código sempre será executado em uma nova transação. Suspende a transação atual, se houver.Define o contrato de dados entre transações.
Read Uncommitted
: Permite leituras sujas.Read Committed
: Não permite leituras sujas.Repeatable Read
: Se uma linha for lida duas vezes na mesma transação, o resultado será sempre o mesmo.Serializable
: Executa todas as transações em uma sequência.Os diferentes níveis têm características de desempenho diferentes em um aplicativo multithread. Eu acho que se você entender o dirty reads
conceito, poderá selecionar uma boa opção.
Exemplo de quando uma leitura suja pode ocorrer:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
Portanto, pode ser um padrão sensato (se é que pode ser reivindicado) Read Committed
, que apenas permite ler valores que já foram confirmados por outras transações em execução, em combinação com um nível de propagação de Required
. Em seguida, você poderá trabalhar a partir daí se o seu aplicativo tiver outras necessidades.
Um exemplo prático de onde uma nova transação sempre será criada ao entrar na provideService
rotina e concluída ao sair:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
Se tivéssemos usado Required
, a transação permaneceria aberta se a transação já estivesse aberta ao entrar na rotina. Observe também que o resultado de a rollback
pode ser diferente, pois várias execuções podem participar da mesma transação.
Podemos facilmente verificar o comportamento com um teste e ver como os resultados diferem com os níveis de propagação:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
Com um nível de propagação de
Requires new
: esperaríamos fooService.provideService()
era não revertida desde que criou seu próprio sub-transação.
Required
: esperamos que tudo tenha sido revertido e a loja de backup permaneça inalterada.
sessionFactory.getCurrentTransaction()
foi adicionado, não há mais a necessidade de executar HibernateTemplate
para gerenciar transações. Tirei-:)
PROPAGATION_REQUIRED = 0 ; Se o DataSourceTransactionObject T1 já estiver iniciado para o Método M1, se for necessário um objeto de Transação do Método M2, nenhum novo objeto de Transação será criado. O mesmo objeto T1 será usado para o M2
PROPAGATION_MANDATORY = 2 ; O método deve ser executado dentro de uma transação. Se nenhuma transação existente estiver em andamento, uma exceção será lançada
PROPAGATION_REQUIRES_NEW = 3 ; Se o DataSourceTransactionObject T1 já estiver iniciado para o Método M1 e estiver em andamento (executando o método M1) .Se outro método M2 iniciar a execução, T1 será suspenso pela duração do método M2 com o novo DataSourceTransactionObject T2 para M2.M2 executado em seu próprio contexto de transação
PROPAGATION_NOT_SUPPORTED = 4 ; Se o DataSourceTransactionObject T1 já tiver sido iniciado para o Método M1. Se outro método M2 for executado simultaneamente, o M2 não deverá ser executado no contexto da transação. T1 é suspenso até que M2 seja finalizado.
PROPAGATION_NEVER = 5 ; Nenhum dos métodos é executado no contexto da transação.
Um nível de isolamento: trata-se de quanto uma transação pode ser afetada pelas atividades de outras transações simultâneas . Ele suporta a consistência, deixando os dados em várias tabelas em um estado consistente. Envolve o bloqueio de linhas e / ou tabelas em um banco de dados.
O problema com várias transações
Cenário 1 .Se a transação T1 ler dados da tabela A1 que foram gravados por outra transação simultânea T2.Se no modo T2 for reverso, os dados obtidos por T1 serão inválidos.Eg a = 2 são dados originais.Se T1 ler um = Se a reversão de T2, em seguida, a = 1 será revertida para a = 2 no DB.Mas agora, T1 tem a = 1, mas na tabela DB é alterada para a = 2.
Cenario2 .Se T1 transação lê dados da tabela A1.If outra transação simultânea (T2) atualizar os dados na mesa A1.Then os dados que T1 tenha lido é diferente da tabela A1.Because T2 actualiza os dados na tabela A1.Eg se T1 leia a = 1 e T2 atualizou a = 2.Então a! = b.
Cenário 3. Se a transação T1 ler dados da tabela A1 com determinado número de linhas. Se outra transação simultânea (T2) inserir mais linhas na tabela A1. O número de linhas lidas por T1 será diferente das linhas da tabela A1
O cenário 1 é chamado de leituras sujas.
O cenário 2 é chamado de leituras não repetíveis.
O cenário 3 é chamado de leituras fantasmas.
Portanto, o nível de isolamento é a extensão na qual o cenário 1, cenário 2 e cenário 3 podem ser evitados. Você pode obter um nível completo de isolamento implementando o bloqueio. Isso impede que ocorram leituras e gravações simultâneas nos mesmos dados. Mas isso afeta o desempenho. O nível de isolamento depende de aplicativo para aplicativo quanto de isolamento é necessário.
ISOLATION_READ_UNCOMMITTED : Permite ler alterações que ainda não foram confirmadas. Elas sofrem do cenário 1, cenário 2, cenário 3
ISOLATION_READ_COMMITTED : Permite leituras de transações simultâneas que foram confirmadas. Pode sofrer dos cenários 2 e 3. Como outras transações podem estar atualizando os dados.
ISOLATION_REPEATABLE_READ : Várias leituras do mesmo campo produzirão os mesmos resultados até que sejam alteradas por si só.Pode sofrer com o Cenário 3.Porque outras transações podem estar inserindo os dados
ISOLATION_SERIALIZABLE : o cenário 1, o cenário 2, o cenário 3 nunca acontece. É um isolamento completo. Envolve um bloqueio completo. Afeta o desempenho devido ao bloqueio.
Você pode testar usando
public class TransactionBehaviour {
// set is either using xml Or annotation
DataSourceTransactionManager manager=new DataSourceTransactionManager();
SimpleTransactionStatus status=new SimpleTransactionStatus();
;
public void beginTransaction()
{
DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
// overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
// set is either using xml Or annotation
manager.setPropagationBehavior(XX);
manager.setIsolationLevelName(XX);
status = manager.getTransaction(Def);
}
public void commitTransaction()
{
if(status.isCompleted()){
manager.commit(status);
}
}
public void rollbackTransaction()
{
if(!status.isCompleted()){
manager.rollback(status);
}
}
Main method{
beginTransaction()
M1();
If error(){
rollbackTransaction()
}
commitTransaction();
}
}
Você pode depurar e ver o resultado com diferentes valores para isolamento e propagação.
Explicações suficientes sobre cada parâmetro são dadas por outras respostas; No entanto, você pediu um exemplo do mundo real, aqui está o que esclarece o propósito de diferentes opções de propagação :
Suponha que você esteja encarregado de implementar um serviço de inscrição no qual um email de confirmação é enviado ao usuário. Você cria dois objetos de serviço, um para registrar o usuário e outro para enviar e-mails, os quais são chamados dentro do primeiro. Por exemplo, algo como isto:/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
...
void SignUp(User user){
...
emailService.sendMail(User);
}
}
/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
...
void sendMail(User user){
try{
... // Trying to send the e-mail
}catch( Exception)
}
}
Você deve ter notado que o segundo serviço é do tipo propagação REQUIRES_NEW e, além disso, é possível que exista uma exceção (servidor SMTP inativo, email inválido ou outros motivos). Você provavelmente não deseja que todo o processo seja revertido, como remover as informações do usuário do banco de dados ou outras coisas; portanto, você chama o segundo serviço em uma transação separada.
De volta ao nosso exemplo, desta vez você se preocupa com a segurança do banco de dados e define suas classes DAO da seguinte maneira:/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
// some CRUD methods
}
Significando que sempre que um objeto DAO e, portanto, um acesso potencial ao db, é criado, precisamos garantir que a chamada foi feita de dentro de um de nossos serviços, implicando a existência de uma transação ativa; caso contrário, ocorrerá uma exceção. Portanto, a propagação é do tipo MANDATORY .
O nível de isolamento define como as alterações feitas em um repositório de dados por uma transação afetam outras transações simultâneas simultâneas e também como e quando esses dados alterados ficam disponíveis para outras transações. Quando definimos uma transação usando a estrutura Spring, também podemos configurar em qual nível de isolamento a mesma transação será executada.
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
O nível de isolamento READ_UNCOMMITTED afirma que uma transação pode ler dados que ainda não foram confirmados por outras transações.
O nível de isolamento READ_COMMITTED afirma que uma transação não pode ler dados que ainda não foram confirmados por outras transações.
O nível de isolamento REPEATABLE_READ indica que, se uma transação lê um registro do banco de dados várias vezes, o resultado de todas essas operações de leitura deve sempre ser o mesmo.
O nível de isolamento SERIALIZÁVEL é o mais restritivo de todos os níveis de isolamento. As transações são executadas com bloqueio em todos os níveis (bloqueio de leitura, intervalo e gravação), para que pareçam ter sido executadas de maneira serializada.
Propagação é a capacidade de decidir como os métodos de negócios devem ser encapsulados em transações lógicas ou físicas.
O comportamento Spring REQUIRED significa que a mesma transação será usada se já houver uma transação aberta no contexto de execução do método do bean atual.
O comportamento de REQUIRES_NEW significa que uma nova transação física será sempre criada pelo contêiner.
O comportamento NESTED faz transações Spring aninhadas para usar a mesma transação física, mas define pontos de salvamento entre invocações aninhadas para que as transações internas também possam reverter independentemente das transações externas.
O comportamento MANDATORY indica que uma transação aberta existente já deve existir. Caso contrário, uma exceção será lançada pelo contêiner.
O comportamento NUNCA afirma que uma transação aberta existente ainda não deve existir. Se existir uma transação, uma exceção será lançada pelo contêiner.
O comportamento NOT_SUPPORTED será executado fora do escopo de qualquer transação. Se uma transação aberta já existir, ela será pausada.
O comportamento SUPPORTS será executado no escopo de uma transação se uma transação aberta já existir. Se ainda não houver uma transação aberta, o método será executado de qualquer maneira, mas de maneira não transacional.
Uma transação representa uma unidade de trabalho com um banco de dados.
Na Primavera de TransactionDefinition
interface que define as propriedades de transação Primavera conformes. @Transactional
A anotação descreve atributos de transação em um método ou classe.
@Autowired
private TestDAO testDAO;
@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {
// Interact with testDAO
}
Propagação (reprodução): é usada para a relação entre transações. (análoga à comunicação entre threads java)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value | Propagation | Description |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| -1 | TIMEOUT_DEFAULT | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
| 0 | PROPAGATION_REQUIRED | Support a current transaction; create a new one if none exists. |
| 1 | PROPAGATION_SUPPORTS | Support a current transaction; execute non-transactionally if none exists. |
| 2 | PROPAGATION_MANDATORY | Support a current transaction; throw an exception if no current transaction exists. |
| 3 | PROPAGATION_REQUIRES_NEW | Create a new transaction, suspending the current transaction if one exists. |
| 4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally. |
| 5 | PROPAGATION_NEVER | Do not support a current transaction; throw an exception if a current transaction exists. |
| 6 | PROPAGATION_NESTED | Execute within a nested transaction if a current transaction exists. |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
Isolamento: O isolamento é uma das propriedades ACID (Atomicidade, Consistência, Isolamento, Durabilidade) das transações do banco de dados. O isolamento determina como a integridade da transação é visível para outros usuários e sistemas. Ele usa para bloqueio de recursos, ou seja, controle de concorrência, verifique se apenas uma transação pode acessar o recurso em um determinado ponto.
Percepção de bloqueio: o nível de isolamento determina a duração em que os bloqueios são mantidos.
+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode | Read | Insert | Update | Lock Scope |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED | uncommitted data | Allowed | Allowed | No Lock |
| READ_COMMITTED (Default) | committed data | Allowed | Allowed | Lock on Committed data |
| REPEATABLE_READ | committed data | Allowed | Not Allowed | Lock on block of table |
| SERIALIZABLE | committed data | Not Allowed | Not Allowed | Lock on full table |
+---------------------------+-------------------+-------------+-------------+------------------------+
Percepção de leitura: os seguintes 3 tipos de problemas principais ocorrem:
UPDATES
de outra TX.INSERTS
e / ou DELETES
de outra txNíveis de isolamento com diferentes tipos de leituras:
+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode | Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED | allows | allows | allows |
| READ_COMMITTED (Default) | prevents | allows | allows |
| REPEATABLE_READ | prevents | prevents | allows |
| SERIALIZABLE | prevents | prevents | prevents |
+---------------------------+----------------+----------------------+----------------+
Você quase nunca deseja usar, Read Uncommited
pois não é realmente ACID
compatível. Read Commmited
é um bom ponto de partida padrão. Repeatable Read
provavelmente é necessário apenas em cenários de relatórios, rollup ou agregação. Observe que muitos bancos de dados, incluindo o postgres, na verdade não suportam a leitura repetida; você deve usar Serializable
. Serializable
é útil para coisas que você sabe que precisam acontecer completamente independentemente de qualquer outra coisa; pense nisso como synchronized
em Java. Serializable anda de mãos dadas com a REQUIRES_NEW
propagação.
Eu uso REQUIRES
para todas as funções que executam consultas UPDATE ou DELETE, bem como funções de nível de "serviço". Para funções no nível DAO que executam apenas SELECTs, utilizo as SUPPORTS
que participarão de um TX se uma já estiver iniciada (isto é, sendo chamada de uma função de serviço).
Isolamento de Transação e Propagação de Transação, embora relacionados, mas são claramente dois conceitos muito diferentes. Nos dois casos, os padrões são personalizados no componente de limite do cliente, usando o gerenciamento de transações declarativo ou o gerenciamento de transações programáticas . Detalhes de cada nível de isolamento e atributos de propagação podem ser encontrados nos links de referência abaixo.
Para duas ou mais transações / conexões em execução em um banco de dados, como e quando as alterações feitas pelas consultas em uma transação impactam / são visíveis para as consultas em uma transação diferente. Também estava relacionado a que tipo de bloqueio de registro do banco de dados será usado para isolar as alterações nessa transação de outras transações e vice-versa. Isso geralmente é implementado pelo banco de dados / recurso que está participando da transação.
.
Em um aplicativo corporativo para qualquer solicitação / processamento, há muitos componentes envolvidos para realizar o trabalho. Alguns desses componentes marcam os limites (início / fim) de uma transação que será usada no respectivo componente e seus subcomponentes. Para esse limite transacional de componentes, a Propagação de Transação especifica se o respectivo componente participará ou não da transação e o que acontecerá se o componente de chamada já tiver ou não uma transação já criada / iniciada. É o mesmo que os atributos de transação do Java EE. Isso geralmente é implementado pelo gerenciador de transações / conexões do cliente.
Referência:
Eu corri outerMethod
, method_1
e method_2
com modo de propagação diferente.
Abaixo está a saída para o modo de propagação diferente.
Método externo
@Transactional
@Override
public void outerMethod() {
customerProfileDAO.method_1();
iWorkflowDetailDao.method_2();
}
Método 1
@Transactional(propagation=Propagation.MANDATORY)
public void method_1() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "XXX");
session.save(entity);
System.out.println("Method - 1 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Método_2
@Transactional()
@Override
public void method_2() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "CCC");
session.save(entity);
int i = 1/0;
System.out.println("Method - 2 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Podemos adicionar para isso:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
Você pode usar assim:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
Você pode usar essa coisa também:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}