You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQLite+Next.js+TypeORM插入数据无法返回自增ID问题

问题描述
  • SQLite表中有个AUTOINCREMENT类型的数字字段id,作为主键
  • 插入仅包含非必填字段的行时,用TypeORM拿不到数据库自动生成的id值
  • 下面是相关配置和我试过的各种代码

实体定义

export class Requests {
  @PrimaryGeneratedColumn('increment')
  id: number;

  @Column({ nullable: true })
  name: string;

// 其他字段...
}

迁移表语句

CREATE TABLE "requests" (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name varchar,
-- 其他字段...
);

试过的代码及遇到的问题

我试了create/insert/upsert/save这些方法,都拿不到自增ID,但我必须获取插入行的主键id:

public async saveRequest(passedPartialRequest: Partial<Requests> | any) {
    // case1: 不传id就报错"TypeORMError: Cannot update entity because entity id is not set in the entity."
    // 传id:null或0的话,返回的id就是null/0,但数据库里已经生成了正确的自增id
    const case1 = await this.requestsRepository.save({
      ...passedPartialRequest,
      id: null,
    });

    // case2: 数据插入成功了,但返回的id是undefined
    const case2 = await this.requestsRepository.create({
      passedPartialRequest,
    });

    // case3: 不传id报错,传id:null后拿不到有效id
    const case3 = await this.requestsRepository.insert({
      ...passedPartialRequest,
      id: null,
    });

    // case4: 不传id报错,传id:null返回identifier: null
    const saved4 = await this.requestsRepository
      .createQueryBuilder()
      .insert()
      .values({
        ...passedPartialRequest,
        id: null,
      })
      .execute();

    // case5: 传id:null返回identifier: null,不传就报错
    const case5 = await this.requestsRepository.upsert(
      { ...passedPartialRequest, id: null },
      ['id']
    );

    // case6: 直接报错"OUTPUT or RETURNING clause only supported by Microsoft SQL Server or PostgreSQL or MariaDB databases."
    const case6 = await this.requestsRepository
      .createQueryBuilder()
      .insert()
      .into('requests')
      .values([passedPartialRequest])
      .returning('INSERTED.*')
      .printSql()
      .execute();    
}
解决办法

1. 修正实体的主键定义

SQLite的自增主键在TypeORM里有对应的正确配置,把原来的@PrimaryGeneratedColumn('increment')改成下面两种方式之一:

// 方式一:显式指定生成器
@PrimaryGeneratedColumn({
  type: 'integer',
  generator: 'sqlite_autoincrement',
})
id: number;

// 方式二:用默认配置,TypeORM会自动适配SQLite
@PrimaryGeneratedColumn()
id: number;

TypeORM对SQLite的数字主键,默认会生成INTEGER PRIMARY KEY AUTOINCREMENT的结构,不用额外指定increment参数。

2. 正确用save方法拿ID

别手动传id: null,TypeORM会自己判断这是新插入的实体,不是更新。直接这么写:

public async saveRequest(passedPartialRequest: Partial<Requests>) {
  // 先创建实体实例
  const request = this.requestsRepository.create(passedPartialRequest);
  // 保存到数据库,返回的实例里就有生成的id
  const savedRequest = await this.requestsRepository.save(request);
  return savedRequest.id;
}

注意create只是在内存里生成实体,不会操作数据库,必须调用save才会插入,这时候返回的对象才带数据库生成的id。

3. 用insert方法的正确姿势

insert方法返回的结果里,SQLite可以通过raw.insertId拿到生成的id,而且别传id: null:

public async saveRequest(passedPartialRequest: Partial<Requests>) {
  const result = await this.requestsRepository.insert(passedPartialRequest);
  // 这里的insertId就是数据库生成的自增ID
  return result.raw.insertId;
}

4. 检查迁移和实体的一致性

确保手动写的迁移语句里id INTEGER PRIMARY KEY AUTOINCREMENT和实体定义是匹配的,如果用TypeORM自动迁移的话,尽量别手动改迁移文件,避免出现不一致。

5. 纯插入别用upsert

upsert是用来插入或更新的,你指定['id']作为冲突键,又传id: null,逻辑肯定乱。纯插入场景就用save或者insert就行。

问题原因
  • 手动传id: null会让TypeORM搞不清你是要插入还是更新,所以没法正确返回生成的id
  • SQLite不支持RETURNING子句,所以case6的方法直接报错
  • create方法本身不操作数据库,只是生成实体对象,自然拿不到数据库生成的id

内容的提问来源于stack exchange,提问作者Akber Iqbal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 11:16:06