如何优雅使用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();
相关推荐
uhakadotcom1 小时前
Python 量化计算入门:基础库和实用案例
后端·算法·面试
小萌新上大分1 小时前
SpringCloudGateWay
java·开发语言·后端·springcloud·springgateway·cloudalibaba·gateway网关
uhakadotcom2 小时前
使用Python获取Google Trends数据:2025年详细指南
后端·面试·github
uhakadotcom2 小时前
使用 Python 与 Google Cloud Bigtable 进行交互
后端·面试·github
直视太阳2 小时前
springboot+easyexcel实现下载excels模板下拉选择
java·spring boot·后端
追逐时光者3 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 33 期(2025年4.1-4.6)
后端·.net
灼华十一3 小时前
Golang系列 - 内存对齐
开发语言·后端·golang
兰亭序咖啡3 小时前
学透Spring Boot — 009. Spring Boot的四种 Http 客户端
java·spring boot·后端
Asthenia04123 小时前
深入解析Pandas索引机制:离散选择与聚合选择的差异及常见误区
后端