feng xiaohan

Sequelize

简介

Sequelize 是一个基于promiseNode.js ORM(对象关系映射库—Object-Relational Mapping Library)。它具有强大的事务支持,关联关系,预读和延迟加载,读取复制等功能。

它将所有的SQL语句代码映射到Javascript对象中(封装),然后导出变成我们可调用的方法,这样我们就不用自己编写SQL语句代码

例如:

INSERT INTO users VALUE ('Jon', 28, 'HJDFKLS')

——>

const user = User.create({ name: 'Jon', age: 28, password: 'HJDFKLS'})

安装

注意:sequelize需要安装数据库的驱动程序,我使用的数据库是MySQL,所以需要安装mysql2包!

npm install --save sequelize

使用

连接数据库

database.js

const Sequelize = require('sequelize'); // 引入sequelize

const sequelize = new Sequelize('node-complete', 'root', 'rootpassword', { // 数据库的库名,用户名,密码以及其他配置
  dialect: 'mysql', // 数据库语言
  host: 'localhost', // 连接服务器
});

module.exports = sequelize; // @1

@1:导出的sequelize对象本质上也是数据库连接池(同mysql2连接),只不过通过sequelize管理后我们能使用其他的一些功能。

定义新模型

使用define()来定义一个新的模型(数据表),它的第一个参数为模型名称(表名),第二个参数为模型的各个参数(表中字段):

models/product.js

const Sequelize = require('sequelize');

const sequelize = require('../util/database');

const Product = sequelize.define('product', {
  id: {
    type: Sequelize.INTEGER, // 类型
    autoIncrement: true, // 是否自增
    allowNull: false, // 是否为Null
    primaryKey: true // 主键
  },
  title: Sequelize.STRING,
  price: {
    type: Sequelize.DOUBLE,
    allowNull: false
  },
  imageUrl: {
    type: Sequelize.STRING,
    allowNull: false
  },
  description: {
    type: Sequelize.STRING,
    allowNull: false
  }
});

module.exports = Product;

创建新的表

将创建的模型转为数据库中的表

app.js

...
const sequelize = require('./util/database');

sequelize
  .sync() // @2
  .then(result => {
    app.listen(3000);
  })
  .catch(err => {
    console.log(err);
  });
...

注:如果数据库中没有这个表,则创建;如果已经存在这个表则不会覆盖原先的表

@2:如果已经创建了表,想要新建一个覆盖原来的表,可以设置force

.sync( { force: true })

表创建完需删除force,避免每次更新项目都重新创建新表。

之后数据库会自动创建一个与模型字段及信息对应的表。

向表中新增一条数据

通过create()向表中新增一条数据:

...
const Product = require('../models/product');

router.post('/product', (req, res, next) => {
    const title = req.body.title;
    const imageUrl = req.body.imageUrl;
    const price = req.body.price;
    const description = req.body.description;

    Product.create({
      title: title,
      price: price,
      imageUrl: imageUrl,
      description: description
    })
    .then(result => {
      console.log('Created Product');
      res.redirect('/admin/products');
    })
    .catch(err => {
      console.log(err);
    })
});

查询表中数据

findAll()

通过findAll()来查询表中所有数据:

  • 查询表中所有数据:

    router.get('/products', (req, res, next) => {
      Product.findAll()
        .then(products => {
          res.render('shop/index', {
            prods: products, 
            pageTitle: 'Shop',
            path : "/",
          });
        })
        .catch(err => console.log(err));
    });
    
  • 查询id为prodId的数据:

    router.get('/products/:productId', (req, res, next) => {
      const prodId = req.params.productId;
      Product.findAll({ where: { id: prodId} })
        .then(products => {
          res.render('shop/index', {
            prods: products[0], 
            pageTitle: products[0].title,
            path : "/products",
          });
        })
        .catch(err => console.log(err));
    });
    

    注意:通过where子句返回的then的参数是一个数组

findByPk()

使用提供的主键从表中仅获得一条数据:

const Product = require('../models/product');
// 此处主键在数据库中为id
router.get('/products/:productId', (req, res, next) => {
  const prodId = req.params.productId;
  Product.findByPk(prodId)
    .then((product) => {
      res.render('shop/product-detail', { 
        product: product,
        pageTitle: product.title,
        path: '/products'
      })
    })
    .catch(err => console.log(err));
});

修改表中数据

通过主键(id)找到对应数据后,在本地修改数据,修改完后通过save()方法将修改完的数据发送给数据库,如果数据库中存在修改的数据就覆盖它,如果不存在就新建它:

...
router.post('/edit-product', (req, res, next) => {
  const prodId = req.body.productId;
  const updatedTitle = req.body.title;
  const updatedPrice = req.body.price;
  const updatedImageUrl = req.body.imageUrl;
  const updatedDesc = req.body.description;
  Product.findByPk(prodId)
    .then(product => {
      product.title = updatedTitle; // 在本地修改数据
      product.price = updatedPrice; // 在本地修改数据
      product.description = updatedDesc; // 在本地修改数据
      product.imageUrl = updatedImageUrl; // 在本地修改数据
      return product.save(); // 将修改好的数据发给数据库,返回一个Promise
    })
    .then(result => {
      console.log('UPDATED PRODUCT!');
      res.redirect('/admin/products');
    })
    .catch(err => console.log(err));
});
...

删除数据

通过destroy()来删除数据:

  • 删除指定ID数据:

    ...
    router.post('/delete-product', (req, res, next) => {
      const prodId = req.body.productId;
      Product.findByPk(prodId)
        .then(product => {
          return product.destroy();
        })
        .then(result => {
          console.log('DESTROYED PRODUCT');
          res.redirect('/admin/products');
        })
        .catch(err => console.log(err));
    });
    

    也可以通过destroy()的where语句来进行指定删除。

关联

Sequelize 支持标准关联关系:一对一、一对多和多对多。

为此,Sequelize 提供了四种关联类型,并将它们组合起来以创建关联:

  • HasOne 关联类型
  • BelongsTo 关联类型
  • HasMany 关联类型
  • BelongsToMany 关联类型

一对一

每个User只有一个Cart,Cart属于User:

...
User.hasOne(Cart);
Cart.belongsTo(User);
...

一对多

Product属于User,每个User可以有多个Product:

...
Product.belongsTo(User, {
  constraints: true, // 开启定义关系约束
  onDelete: 'CASCADE' // 级联删除
});
User.hasMany(Product);
...

多对多

一个Cart里可以有多个Product,每个Product可以在不同的Cart中:

...
Cart.belongsToMany(Product, {
  through: CartItem // 这些连接存储的位置
});
Product.belongsToMany(Cart, { through: CartItem });
...

添加到实例的特殊方法

当两个模型之间定义了关联时,这些模型的实例将获得特殊的方法来与其关联的另一方进行交互。

// 定义Product和User的关系
Product.belongsTo(User, {
  constraints: true, // 开启定义关系约束
  onDelete: 'CASCADE' // 级联删除
});
User.hasMany(Product);
createXXX()

使用instance.createProduct()来创建Product将会自动生成关联User的id:

const Product = require('../models/product');

router.post('/product', (req, res, next) => {
    const title = req.body.title;
    const imageUrl = req.body.imageUrl;
    const price = req.body.price;
    const description = req.body.description;

    // 创建Product模型,并自动添加userId(关联主键)
    req.user.createProduct({ // req.user是sequelize保存在数据库中的数据(一个辅助方法)
      title: title,
      price: price,
      imageUrl: imageUrl,
      description: description,
    })
    .then(result => {
      console.log('Created Product');
      res.redirect('/admin/products');
    })
    .catch(err => {
      console.log(err);
    })
});
getXXX()

使用instance.getProducts()来获取Product:

router.get('/edit-product/:productId',(req, res, next) => {
  const editMode = req.query.edit;
  if (!editMode) {
    return res.redirect('/')
  }
  const prodId = req.params.productId;
  req.user
    .getProducts({ where: { id: prodId } }) // 获取对应id的Product
  // Product.findByPk(prodId) // 也可以使用findByPk()来寻找
    .then(products => {
      const product = products[0];
      if (!product) {
        return res.redirect('/');
      }
      res.render('admin/edit-product',{
        pageTitle: 'Edit Product',
        path: '/admin/edit-product',
        editing: editMode,
        product: product
      });
    })
    .catch(err => console.log(err));
});
addXXX()

使用instance.addProducts()来添加Product:

...
addProduct(product, {
    through: { quantity: newQuantity }
  });
})
...