typeorm装饰器OneToOne之cascadeInsert
发布日期:2021-05-28 16:24:14 浏览次数:10 分类:技术文章

本文共 9547 字,大约阅读时间需要 31 分钟。

使用OneToOne关系时,typeorm框架的保存处理方式有一些要点:

什么情况下会插入外键列,即实体对象之间的属性如何设置才会插入相关外键列,如user.post=post还是post.user=user

什么情况下需要先保存主表对象,才能保存相关联的从表对象
什么情况下可以直接保存从表对象,框架会先级联保存主表对象
能否通过保存主表对象,级联保存相关联的从表对象
这里Post实体对应主表,User实体对应从表,外键在从表中,从表引用主表
1.默认选项
实体:

import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { User} from './User'@Entity()export class Post {    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>User,user=>user.post)    user:User}
import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { Post } from './Post'@Entity()export class User{    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>Post,post=>post.user)    @JoinColumn()    post:Post}

save方法进行保存:

import { createConnection ,Repository} from 'typeorm'
import { User } from './entity/User'import { Post } from './entity/Post'createConnection({    name:'test',    type:'mysql',    database:'test',    host:'localhost',    port:3306,    username:'root',    password:'123456',    charset:'UTF8',    entities:[User,Post],    synchronize:true,    dropSchema:true,    logging:'all',    logger:'simple-console'}).then(async connection=>{    let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) user.post = post await postRepository.save(post) await userRepository.save(user) })

在上面的保存方式中,如果没有先保存主表对象post,而直接保存从表对象user,会报错:

Cannot add or update a child row: a foreign key constraint fails (`test`.`user`, CONSTRAINT`fk_41caccae10590801e045be12f56` FOREIGN KEY (`postId`) REFERENCES `post` (`id`))

这是因为在插入user对象时,框架检测到post属性,则自动插入postId列,而数据库插入postId时,启动外键检查,发现指定id的post数据不存在,报出数据库错误。这说明在框架中将user对象的post属性与postId关联起来是一种默认行为,也就是框架处理关系的基本行为,不必进行多余的选项配置,如果post属性为null,则postId也为null

save方法保存的另一种情况:

then(async connection=>{    let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) post.user = user await postRepository.save(post) await userRepository.save(user) })

保存结果:

mysql> select * from user,post;+----+------+--------+----+----------+| id | name | postId | id | name     |+----+------+--------+----+----------+|  1 | 李四 |   NULL |  1 | 滴滴滴滴 |+----+------+--------+----+----------+1 row in set (0.00 sec)

说明,只设置post.user=user时,在保存user时不能获取其关联的post对象,进而postId为null,这是语言的限制,所以必须要设置从表对象user的post属性才能保存外键

insert方法:

then(async connection=>{    let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) user.post = post await postRepository.insert(post) await userRepository.insert(user)})

保存结果:

mysql> select * from user,post;+----+------+--------+----+----------+| id | name | postId | id | name     |+----+------+--------+----+----------+|  1 | 李四 |      1 |  1 | 滴滴滴滴 |+----+------+--------+----+----------+1 row in set (0.00 sec)

说明默认选项下,insert与save保存效果相同,insert中依然会将user的post属性与postId列关联起来,这个默认行为不会改变

 

 

2.主表级联保存

实体:

import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { User} from './User'@Entity()export class Post {    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>User,user=>user.post,{        cascadeInsert:true    })    user:User}
import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { Post } from './Post'@Entity()export class User{    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>Post,post=>post.user)    @JoinColumn()    post:Post}

save保存:

then(async connection=>{
let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) post.user = user await postRepository.save(post)})

保存结果:

mysql> select * from user,post;+----+------+--------+----+----------+| id | name | postId | id | name     |+----+------+--------+----+----------+|  1 | 李四 |      1 |  1 | 滴滴滴滴 |+----+------+--------+----+----------+1 row in set (0.00 sec)

说明:主表中设置cascadeInsert:true时,只保存post对象,可以关联保存user对象

此时的实际保存顺序类似于表面看到的,即先保存post,然后获取其user属性,设置postId,保存user

也就是说此时cascadeInsert的内部默认行为为:获取其关系属性user,然后将先前保存的post对象的id设置为user的postId,然后保存user对象

save方法的另一种情况:

then(async connection=>{    let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) user.post = post await postRepository.save(post) console.log('执行完毕'+new Date())})

保存结果:

mysql> select * from user;Empty set (0.00 sec)mysql> select * from post;+----+----------+| id | name     |+----+----------+|  1 | 滴滴滴滴 |+----+----------+1 row in set (0.00 sec)

说明,主表级联保存,必须设置主表对象的user属性,才能进行级联

insert方法:

then(async connection=>{    let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) post.user = user await postRepository.insert(post)})

保存结果:

mysql> select * from user;Empty set (0.00 sec)mysql> select * from post;+----+----------+| id | name     |+----+----------+|  1 | 滴滴滴滴 |+----+----------+1 row in set (0.00 sec)

说明insert方法触发不了cascadeInsert的内部默认行为,即无法发现post的user属性,并设置其postId,再插入它,insert方法无法做到这些

但是在前面发现,insert保存user时,可以根据其post属性设置postId,这个行为是可以触发的

 

3.从表级联保存

实体:

import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { User} from './User'@Entity()export class Post {    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>User,user=>user.post)    user:User}
import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { Post } from './Post'@Entity()export class User{    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>Post,post=>post.user,{        cascadeInsert:true    })    @JoinColumn()    post:Post}

save方法保存:

then(async connection=>{    let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) user.post = post await userRepository.save(user) console.log('执行完毕'+new Date())})

报错:

Cannot add or update a child row: a foreign key constraint fails (`test`.`user`, CONSTRAINT `fk_41caccae10590801e045be12f56` FOREIGN KEY (`postId`) REFERENCES `post` (`id`))

老错误,还是先保存user时,进行外键检查出错

这里我们期待的是,typeorm框架能先保存user对象的post属性,然后再保存user对象(包含postId),但是实际上框架还是直接保存user,且设置了postId,出错

说明typeorm框架可以保存主表对象时级联保存从表对象,不可以在保存从表对象时级联保存主表对象

typeorm中的级联保存都是顺序保存,先保存顶层对象,再查找属性进行保存,不能先找到需要保存的主表对象属性,然后保存它

 

4.主表多次级联保存

实体:

 

import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { User} from './User'@Entity()export class Post {    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>User,user=>user.post,{        cascadeInsert:true    })    user:User}
import { Entity,PrimaryColumn,Column,OneToOne,JoinColumn} from 'typeorm'import { Post } from './Post'@Entity()export class User{    @PrimaryColumn('int')    id:number;    @Column('varchar')    name:string    @OneToOne(type=>Post,post=>post.user)    @JoinColumn()    post:Post}

save方法多次保存:

 

 

then(async connection=>{    console.log('开始执行')    let userRepository:Repository
= connection.getRepository(User) let postRepository:Repository
= connection.getRepository(Post) let user:User = userRepository.create({id:1,name:'李四'}) let post:Post = postRepository.create({id:1,name:'滴滴滴滴'}) post.user = user await postRepository.save(post) console.log('保存完成') post.user = userRepository.create({id:2,name:'王五'}) await postRepository.save(post) console.log(await userRepository.find()) console.log(await postRepository.find()) console.log('执行完毕'+new Date())})

保存结果:

 

 

mysql> select * from user;+----+------+--------+| id | name | postId |+----+------+--------+|  1 | 李四 |      1 ||  2 | 王五 |      1 |+----+------+--------+2 rows in set (0.00 sec)mysql> select * from post;+----+----------+| id | name     |+----+----------+|  1 | 滴滴滴滴 |+----+----------+1 row in set (0.00 sec)

可以说是失败了,因为这不是OneToOne,这是MnayToOne,这是因为OneToOne关系的外键应该为unique'列,但是框架没有自动给外键列添加unique约束,需要自己添加

 

 

 

 

 

这里还有一个问题,因为OneToOne装饰器只有一个,在关系的两边都使用了它,但是由于关系本身不是对称的,所以有一些属性只会在一边起作用

如这里看到的cascadeInsert只会在主表中起作用,而onDelete属性只会在从表中起作用

 

关于保存,这里主要测试了以下几个问题:

1.默认情况下save、insert方法,如何触发框架设置外键列的

结果:他们都可以触发框架将从表对象user的post属性关联到外键列postId,可见这是一个基本行为

2.save、insert方法,对cascadeInsert的触发

结果:只有save方法可以触发cascadeInsert,这在文档中也是说明了的

3.save级联保存时的方向

结果:只能在保存主表对象时级联保存作为其属性的从表对象,不能通过保存从表对象级联保存主表对象,这个在别的orm框架中不知道如何设定

 

 

 

 

转载地址:https://blog.csdn.net/qq_27868061/article/details/79336757 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:typeorm装饰器OneToOne之cascadeUpdate
下一篇:typeorm之MysqlDriver

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年02月01日 01时05分09秒