mysql 当发生唯一约束错误时,如何跳过nodejsORM sequelize中的主键自动递增

hwamh0ep  于 2023-01-04  发布在  Mysql
关注(0)|答案(3)|浏览(71)

当发生唯一约束错误时,我如何跳过sequelize node.js中的主键自动递增当我使用Postman将相同的用户名输入两次时,我的程序以正确的方式运行,但问题是递增的主键仍在继续。例如当我插入另一个不同的用户名值时,程序会像预期的那样跳转到一个连续的主键,所以,如何才能停止自动递增id,因为我已限制不能在数据库中插入重复的用户名值

/* DATABASE CONFIGURATION FILE */
    const { Sequelize, QueryTypes, DataTypes, Op, UniqueConstraintError, ValidationErrorItem } = require(`sequelize`);

    const sequelize = new Sequelize(`tutorialdb`, `root`, ``, {
        host: `localhost`,
        dialect: `mysql`,
        logging: true,
        pool: {
            max: 5,
            min: 0,
            acquire: 30000,
            idle: 10000,
        },
    });

    sequelize
    .authenticate()
    .then(() => {
    console.log(`Connection has been established successfully...`);
    })
    .catch((err) => {
    console.log(`Unable to connect to the database: `, err);
    });

    const db = {};
    db.Sequelize = Sequelize;
    db.sequelize = sequelize;
    db.QueryTypes = QueryTypes;
    db.DataTypes = DataTypes;
    db.Op = Op;
    db.ValidationErrorItem = ValidationErrorItem;
    db.UniqueConstraintError = UniqueConstraintError;

    db.postModel = require(`../models/post.model.jsx`)(sequelize, DataTypes);

    db.sequelize.sync({ force: false, alter: false, match: /tutorialdb$/ }).then(() => {
    console.log(`Tables were synced successfully`);
    });

    module.exports = db;

    /* Model definition File */
    module.exports = (sequelize, DataTypes) => {
    const Post = sequelize.define(
    `post`,
    {
        id: {
            type: DataTypes.INTEGER.UNSIGNED,
            allowNull: false,
            primaryKey: true,
            autoIncrement: true,
        },
        title: {
            type: DataTypes.STRING(30),
            allowNull: false,
            validate: {
                notEmpty: {
                    args: true,
                    msg: `Title is required`,
                },
                len: {
                    args: [3, 50],
                    msg: `Title must between 3 and 30 characters`,
                },
            },
        },
        text: {
            type: DataTypes.STRING(100),
            allowNull: false,
            validate: {
                notEmpty: {
                    args: true,
                    msg: `Text is required`,
                },
                len: {
                    args: [5, 100],
                    msg: `Text must between 5 and 100 characters`,
                },
            },
        },
        username: {
            type: DataTypes.STRING(20),
            allowNull: false,
            unique: true,
            validate: {
                notEmpty: {
                    args: true,
                    msg: `Username is required`,
                },
                len: {
                    args: [3, 20],
                    msg: `Username must between 3 and 20 characters`,
                },
            },
        },
    },
    {
        timestamps: true,
        paranoid: true,
    }
    );

    Post.beforeCreate(async (post, options) => {
        post.username = post.username.toLowerCase();
    });

    Post.beforeUpdate(async (post, options) => {
        post.username = post.username.toLowerCase();
    });

    return Post;
    };

    /* Controller File */
    const db = require(`../config/db.config.jsx`);
    const postModel = db.postModel;

    const Sequelize = db.Sequelize;
    const sequelize = db.sequelize;
    const QueryTypes = db.QueryTypes;
    const DataTypes = db.DataTypes;
    const Op = db.Op;
    const ValidationErrorItem = db.ValidationErrorItem;
    const UniqueConstraintError = db.UniqueConstraintError;

    /* Create new Post */
    exports.create = async (req, res) => {
        const transactions = await sequelize.transaction();
        try {
            const trim = (noSpace) => {
                return noSpace.replace(/\s/g, ``);
            };
            const post = await postModel.create(
                {
                    title: req.body.title,
                    text: req.body.text,
                    username: trim(req.body.username),
            },
            { transaction: transactions }
        );
        await transactions.commit();
        res.status(200).json(post);
        } catch (err) {
            await transactions.rollback();
            const messages = {};
            let message;
            err.errors.forEach((error) => {
                messages[error.path] = error.message;
                message = messages[error.path];
            });
            res.status(500).json(message);
        }
    };

    /* Find All posts */
    exports.findAll = async (req, res) => {
        const transactions = await sequelize.transaction();
        try {
            const title = req.query.title;
            const text = req.query.text;
            const username = req.query.username;
            let finder = title ? { title: { [Op.like]: `%${title}%` } } : text ? { text: { [Op.like]: `%${text}%` } } : username ? { username: { [Op.like]: `%${username}%` } } : null;
            const posts = await postModel.findAll({
                as: `posts`,
                attributes: [`id`, `title`, `text`, `username`, `createdAt`, `updatedAt`, `deletedAt`],
                transaction: transactions,
                lock: false,
                paranoid: false,
                order: [[`id`, `DESC`]],
                where: finder,
            });
            await transactions.commit();
            res.status(200).json(posts);
        } catch (err) {
            await transactions.rollback();
            res.status(500).json(err.message);
        }
    };

    /* Router File */

    module.exports = (app) => {
        const router = require(`express`).Router();
        const postCtrl = require(`../controllers/post.controller.jsx`);

        router.route(`/post`).post(postCtrl.create).get(postCtrl.findAll);

        app.use(`/api/v1`, router);
    };

    /* MiddleWare Logger File */

    const moment = require(`moment`);

    /* Create Logger */
    const logger = (req, res, next) => {
        console.log(`${req.protocol}://${req.get(`host`)}${req.originalUrl} : ${moment().format()}`);
        next();
    };

    module.exports = logger;

    /* Server File */
    const express = require(`express`);
    const cors = require(`cors`);
    const logger = require(`./src/middleware/logger.jsx`);
    const app = express();

    const corsOptions = {
    origin: `http://localhost:4001`,
    optionsSuccessStatus: 200,
    };

    app
    .use(cors(corsOptions))
    .use(logger)
    .use(express.json())
    .use(express.urlencoded({ extended: false }))
    .get(`/`, (req, res) => res.status(200).send(`Welcome to fullstack tutorial application`));

    require(`./src/routes/routers.jsx`)(app);

    const PORT = process.env.PORT || 4000;
    app.listen(PORT, () => console.log(`Server is running on port ${PORT}...`));

输出结果运行良好,但主键自动递增仍在继续
网址:2022年8月28日星期一11:02:47+03:00执行(操作9):开始交易;执行(操作编号12 d 76 f-d 7 dc-4040-9692- 3d 6 b853 feac 9):插入postsidtitletextusernamecreatedAtupdatedAt)值(默认值、?、?、?、?);执行(操作编号12 d 76 f-d 7 dc-4040-9692- 3d 6 b853 feac 9):回滚;

w8f9ii69

w8f9ii691#

我曾尝试过以下解决方案,并完美地工作我。

/* Create new User */
exports.create = async (req, res) => {
    const trim = (noSpace) => {
        return noSpace.replace(/\s/g, ``);
    };
    const transactions = await sequelize.transaction();
    try {
        const { username, password } = req.body;
        const users = await userModel.findOne({
            where: { username: trim(username) },
            transaction: transactions,
        });

        if (users !== null) {
            await transactions.rollback();
            res.json(`Username ${username} already exist`);
        } else {
            const user = await userModel.create(
                {
                    username: trim(username),
                    password: trim(password),
                },
                {
                    transaction: transactions,
                }
            );
            await transactions.commit();
            res.status(200).json(user);
        }
    } catch (err) {
        await transactions.rollback();
        const messages = {};
        let message;
        err.errors.forEach((error) => {
            messages[error.path] = error.message;
            message = messages[error.path];
        });
        res.status(500).json(message);
    }
};
mlnl4t2r

mlnl4t2r2#

exports.create = async (req, res) => {
    const transactions = await sequelize.transaction();
    try {
        const trim = (noSpace) => {
            return noSpace.replace(/\s/g, ``);
        };
        const [user, created] = await userModel.findOrCreate({
            where: { username: trim(req.body.username) },
            defaults: { password: trim(req.body.password) },
            transaction: transactions,
        });
        return created ? (await transactions.commit(), res.status(200).json(user)) : user ? (await transactions.rollback(), res.json(`Username already exist`)) : err;
    } catch (err) {
        await transactions.rollback();
        const messages = {};
        let message;
        err.errors.forEach((error) => {
            messages[error.path] = error.message;
            message = messages[error.path];
        });
        res.status(500).json(message);
    }
};
lfapxunr

lfapxunr3#

我不确定以前版本的sequelize是否存在这个问题。但是如果在下面提到的版本中使用Object.findOrCreate(),这个问题就不存在了。
但是,如果使用Object.create()方法,为字段值设置唯一约束,并且在使用Object.create()之前未检查字段值是否存在,则会出现此问题,例如,在以下代码中,设置了电子邮件唯一属性,并且如果将user.create()用于数据库中的现有电子邮件,则会引发错误,但userid会递增,因此对于下一次成功创建,userid与预期不符。
另一种解决方案是在使用user.create()之前使用user.findOne(),但这超出了本答案的范围,可以使用object.findOrCreate()避免问题,如下所示
版本:“mysql2”:“^2.3.3”,“续作”:“^6.28.0”
要避免此问题,请尝试使用以下方法

const router = require("express").Router();
const { Sequelize, DataTypes, Model } = require("sequelize");
const dotenv = require("dotenv");

dotenv.config();

const sequelize = new Sequelize(
  process.env.MYSQL_DB_NAME,
  process.env.MYSQL_DB_USER,
  process.env.MYSQL_DB_PASS,
  {
    host: process.env.MYSQL_DB_HOST,
    dialect: "mysql",
  }
);

class User extends Model {}

User.init(
  {
    userid: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
      field: "fUserID",
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true,
      field: "fEmail",
    },
    password: {
      type: DataTypes.STRING(1024),
      allowNull: false,
      field: "fPassword",
    },
    firstname: {
      type: DataTypes.STRING,
      field: "fFirstName",
    },
    lastname: {
      type: DataTypes.STRING,
      field: "fLastName",
    },
    metadata: {
      type: DataTypes.STRING(2048),
      field: "fMetaData",
    },
    created: {
      type: DataTypes.DATE,
      field: "fCreated",
    },
    updated: {
      type: DataTypes.DATE,
      field: "fUpdated",
    },
  },
  {
    sequelize,
    tableName: "tbl_user",
    timestamps: true,
    id: "userid",
    createdAt: "created",
    updatedAt: "updated",
  }
);

router.post("/register", async (req, res) => {
  try {
    const [user, created] = await User.findOrCreate({
      where: { email: req.body.email },
      defaults: {
        password: req.body.password,
        firstname: req.body.firstname,
        lastname: req.body.lastname,
        metadata: "Any thing",
      },
    });

    if (created === false) return res.status(400).send("email already exist");

    res.send(user.toJSON());
  } catch (ex) {
    res.status(400).send(ex.errors[0].message);
  }
});

module.exports = router;

相关问题