本文共 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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!