首页 > 解决方案 > 使用事务在 AdonisJS 中插入关系

问题描述

我是这个 JS Word 的新手,我阅读了 Adonis 和 Knex 文档,但我真的不明白我在这里做错了什么。

阿多尼斯:https ://preview.adonisjs.com/guides/database/transactions和https://adonisjs.com/docs/4.1/lucid#_transactions

knex:http ://knexjs.org/#Transactions

在没有任何问题的情况下,所有数据都存储没有问题,但是当我提出错误只是为了测试事务时,我可以注意到“empresas”表有一条记录而其他表是空的,我想通过事务的 rollback() 函数,当发生错误时,所有表都需要为空。有人可以在这里启发我吗?使用:Adonis 和 postgres

这是我的迁移:

表empresas:

'use strict'

/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')

class EmpresaSchema extends Schema {
  up () {
    this.create('empresas', (table) => {
      table.increments()
      table.text('codigo').notNullable()
      table.text('tipo').notNullable()
      table.text('origem').notNullable()
      table.bigInteger('grupo_id').notNullable()
      table.text('nome_fantasia')
      table.text('razao_social')
      table.timestamps()
    })
  }

  down () {
    this.drop('empresas')
  }
}

module.exports = EmpresaSchema

表内容:

'use strict'

/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')

class ContatosSchema extends Schema {
  up () {
    this.create('contatos', (table) => {
      table.increments()
      table
        .integer('tipo_id')
        .unsigned()
        .notNullable()
        .references('id')
        .inTable('contato_tipos')
      table.text('nome').notNullable()
      table.text('dado').notNullable()
      table.timestamps()
    })
  }

  down () {
    this.drop('contatos')
  }
}

module.exports = ContatosSchema

表empresa_contatos:

'use strict'

/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')

class EmpresaContatosSchema extends Schema {
  up () {
    this.create('empresa_contatos', (table) => {
      table.increments()
      table
        .integer('empresa_id')
        .unsigned()
        .references('id')
        .inTable('empresas')
        .onUpdate('CASCADE')
        .onDelete('CASCADE')
        .index()

      table
        .integer('contato_id')
        .unsigned()
        .references('id')
        .inTable('contatos')
        .onUpdate('CASCADE')
        .onDelete('CASCADE')
        .index()
    })
  }

  down () {
    this.drop('empresa_contatos')
  }
}

module.exports = EmpresaContatosSchema

我的模型:

企业模式:

'use strict'

/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')

class Empresa extends Model {
  contatos () {
    return this.belongsToMany('App/Models/Contato').pivotTable('empresa_contatos')
  }
}

module.exports = Empresa

联系型号:

'use strict'

/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')

class Contato extends Model {
  static get table () {
    return 'contatos'
  }

  empresa () {
    return this.belongsToMany('App/Models/Empresa').pivotTable('empresa_contatos')
  }
}

module.exports = Contato

这是我的 EmpresaController 的一部分:

'use strict'

const Database = use('Database')
const Empresa = use('App/Models/Empresa')
const Contato = use('App/Models/Contato')
const Sequence = use('App/Controllers/Http/SequenceController')


class EmpresaController {
  async customCreate ({ request, response, auth }) {
    const trx = await Database.beginTransaction()
    try {
      const { enderecos, contatos, ...data } = request.all()
      if (data.endereco.logradouro !== '') {
        enderecos.push(data.endereco)
      }

      if (data.contato.nome !== '') {
        contatos.push(data.contato)
      }

      const codigo = await Sequence.gerarNovoCodigoCliente(auth.user.grupo_id, trx)

      let empresa = null
      await Empresa.create({
        codigo: codigo,
        grupo_id: 1,
        tipo: data.tipo,
        origem: data.origem,
        nome_fantasia: data.nome_fantasia,
        razao_social: data.razao_social
      }, trx)
        .then(response => {
          empresa = response
        })
        .catch(error => {
          console.log('error empresa:')
          console.log(error)
        })

      contatos.forEach(async contato => {
        let novosContato = null

        await Contato.create({
          nome: contato.nome,
          dado: contato.dado,
          tipo_id: contato.tipo_contato.id
        }, trx)
          .then(response => {
            novosContato = response
            console.log('contato ok')
          })
          .catch(error => {
            console.log('error contato:')
            console.log(error)
          })

        await empresa.contatos().attach(novosContato.id, null, trx)
          .then(response => {
            console.log('attach ok')
          })
          .catch(error => {
            console.log('error attach:')
            console.log(error)
          })
      })

      await trx.commit()
      return response.ok(empresa)
    } catch (error) {
      await trx.rollback()
      return response.badRequest(error.message)
    }
  }
}

module.exports = EmpresaController

我已经尝试将每个异步函数放在一个 const 上并使用 promise.all 方法解决所有问题,并遇到同样的问题,我真的不知道,但我猜“const empresa = await Empresa.create(...... .)" 正在提交事务并在它之后运行其他所有内容。

(编辑)日志:

error contato:
Error: Transaction query already complete, run with DEBUG=knex:tx for more info
    at completedError (/app/node_modules/knex/lib/transaction.js:261:9)
    at /app/node_modules/knex/lib/transaction.js:231:22
From previous event:
    at Client_PG.trxClient.query (/app/node_modules/knex/lib/transaction.js:229:33)
    at Runner.<anonymous> (/app/node_modules/knex/lib/runner.js:138:36)
From previous event:
    at /app/node_modules/knex/lib/runner.js:47:21
    at processImmediate (internal/timers.js:461:21)
From previous event:
    at Runner.run (/app/node_modules/knex/lib/runner.js:33:30)
    at Builder.Target.then (/app/node_modules/knex/lib/interface.js:23:43)
warning: 
  WARNING: Adonis has detected an unhandled promise rejection, which may
  cause undesired behavior in production.
  To stop this warning, use catch() on promises or wrap await
  calls inside try/catch.

TypeError: Cannot read property 'id' of null
    at /app/app/Controllers/Http/EmpresaController.js:165:54

标签: postgresqlasync-awaittransactionsadonis.js

解决方案


您还需要传递交易参考来创建...

只需在此处添加 trx:

  const empresa = await Empresa.create({
    codigo: codigo,
    grupo_id: 1,
    tipo: data.tipo,
    origem: data.origem,
    nome_fantasia: data.nome_fantasia,
    razao_social: data.razao_social
  }, trx)   <-- adding trx to create!

更多信息在这里:https ://adonisjs.com/docs/4.1/lucid#_transactions


推荐阅读