如何优雅使用node.js操作数据库 助力个人应用开发

当我刚开始接触编程的时候,第一个接触的语言是C#,开发网站使用的是.net,因为是在职校学习的所以在技术学习范围就变的很狭隘,连接数据库要写一长串代码直连,并且每个语句都要写出来 加上拼接自己的参数去实现查询。后来接触了第一个orm框架Hibernate 再到上班接触学习了TypeORM、Sequelize。

今天我们讲解一下如何sequelize的基本使用,解放你写原生语句的烦恼

一、连接mysql

首先在mysql上创建一个测试用的库 sequelize 推荐使用的数据库管理工具(navicat)

注意:电脑上要已安装了mysql服务才有用!

shell 复制代码
cnpm install --save sequelize mysql2 moment

编写测试连接的代码:

javascript 复制代码
// 引入库
const { Sequelize } = require("sequelize");
// 连接数据库
const sequelize = new Sequelize({
  // 选择使用的数据库 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mssql' | 'db2' | 'snowflake' | 'oracle'
  dialect: "mysql",
  // 连接本机mysql
  host: "localhost",
  port: 3306,
  // 输入mysql的账号密码 并指定数据库
  username: "root",
  password: "root",
  database: "sequelize",
});
// 测试连接
async function authenticate() {
  try {
    await sequelize.authenticate();
    console.log("数据库连接成功");
  } catch (error) {
    console.error("无法连接数据库", error);
    return process.exit(1);
  }
}
authenticate();

二、编写表模型代码

编写代码之前我们先要想我们的数据表 这是最重要的部分

我们创建一个model.js的文件 根据创建的模型想法 写入代码

javascript 复制代码
const { Sequelize, DataTypes, NOW } = require("sequelize");
const moment = require("moment");
const { STRING, INTEGER, DECIMAL, DATE, BOOLEAN, ENUM, TEXT } = DataTypes;
/**
 * @description:创建数据库表用
 * @param {Sequelize} sequelize // IDE检测不到 这个参数是啥 我们用的又是js 所以使用这个方式就能让IDE知道这个参数的类型 VsCode插件:koroFileHeader
 */
module.exports = (sequelize) => {
  // 学生啊 班级啊 教师啊 那些表都用烂了 男人的浪漫还是要车子 我们就以车子为例

  //  属性介绍
  //   type(字段类型)
  //   primaryKey(主键)
  //   autoIncrement(自动增长)
  //   allowNull(是否允许为Null)
  //   defaultValue(默认值)
  //   get(获取时的操作)
  //   set(设置时的操作)
  //   values(枚举允许使用的值)
  //   comment(注释)

  const common_config = {
    // Model 对应的表名将与model名相同
    freezeTableName: true,
    // 时间戳开启 并且开启sequelize为我们预设的关于时间的字段
    timestamps: true,
    deletedAt: true,
    createdAt: true,
    updatedAt: true,
    // 偏执表 开启后 删除记录都是软删除 防止误删
    paranoid: true,
  };

  // 格式化创建时间
  const createdAt = {
    type: DATE,
    defaultValue: NOW,
    get() {
      return moment(this.getDataValue("createdAt")).format("YYYY-MM-DD HH:mm");
    },
  };

  // 车企老板
  const CompanyOwner = sequelize.define(
    "company_onwer",
    {
      id: { type: INTEGER, primaryKey: true, autoIncrement: true },
      name: { type: STRING, allowNull: false, comment: "老板名称" },
      sex: { type: BOOLEAN, allowNull: false, comment: "性别", defaultValue: 1 },
      // 一些特殊的用法 比如塞一个json数据到里面 我想获取和设置的时候不需要额外处理
      data: {
        type: TEXT("long"),
        allowNull: false,
        get() {
          // 如果使用raw 去获取的话会失效
          return JSON.parse(this.getDataValue("data"));
        },
        set(value) {
          this.setDataValue("data", JSON.stringify(value));
        },
      },
      createdAt,
    },
    common_config
  );

  // 车企
  const CarCompany = sequelize.define(
    "car_company",
    {
      id: { type: INTEGER, primaryKey: true, autoIncrement: true },
      name: { type: STRING, allowNull: false, comment: "厂家名称" },
      boss_id: { type: INTEGER, allowNull: false, comment: "boss的id" },
      createdAt,
    },
    common_config
  );

  // 车子
  const CarType = sequelize.define(
    "car_type",
    {
      id: { type: INTEGER, primaryKey: true, autoIncrement: true },
      car_type: { type: ENUM, allowNull: false, values: ["轿车", "suv"], comment: "车子类型" },
      car_name: { type: STRING, allowNull: false, comment: "车辆名称" },
      speed: { type: DECIMAL, allowNull: false, comment: "0-100加速" },
      belong: { type: INTEGER, allowNull: false, comment: "所属厂家" },
      money: { type: DECIMAL, allowNull: false, comment: "指导价" },
      createdAt,
    },
    common_config
  );

  // 4S店
  const CarStore = sequelize.define(
    "car_store",
    {
      id: { type: INTEGER, primaryKey: true, autoIncrement: true },
      name: { type: STRING, allowNull: false, comment: "4s名称" },
      createdAt,
    },
    common_config
  );

  // 4S与车子的连接表
  const CarStoreSell = sequelize.define(
    "car_store_sell",
    {
      id: { type: INTEGER, primaryKey: true, autoIncrement: true },
      car_id: { type: INTEGER, allowNull: false, comment: "车辆id" },
      store_id: { type: INTEGER, allowNull: false, comment: "4S店的id" },
      discounts: { type: DECIMAL, allowNull: false, comment: "优惠价格" },
      createdAt,
    },
    common_config
  );

  // 定义关联情况

  // 关联讲解

  // 一对一 就是你的车架号和你的车 是绑定的 不会有相同的车架号
  // 一对多 就是一个车企 可以有很多子品牌 比如 吉利下有 领克 极氪等 但他们都是属于吉利下的
  // 多对多 就是你买车的时候 可以去A4S店 也可以去B4S店买 但是A、B店不止卖这一款车 而且优惠力度也不一样

  // 配置项还可以使用 constraints:false 虚拟绑定不创建外键 防止删除表的时候一直很恶心
  // 一对一 一个公司 一个老板 这里只是为了把三种关联关系都绑上 一个老板可以有很多个公司
  CompanyOwner.hasOne(CarCompany, { foreignKey: "boss_id", sourceKey: "id", constraints: false });
  CarCompany.belongsTo(CompanyOwner, { as: "bossInfo", foreignKey: "boss_id", targetKey: "id", constraints: false });

  // 一对多 一个公司可以生产很多车型
  CarCompany.hasMany(CarType, { foreignKey: "belong", constraints: false });
  CarType.belongsTo(CarCompany, { foreignKey: "belong", constraints: false });

  // 多对多 一个4S可以卖多种类的车 一个种类的车也可以被多个4S店卖
  CarStore.belongsToMany(CarType, { through: CarStoreSell, sourceKey: "id", foreignKey: "store_id", constraints: false });
  CarType.belongsToMany(CarStore, { through: CarStoreSell, sourceKey: "id", foreignKey: "car_id", constraints: false });

  // 初始化表 创建完记得注释掉 使用alter属性还会重复创建外键 所以 只有初始化的时候用就好了
  // sequelize.sync();

  return {
    CarCompany,
    CarStore,
    CarType,
    CompanyOwner,
    CarStoreSell,
  };
};

三、增删改查

1.代码修改

我们先对原来的代码进行修改 后续代码统一写入transaction

javascript 复制代码
// 引入库
const { Sequelize, Op, fn, col, literal } = require("sequelize");
// 连接数据库
const sequelize = new Sequelize({
  // 选择使用的数据库 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mssql' | 'db2' | 'snowflake' | 'oracle'
  dialect: "mysql",
  // 连接本机mysql
  host: "localhost",
  port: 3306,
  // 输入mysql的账号密码 并指定数据库
  username: "root",
  password: "root",
  database: "sequelize",
  logging: false,
});
// 测试连接
async function authenticate() {
  try {
    await sequelize.authenticate();
    console.log("数据库连接成功");
    let { CarCompany, CarStore, CarType, CompanyOwner, CarStoreSell } = require("./models")(sequelize);
    // 运行前清空所有表数据
    await sequelize.truncate({ force: true });
    try {
      // 使用事务 后续再讲 原子性 要全部语句运行成功 有一个错误就回滚
      await sequelize.transaction(async (transaction) => {
        // 在此写入代码
      });
    } catch (err) {
      console.log("数据操作的时候出现了错误:");
      console.log(err);
    }
  } catch (error) {
    console.error("无法连接数据库", error);
    return process.exit(1);
  }
}
authenticate();

2.增的基础使用

javascript 复制代码
        // 增删改查 增的基础使用
        const createMethods = async () => {
          // 推荐使用findOrCreate 测试途中会重复运行代码 防止重复创建
          // xxx.create(结构)
          // xxx.findOrCreate 返回结构为[model,created] model代表返回查询或者刚创建的模型 created代表是否是刚刚创建的

          // 1.一个叫张三的老板
          let boss = await CompanyOwner.create(
            {
              name: "张三",
              data: {
                story: "测试数据",
              },
            },
            {
              transaction,
            }
          );

          // 2.创建了一个车企
          let bwm = await CarCompany.create(
            {
              name: "别摸我",
              boss_id: boss.id,
            },
            {
              transaction,
            }
          );

          // 3.生产了两个车型
          let M3 = await CarType.create(
            {
              car_type: "轿车",
              car_name: "M3 雷霆版",
              money: 900000,
              speed: 4,
              belong: bwm.id,
            },
            {
              transaction,
            }
          );

          let GLB200 = await CarType.create(
            {
              car_type: "suv",
              car_name: "GLB200 7座",
              money: 300000,
              speed: 10,
              belong: bwm.id,
            },
            {
              transaction,
            }
          );

          // 4.开了两家4S店 A店只卖M3 B店卖M3和GLB200
          let storeA = await CarStore.create(
            { name: "中升海滨店" },
            {
              transaction,
            }
          );

          let storeB = await CarStore.create(
            { name: "中升讯美店" },
            {
              transaction,
            }
          );

          // 这个生成的添加关联方法有点难找
          // 如果一直提示错误的话 Model.associations 打印出来 看里面的关联 并且将首字母大写
          // 如 我获取到的数据 { car_types: car_types } 所以就是addCar_types
          await storeA.addCar_types(M3, { through: { discounts: 10000 } });
          await storeB.addCar_types(M3, { through: { discounts: 8000 } });
          await storeB.addCar_types(GLB200, { through: { discounts: 30000 } });
          // 基础的数据生成完毕了
        };

3.查的基础使用

javascript 复制代码
  // 增删改查 查的基础使用
        const readMethods = async () => {
          // 为什么查询都要加上事务呢? 因为我们是在同一次事务内操作的 还没提交到数据库中
          // 不加上事务的id 无法查找到还未提交
          let [current_page, page_size] = [1, 10];
          // 查找有几家车企和数据 通常用于分页
          let companys = await CarCompany.findAndCountAll({
            limit: page_size,
            offset: (current_page - 1) * page_size,
            transaction,
          });
          // 返回的结果是 {count:数量,rows:数据}
          console.log(companys.count);

          // 查找叫别摸我的车企 并且获取它的老板信息
          let bwm = await CarCompany.findOne({
            where: { name: "别摸我" },
            include: {
              // 使用模型获取
              //   model: CompanyOwner,
              // 使用别名获取
              association: "bossInfo",
              // 不写获取所有的字段 写了就仅获取所填的字段
              attributes: ["name", "data"],

              // 第二种写法 可以排除属性 也可以包含属性
              //   attributes: {
              //     include: ["name", "data"],
              //     exclude: ["id"],
              //   },
            },
            transaction,
          });
          // 能直接获取到老板的名字
          //   console.log(bwm.company_onwer.name);
          console.log(bwm.bossInfo.name);
          // 我们之前做了处理 所以拿到的直接是对象 而不是字符串
          // { story: '测试数据' }
          console.log(bwm.bossInfo.data);

          // 查询该车企业有几款车
          let cars = await CarCompany.findOne({
            where: {
              // 使用id查询 其实不需要别的条件的话 直接使用findByPk即可
              id: bwm.id,
              // car_type:["suv","轿车"] car_type:"suv" 对车类型进行筛查
              // 对价格进行筛查
              //   money: {
              //     [Op.lte]: 500000,
              //     [Op.gte]: 500000,
              //     [Op.between]: [0, 1000000],
              //   },
              // 更多操作见 https://www.sequelize.cn/core-concepts/model-querying-basics#%E9%AB%98%E7%BA%A7%E6%9F%A5%E8%AF%A2%E4%B8%8D%E4%BB%85%E9%99%90%E4%BA%8E%E5%88%97
            },
            include: {
              model: CarType,
            },
            transaction,
          });
          //   [ 'M3 雷霆版', 'GLB200 7座' ]
          cars.car_types.length && console.log(cars.car_types.map((x) => x.car_name));

          // 查找这几款车有几家4S店在卖 指导价 以及优惠过的价格
          // 尽量不要在循环里写语句 一次查找出来 再用代码逻辑去操作
          let stores = await CarType.findAll({
            where: {
              id: cars.car_types.map((x) => x.id),
            },
            include: {
              model: CarStore,
              // 不希望连接的字段出现
              attributes: [],
              through: {
                model: CarStoreSell,
                attributes: [],
              },
            },
            attributes: [
              "car_name",
              "money",
              [col("car_stores.name"), "store_name"],
              // [col("`car_stores->car_store_sell`.discounts"), "discounts"]
              // 计算优惠后的价格
              [literal("`car_type`.`money` - `car_stores->car_store_sell`.`discounts`"), "sell_money"],
            ],
            // 不需要获取模型原型 只要数据
            raw: true,
            transaction,
          });

          // 获取到的数组其中一项
          //   [
          //     {
          //       car_name: "GLB200 7座",
          //       money: "300000",
          //       store_name: "中升讯美店",
          //       sell_money: "270000",
          //     },
          //   ];

          console.log(stores);

          // 基本的关联查找功能讲解完毕
        };

4.改的基础使用

javascript 复制代码
   // 增删改查 改的基础使用
        const updateMethods = async () => {
          // 修改一些老板的数据
          let boss = await CompanyOwner.findOne({ where: { name: "张三" }, transaction });
          // 赋值方式1
          //   boss.setDataValue("data", { story: "修改数据" });
          // 赋值方式2
          boss.data = { story: "修改数据" };
          boss.sex = 0;
          boss.name = "李四";
          await boss.save({ transaction });

          boss = await CompanyOwner.findOne({ include: CarCompany, transaction });
          // {name:"李四",sex:false,data:{ story: '修改数据' }}
          console.log(boss);

          // 车企指导价下降的批量操作
          // 自减写法1 自增increment
          //   await CarType.decrement("money", {
          //     by: 10000,
          //     where: {
          //       belong: boss.car_company.id,
          //     },
          //     transaction,
          //   });
          // 自减写法2
          await CarType.update(
            {
              money: literal(`money - 20000`),
            },
            {
              where: {
                belong: boss.car_company.id,
              },
              transaction,
            }
          );

          // 更新的基础操作就做完了
        };

5.删的基础使用

javascript 复制代码
        // 增删改查 删除的基础使用
        const deleteMethods = async () => {
          // 4s下架所有车辆
          await CarStoreSell.destroy({ transaction, where: {} });

          // 车企下架单独某款车
          let car = await CarType.findOne({ where: { car_name: "GLB200 7座" }, transaction });
          await car.destroy({ transaction });

          // 车企倒闭了 所有车辆下架 老板销户了
          await CarType.destroy({ transaction, where: {} });
          await CarCompany.destroy({ transaction, where: {} });
          await CompanyOwner.destroy({ where: { name: "李四" }, transaction });

          // 这就是简单的删除应用 操作数据类的代码 最好要用事务去操作 但是事务也有缺陷 可以去看看脏读 幻读 等
        };

6.完整代码

javascript 复制代码
// 引入库
const { Sequelize, Op, fn, col, literal } = require("sequelize");
// 连接数据库
const sequelize = new Sequelize({
  // 选择使用的数据库 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mssql' | 'db2' | 'snowflake' | 'oracle'
  dialect: "mysql",
  // 连接本机mysql
  host: "localhost",
  port: 3306,
  // 输入mysql的账号密码 并指定数据库
  username: "root",
  password: "root",
  database: "sequelize",
  logging: false,
});
// 测试连接
async function authenticate() {
  try {
    await sequelize.authenticate();
    console.log("数据库连接成功");
    let { CarCompany, CarStore, CarType, CompanyOwner, CarStoreSell } = require("./models")(sequelize);
    // 运行前清空所有表数据
    await sequelize.truncate({ force: true });
    try {
      // 使用事务 后续再讲 原子性 要全部语句运行成功 有一个错误就回滚
      await sequelize.transaction(async (transaction) => {
        // 增删改查 增的基础使用
        const createMethods = async () => {
          // 推荐使用findOrCreate 测试途中会重复运行代码 防止重复创建
          // xxx.create(结构)
          // xxx.findOrCreate 返回结构为[model,created] model代表返回查询或者刚创建的模型 created代表是否是刚刚创建的

          // 1.一个叫张三的老板
          let boss = await CompanyOwner.create(
            {
              name: "张三",
              data: {
                story: "测试数据",
              },
            },
            {
              transaction,
            }
          );

          // 2.创建了一个车企
          let bwm = await CarCompany.create(
            {
              name: "别摸我",
              boss_id: boss.id,
            },
            {
              transaction,
            }
          );

          // 3.生产了两个车型
          let M3 = await CarType.create(
            {
              car_type: "轿车",
              car_name: "M3 雷霆版",
              money: 900000,
              speed: 4,
              belong: bwm.id,
            },
            {
              transaction,
            }
          );

          let GLB200 = await CarType.create(
            {
              car_type: "suv",
              car_name: "GLB200 7座",
              money: 300000,
              speed: 10,
              belong: bwm.id,
            },
            {
              transaction,
            }
          );

          // 4.开了两家4S店 A店只卖M3 B店卖M3和GLB200
          let storeA = await CarStore.create(
            { name: "中升海滨店" },
            {
              transaction,
            }
          );

          let storeB = await CarStore.create(
            { name: "中升讯美店" },
            {
              transaction,
            }
          );

          // 这个生成的添加关联方法有点难找
          // 如果一直提示错误的话 Model.associations 打印出来 看里面的关联 并且将首字母大写
          // 如 我获取到的数据 { car_types: car_types } 所以就是addCar_types
          await storeA.addCar_types(M3, { through: { discounts: 10000 } });
          await storeB.addCar_types(M3, { through: { discounts: 8000 } });
          await storeB.addCar_types(GLB200, { through: { discounts: 30000 } });
          // 基础的数据生成完毕了
        };

        // 增删改查 查的基础使用
        const readMethods = async () => {
          // 为什么查询都要加上事务呢? 因为我们是在同一次事务内操作的 还没提交到数据库中
          // 不加上事务的id 无法查找到还未提交
          let [current_page, page_size] = [1, 10];
          // 查找有几家车企和数据 通常用于分页
          let companys = await CarCompany.findAndCountAll({
            limit: page_size,
            offset: (current_page - 1) * page_size,
            transaction,
          });
          // 返回的结果是 {count:数量,rows:数据}
          console.log(companys.count);

          // 查找叫别摸我的车企 并且获取它的老板信息
          let bwm = await CarCompany.findOne({
            where: { name: "别摸我" },
            include: {
              // 使用模型获取
              //   model: CompanyOwner,
              // 使用别名获取
              association: "bossInfo",
              // 不写获取所有的字段 写了就仅获取所填的字段
              attributes: ["name", "data"],

              // 第二种写法 可以排除属性 也可以包含属性
              //   attributes: {
              //     include: ["name", "data"],
              //     exclude: ["id"],
              //   },
            },
            transaction,
          });
          // 能直接获取到老板的名字
          //   console.log(bwm.company_onwer.name);
          console.log(bwm.bossInfo.name);
          // 我们之前做了处理 所以拿到的直接是对象 而不是字符串
          // { story: '测试数据' }
          console.log(bwm.bossInfo.data);

          // 查询该车企业有几款车
          let cars = await CarCompany.findOne({
            where: {
              // 使用id查询 其实不需要别的条件的话 直接使用findByPk即可
              id: bwm.id,
              // car_type:["suv","轿车"] car_type:"suv" 对车类型进行筛查
              // 对价格进行筛查
              //   money: {
              //     [Op.lte]: 500000,
              //     [Op.gte]: 500000,
              //     [Op.between]: [0, 1000000],
              //   },
              // 更多操作见 https://www.sequelize.cn/core-concepts/model-querying-basics#%E9%AB%98%E7%BA%A7%E6%9F%A5%E8%AF%A2%E4%B8%8D%E4%BB%85%E9%99%90%E4%BA%8E%E5%88%97
            },
            include: {
              model: CarType,
            },
            transaction,
          });
          //   [ 'M3 雷霆版', 'GLB200 7座' ]
          cars.car_types.length && console.log(cars.car_types.map((x) => x.car_name));

          // 查找这几款车有几家4S店在卖 指导价 以及优惠过的价格
          // 尽量不要在循环里写语句 一次查找出来 再用代码逻辑去操作
          let stores = await CarType.findAll({
            where: {
              id: cars.car_types.map((x) => x.id),
            },
            include: {
              model: CarStore,
              // 不希望连接的字段出现
              attributes: [],
              through: {
                model: CarStoreSell,
                attributes: [],
              },
            },
            attributes: [
              "car_name",
              "money",
              [col("car_stores.name"), "store_name"],
              // [col("`car_stores->car_store_sell`.discounts"), "discounts"]
              // 计算优惠后的价格
              [literal("`car_type`.`money` - `car_stores->car_store_sell`.`discounts`"), "sell_money"],
            ],
            // 不需要获取模型原型 只要数据
            raw: true,
            transaction,
          });

          // 获取到的数组其中一项
          //   [
          //     {
          //       car_name: "GLB200 7座",
          //       money: "300000",
          //       store_name: "中升讯美店",
          //       sell_money: "270000",
          //     },
          //   ];

          console.log(stores);

          // 基本的关联查找功能讲解完毕
        };

        // 增删改查 改的基础使用
        const updateMethods = async () => {
          // 修改一些老板的数据
          let boss = await CompanyOwner.findOne({ where: { name: "张三" }, transaction });
          // 赋值方式1
          //   boss.setDataValue("data", { story: "修改数据" });
          // 赋值方式2
          boss.data = { story: "修改数据" };
          boss.sex = 0;
          boss.name = "李四";
          await boss.save({ transaction });

          boss = await CompanyOwner.findOne({ include: CarCompany, transaction });
          // {name:"李四",sex:false,data:{ story: '修改数据' }}
          console.log(boss);

          // 车企指导价下降的批量操作
          // 自减写法1 自增increment
          //   await CarType.decrement("money", {
          //     by: 10000,
          //     where: {
          //       belong: boss.car_company.id,
          //     },
          //     transaction,
          //   });
          // 自减写法2
          await CarType.update(
            {
              money: literal(`money - 20000`),
            },
            {
              where: {
                belong: boss.car_company.id,
              },
              transaction,
            }
          );

          // 更新的基础操作就做完了
        };

        // 增删改查 删除的基础使用
        const deleteMethods = async () => {
          // 4s下架所有车辆
          await CarStoreSell.destroy({ transaction, where: {} });

          // 车企下架单独某款车
          let car = await CarType.findOne({ where: { car_name: "GLB200 7座" }, transaction });
          await car.destroy({ transaction });

          // 车企倒闭了 所有车辆下架 老板销户了
          await CarType.destroy({ transaction, where: {} });
          await CarCompany.destroy({ transaction, where: {} });
          await CompanyOwner.destroy({ where: { name: "李四" }, transaction });

          // 这就是简单的删除应用 操作数据类的代码 最好要用事务去操作 但是事务也有缺陷 可以去看看脏读 幻读 等
        };

        await createMethods();
        await readMethods();
        await updateMethods();
        await deleteMethods();
      });
    } catch (err) {
      console.log("数据操作的时候出现了错误:");
      console.log(err);
    }
  } catch (error) {
    console.error("无法连接数据库", error);
    return process.exit(1);
  }
}
authenticate();
相关推荐
why1512 小时前
腾讯(QQ浏览器)后端开发
开发语言·后端·golang
浪裡遊2 小时前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
声声codeGrandMaster2 小时前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
呼Lu噜2 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_1583 小时前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩3 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04123 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝3 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel3 小时前
招幕技术人员
前端·javascript·后端
盖世英雄酱581363 小时前
什么是MCP
后端·程序员