You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constentityIds=entities.map((entity)=>{constentityId=metadata.getEntityIdMap(entity)!// We have to check for an empty `entityId` - if we don't, the query against the database// effectively drops the `where` clause entirely and the first record will be returned -// not what we want at all.if(!entityId)thrownewTypeORMError(`Cannot update entity because entity id is not set in the entity.`,)returnentityId})
最近一个公司官网需要做后台管理,自告奋勇伸出手接下这活。我本来计划技术栈是
Nestjs
+MongoDB
,看我的github的人应该发现,我只会这个。和运维一番沟通后,他说不支持MongoDB
,仅支持Mysql
。这是一段神奇的开始...
在 nestjs 官网文档有个专门的 database 板块。
首推就是 Typeorm ,这篇也算是一个入门教程。(ps:里面也有无尽的坑)
nestjs 也有其他几个操作数据库的的 orm:
以上都是操作
Mysql
的特有 orm,有些 nestjs 做了专门集成封装模块,方便使用。既然官网教程首推
Typeorm
,那我们就用上。我电脑里面装了一个
Navicat Premium
,可以可视化多种数据的图形化界面。关于
Mysql
,你可以选择Docker
安装,也可以直接下载安装文件安装。推荐Docker
。本来我也打算
Docker
安装的,运维给我了一个服务器的Mysql
的地址和账号密码。那就直接连接就行了。因为不会
Mysql
语句,那就傻瓜式图形界面创建数据库吧。也不知道怎么创建,好歹公司后台都是Java,用的全是
Mysql
,找个人问下,就解决问题。图形化界面可以自动生成
Mysql
语句:连接远程
Mysql
搞定,创建数据库搞定,接下来就是程序连接和建表操作。根据 nestjs 官网文档,一顿操作下来完美连接运行。
第一个坑,自动建表
关于
Mysql
的表,在Typeorm
对应叫Entity
。Entity
里面字段列和数据库里面的是一一对应的。换句话来说,在数据库里面建表,要么手动建,设计表结构,另外一种就是
Typeorm
帮我们自动建。Typeorm
载入Entity
有三种方式:单独定义
用到哪些实体,就逐一在此处引入。缺点就是我们每写一个实体就要引入一次否则使用实体时会报错。
自动加载
自动加载我们的实体,每个通过
TypeOrmModule.forFeature()
注册的实体都会自动添加到配置对象的entities
数组中,TypeOrmModule.forFeature()
就是在某个service
中的imports
里面引入的,这个是比较推荐。自定义引入路径
这是官方推荐的方式。
自动建表还有一个配置需要设置:
问题就处在
synchronize: true
上,自动建表,你修改Entity
里面字段,或者*.entity{.ts,.js}
的名字,都会自动帮你修改。正确姿势是使用
typerom migration
方案:migrations 会每次记录数据库更改的版本及内容,以及如何回滚,对于数据处理的更多策略就需要团队根据需求去开发。同时修改的entity 保证新的开发人员可以无需 migrations 即可直接使用。
nestjs 使用 migration 很麻烦,所以官网文档里面都没有写,migrations,大写的懵逼。
migrations
把放在
TypeOrmModule.forRoot
里的配置独立出来ormconfig.ts
建立 cli 配置
ormconfig-migrations.ts
TypeOrmModule.forRoot
里引入ormconfig.ts
配置在
package.json
里面增加scripts
:然后就可以愉快的玩耍了。
第二个坑,自增主键
在
Typeorm
提供的主键的装饰器PrimaryGeneratedColumn
,里面支持四种模式:基本所有教程文章都是用默认的
increment
。然后问题就出现了,使用
increment
在插入数据会出现错误:一开始找到的答案是
.save(entity, {reload: false})
满心欢喜插入了数据库,发现数据库里面的数据
id
是0
。一开始不懂为什么,按道理我设置自增id,起始位置1开始,那么第一条应该是1才对,应该这个是不对的。
我又插入一条数据:
问题原因:我用的
int
,它的默认值就是 0。为什么每次会插入默认值。带着这个疑惑,寻找解决方案,配置里面有个
logging: true,
我把它打开,可以输出执行的Mysql
语句。然后使用
.save(entity, {reload: false})
插入数据:虽然看不懂是什么,大概理解一下,第一个括号插入的字段名,第二括号就是对应的值,
DEFAULT
就是Mysql
默认值,也就是我们设置的default
属性。?
就和后面的参数一一对应。既然
Typeorm
插入有问题,那我是不是可以直接用Mysql
语句插入,就算玩挂了,也就是一个删库跑路。使用
Navicat Premium
执行Mysql
,网上找了一下简单的Mysql
语句:show databases;
然后我在执行插入语句:
还是一样报错
‘Duplicate entry '0' for key 'PRIMARY'
思考:id 是自增的应该不需要传递 id,这个字段吧。带着个这个猜想:
成功插入数据,真是激动万分。
这锅就是
Typeorm
的坑了。那需要解决问题,
Typeorm
提供的可以直接写语句的query
,对于我这种完全不会人肯定无法搞定,那就换个思路解决。Typeorm
会自动给id
一个默认值DEFAULT
,Mysql
就会给它默认一个 0。那如果我不设置默认,Mysql
应该没有undefined
,这种玩意,但是有一个null
,和js
意思一样,都表示空,那我给id
设置null
。又成功插入数据。
意思就是说我在
.save(entity, {reload: false})
插入数据之前,设置entity.id = null
即可。每次创建都是去设置太麻烦了,
给
Entity
类型,设置默认值,这个默认值和数据库default
是有区别的,这是实例属性值。最后发现设置默认值
null
,不光解决Mysql
语句重复添加问题,还解决了Typeorm
报错问题。Typeorm
插入最终都会 https://github.com/typeorm/typeorm/blob/master/src/query-builder/ReturningResultsEntityUpdator.ts 里的ReturningResultsEntityUpdator.insert
方法:这是错误来源代码:
通过 https://github.com/typeorm/typeorm/blob/master/src/metadata/EntityMetadata.ts 里的
EntityMetadata.getValueMap()
静态方法获取。在通过 https://github.com/typeorm/typeorm/blob/master/src/metadata/ColumnMetadata.ts 里的
ColumnMetadata.getEntityValueMap()
实例方法:写在最后
无论使用什么技术都没有一帆风顺的,总是有无尽的坑需要填,各方面原因凑在一起就引起未知的坑,我们需要掌握排坑技巧,不断提升解决问题的能力。
今天就到这里吧,伙计们,玩得开心,祝你好运。
The text was updated successfully, but these errors were encountered: