如何用TypeORM+Oracle批量插入2万条数据?
批量插入Oracle数据的TypeORM问题与解决
问题背景
需要用TypeORM向Oracle的USERS_CACHE表批量插入2万条数据,该操作每天执行1次或多次,希望通过批量插入提升效率。
错误场景
使用批量插入代码时触发Oracle错误:
ORA-01745: invalid host/bind variable name
执行的代码如下:
await AppDataSource.createQueryBuilder() .insert() .into(CacheUsersORM) .values(cacheusers) .execute();
排查发现cacheusers并非预期的实体对象数组,而是一个扁平的一维数组:
[ "3E3F2BF1-AD4C-4903-993E-503E5AB3713C", "usr1", "ldapalias1", 3, "CN=usr1, OU=Users, DC=ad, DC=ent, DC=example, DC=com", "7AECDFA9-2B7B-419F-8BA6-55810B731EC1", "usr2", "ldapalias1", 3, "CN=usr2, OU=Users, DC=ad, DC=ent, DC=example, DC=com" ]
而改用循环单条插入虽然能成功,但本地测试耗时超10分钟,效率极低:
for (const singleuser of cacheusers) { await AppDataSource.createQueryBuilder() .insert() .into(CacheUsersORM) .values(singleuser) .execute(); }
对应的TypeORM实体定义:
import { Column, Entity, PrimaryColumn, Unique, } from "typeorm"; @Entity("USERS_CACHE") @Unique("USERSCACHE_UC", ["userName", "ldapContainerID"]) export class UsersCacheORM { @PrimaryColumn({ name: "USERGUID", type: "nvarchar2", length: 36, primaryKeyConstraintName: "USERSCACHE_PK", }) userGUID?: string; @Column({ name: "USERNAME", nullable: false, type: "varchar2", length: 256 }) userName!: string; @Column({ name: "LDAP_ALIAS", nullable: false, type: "varchar2", length: 256, }) ldapAlias!: string; @Column({ name: "LDAP_CONTAINER_ID", nullable: false, type: "number" }) ldapContainerID!: number; @Column({ name: "LDAP_DN", nullable: false, type: "varchar2", length: 4000 }) ldapDN!: string; }
问题根源
批量插入失败的核心是cacheusers格式错误:TypeORM的批量插入要求values参数必须是匹配实体结构的对象数组,但当前传入的是扁平一维数组,导致TypeORM无法正确映射数据库字段,生成了错误的SQL绑定变量,最终触发Oracle的ORA-01745错误。
解决方案
1. 转换数据格式
先把扁平数组转换成符合UsersCacheORM结构的对象数组(每5个元素对应一个用户的5个字段):
// 将扁平数组转为实体对象数组 const formattedUsers = []; for (let i = 0; i < cacheusers.length; i += 5) { formattedUsers.push({ userGUID: cacheusers[i], userName: cacheusers[i + 1], ldapAlias: cacheusers[i + 2], ldapContainerID: cacheusers[i + 3], ldapDN: cacheusers[i + 4] }); }
2. 分批次批量插入
Oracle对单条SQL的绑定变量数量有限制,一次性插入2万条数据可能超出上限,建议分批次插入(比如每批次1000条):
const batchSize = 1000; for (let i = 0; i < formattedUsers.length; i += batchSize) { const batch = formattedUsers.slice(i, i + batchSize); await AppDataSource.createQueryBuilder() .insert() .into(UsersCacheORM) .values(batch) .execute(); }
3. 备选方案:用EntityManager.save批量插入
如果QueryBuilder方式仍有问题,可以改用EntityManager的save方法,TypeORM会自动处理批量逻辑:
const batchSize = 1000; for (let i = 0; i < formattedUsers.length; i += batchSize) { const batch = formattedUsers.slice(i, i + batchSize); await AppDataSource.manager.save(UsersCacheORM, batch); }
效果说明
转换格式并采用分批次批量插入后,2万条数据的插入耗时可大幅降低至数分钟内(具体时间取决于数据库性能)。
内容的提问来源于stack exchange,提问作者Ranjeet
相关产品推荐
相关产品推荐

