Transações
O Lucid tem suporte de primeira classe para transações e pontos de salvamento. Você pode criar uma nova transação chamando o método Database.transaction
.
import Database from '@ioc:Adonis/Lucid/Database'
// Transação criada
const trx = await Database.transaction()
Assim como o módulo Database
. Você também pode usar o objeto trx
para criar uma instância do construtor de consultas.
await trx
.insertQuery()
.table('users')
.insert({ username: 'virk' })
await trx
.query()
.select('*')
.from('users')
Depois de executar a consulta, você deve commit
ou rollback
a transação. Caso contrário, as consultas travarão até o tempo limite.
A seguir, um exemplo completo de uso de transações com uma consulta de inserção.
const trx = await Database.transaction()
try {
await trx
.insertQuery()
.table('users')
.insert({ username: 'virk' })
await trx.commit()
} catch (error) {
await trx.rollback()
}
Transações gerenciadas
O exemplo acima espera que você tenha que commit
ou rollback
manualmente as transações envolvendo seu código dentro de um bloco try/catch
. Uma transação gerenciada faz isso automaticamente para você.
Você pode criar uma transação gerenciada passando um retorno de chamada para o método transaction
.
- A transação é confirmada automaticamente após executar o retorno de chamada.
- Se um retorno de chamada gerar uma exceção, a transação será revertida automaticamente e lançará a exceção novamente.
await Database.transaction(async (trx) => {
await trx
.insertQuery()
.table('users')
.insert({ username: 'virk' })
})
Você também pode retornar um valor do retorno de chamada e acessá-lo no escopo de nível superior. Por exemplo:
const userId = await Database.transaction(async (trx) => {
const response = await trx
.insertQuery()
.table('users')
.insert({ username: 'virk' })
return response[0] // 👈 retorna o valor
})
Níveis de isolamento
Você pode definir o nível de isolamento de uma transação ao chamar o método Database.transaction
.
await Database.transaction({
isolationLevel: 'read uncommitted'
})
A seguir está um exemplo de definição do nível de isolamento com uma transação gerenciada.
await Database.transaction(async (trx) => {
// use trx aqui
}, {
isolationLevel: 'read committed'
})
A seguir está a lista de níveis de isolamento disponíveis.
- "read uncommitted"
- "read committed"
- "snapshot"
- "repeatable read"
- "serializable"
Passando transação como referência
A API de transações não se limita apenas a criar uma instância do construtor de consultas a partir de um objeto de transação. Você também pode passá-la para instâncias ou modelos existentes do construtor de consultas.
import Database from '@ioc:Adonis/Lucid/Database'
const trx = await Database.transaction()
Database
.insertQuery({ client: trx }) 👈
.table('users')
.insert({ username: 'virk' })
Ou passá-la em um estágio posterior usando o método useTransaction
.
import Database from '@ioc:Adonis/Lucid/Database'
const trx = await Database.transaction()
Database
.insertQuery()
.table('users')
.useTransaction(trx) 👈
.insert({ username: 'virk' })
Savepoints
Toda vez que você cria uma transação aninhada, o Lucid cria nos bastidores um novo savepoint. Como as transações precisam de uma conexão dedicada, usar savepoints reduz o número de conexões necessárias.
import Database from '@ioc:Adonis/Lucid/Database'
// A transação é criada
const trx = await Database.transaction()
// Desta vez, um ponto de salvamento é criado
const savepoint = await trx.transaction()
// também reverte o ponto de salvamento
await trx.rollback()
Usando transações com modelos Lucid
Você pode passar a transação para uma instância de modelo usando o método useTransaction
.
Na classe de modelo, você pode acessar o objeto de transação usando a propriedade this.$trx
. A propriedade só está disponível durante uma transação em andamento. Após ``commitou
rollback, ela será redefinida para
undefined`.
import User from 'App/Models/User'
import Database from '@ioc:Adonis/Lucid/Database'
await Database.transaction(async (trx) => {
const user = new User()
user.username = 'virk'
user.useTransaction(trx)
await user.save()
})
Construtor de consulta de modelo
Assim como o construtor de consulta padrão, você também pode passar a transação para o construtor de consulta de modelo.
import Database from '@ioc:Adonis/Lucid/Database'
import User from 'App/Models/User'
const trx = await Database.transaction()
const users = await User
.query({ client: trx }) 👈
.where('is_active', true)
Persistindo relacionamentos dentro de uma transação
O caso de uso mais comum para transações é persistir relacionamentos. Considere o seguinte exemplo de criação de um novo usuário e seu perfil envolvendo-os dentro de uma única transação.
import Database from '@ioc:Adonis/Lucid/Database'
import User from 'App/Models/User'
await Database.transaction(async (trx) => {
const user = new User()
user.username = 'virk'
user.useTransaction(trx)
await user.save()
/**
* O relacionamento referenciará implicitamente a
* transação da instância do usuário
*/
await user.related('profile').create({
fullName: 'Harminder Virk',
avatar: 'some-url.jpg',
})
})
No exemplo a seguir, buscamos um usuário existente e criamos um novo perfil para ele.
import Database from '@ioc:Adonis/Lucid/Database'
import User from 'App/Models/User'
await Database.transaction(async (trx) => {
const user = await User.findOrFail(1, { client: trx })
/**
* O relacionamento referenciará implicitamente a
* transação da instância do usuário
*/
await user.related('profile').create({
fullName: 'Harminder Virk',
avatar: 'some-url.jpg',
})
})