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
相关产品推荐
相关产品推荐

