簡體   English   中英

sequelize node.js出現唯一約束錯誤時如何跳過主鍵自增

[英]How do I skip the primary key auto increment in sequelize node js when unique constraint error occurs

當發生唯一約束錯誤時,如何在 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}...`));

output 結果運行良好。 但是primary Key自增還在繼續

http://localhost:4000/api/v1/post: 2022-08-28T11:02:47+03:00 執行 (ac12d76f-d7dc-4040-9692-3d6b853feac9): 開始交易; 執行(ac12d76f-d7dc-4040-9692-3d6b853feac9):插入postsidtitletextusernamecreatedAtupdatedAt )VALUES(默認,?,?,?,?,?); 執行(ac12d76f-d7dc-4040-9692-3d6b853feac9):回滾;

我嘗試了以下解決方案並完美地工作。

/* 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);
    }
};
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);
    }
};

我不確定以前版本的 sequelize 中是否存在問題。 但是,如果將 Object.findOrCreate() 與以下提到的版本一起使用,則不存在此問題。

但是,如果使用 Object.create() 方法並為字段值設置了唯一約束,並且在使用 Object.create() 之前不檢查字段值是否存在,例如在以下代碼中設置了 email 唯一屬性,並且如果 user.create()用於數據庫中現有的 email 會拋出錯誤,但 userid 會遞增,因此對於下一次成功創建,userid 與預期不同。

另一種解決方案是在使用 user.create() 之前使用 user.findOne() 但在這個答案的 scope 之外,可以使用 object.findOrCreate() 避免問題,如下所示

版本:“mysql2”:“^2.3.3”,“sequelize”:“^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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM