nestjs入门学习规划:https://blog.csdn.net/lxy869718069/article/details/114028195
一对一表关系
一对一是一种 A 只包含一个 B 实例,而 B 只包含一个 A 实例的关系
例如:
有两张表,一个为用户账号表,另一个为用户信息表,信息表中带有一个外键来保存与账号表的关联信息。而一个账号只能有一个用户信息与之对应。
这就是一个典型的一对一关系,其中带有外键的表称之为副表,不带外键的表称之为主表。
项目实例与解析
目录结构:
1.建立两个entity并使用@OneToOne装饰器绑定关系:
主表 user.account.entity.ts 内容如下:
import {
Column,
Entity,
PrimaryGeneratedColumn,
BaseEntity,
OneToOne,
} from 'typeorm';
import { UserInfo } from './user.info.entity';
/*
关于主表和副表的概念:
主表就是你经常操作的那个表。比如:user_account就是主表,UserAccount就是主表对应的实体类
而操作:
getRepository(UserAccount)
.createQueryBuilder('user_account')
.leftJoinAndSelect('user_account.userinfo', 'user_info.id')
.getMany();
上面建立表关联查询
外键 即 @JoinColumn() 通常是放在副表里面
双向关联:允许你使用QueryBuilder从双方加入关系
*/
/* 账号信息表---与用户信息建立一对一双向关联 */
@Entity()
export class UserAccount extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
type: 'varchar',
name: 'account',
length: 20,
default: null,
comment: '账号',
})
account: string;
@Column({
type: 'varchar',
name: 'password',
length: 20,
default: null,
comment: '密码',
})
password: string;
@OneToOne(() => UserInfo, (m) => m.account)
userinfo: UserInfo;
}
副表 user.info.entity.ts 内容如下:
import {
Column,
Entity,
PrimaryGeneratedColumn,
BaseEntity,
OneToOne,
JoinColumn,
} from 'typeorm';
import { UserAccount } from './user.account.entity';
/* 用户信息表---与账号信息建立一对一双向关联 */
@Entity()
export class UserInfo extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({
type: 'varchar',
name: 'name',
length: 20,
default: null,
comment: '姓名',
})
name: string;
@Column({ type: 'int', name: 'age', default: null, comment: '年龄' })
age: Number;
@Column({
type: 'varchar',
name: 'phone',
length: 12,
default: null,
comment: '手机号',
})
phone: Number;
@OneToOne(() => UserAccount, (m) => m.userinfo)
@JoinColumn()
account: UserAccount;
}
解析:这两个实体建立完成之后,将会在数据库中自动生成两个表,表与表之间的关联依靠@OneToOne()这个装饰器来绑定,其中带有@JoinColumn()为副表并且外键放置生成在该字段。
@OneToOne()这个方法传递两个参数第一个参数为一个回调函数,直接指向绑定关联的实体类,第二个参数也是一个回调函数,但是包含一个参数,这个参数代指第一个参数中的实体类,如上的user.info.entity.ts中的m即代指UserAccount实体类,因此可以调用其中的userinfo,即m.userinfo。
这是一对一的关联方式,因此固定这么做就是了。
2.建立控制层和服务层,进行增删查改操作。
控制层 user.controller.ts 内容如下:
import {
Controller,
Get,
Post,
Delete,
Body,
Query,
Put,
} from '@nestjs/common';
import { Transaction, TransactionManager, EntityManager } from 'typeorm';
import { UserService } from './user.service';
import { UserAccount } from '../entities/user.account.entity';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
/*
查询所有列表
@params 无
*/
@Get('list')
findAll(): Promise<UserAccount[]> {
return this.userService.findAll();
}
/*
查询单个详情
@Query ?id=xxx
*/
@Get('detail')
findOne(@Query() query): Promise<UserAccount> {
return this.userService.findOne(query);
}
/*
新增账号和用户信息数据
新增时候由于有可能出现两张表同时进行操作的情况
因此开启事务事件:为了让同时进行的表操作要么一起完成,要么失败
@Transaction()和 @TransactionManager() manager: EntityManager 是事务的装饰器和对象
@Body
{
"account": "admin1",
"password": "000000",
"userinfo": {
"name": "zhang1",
"age": 12,
"phone": "15622222211"
}
}
*/
@Post('add')
@Transaction()
addOne(
@Body() rUser,
@TransactionManager() manager: EntityManager,
): Promise<String> {
return this.userService.addOne(rUser, manager);
}
/*
修改账号和用户信息数据
事务同上
@Body
{
"id": "14",
"account": "admin21112222",
"password": "00000022",
"userinfo": {
"id":'10',
"name": "zhang111222",
"age": 12,
"phone": "156222222"
}
}
*/
@Put('update')
@Transaction()
updateOne(
@Body() uUser,
@TransactionManager() manager: EntityManager,
): Promise<String> {
return this.userService.updateOne(uUser, manager);
}
/*
删除数据
事务同上
@Query ?id=xxx
*/
@Delete('del')
@Transaction()
delOne(
@Query() query,
@TransactionManager() manager: EntityManager,
): Promise<String> {
return this.userService.delOne(query, manager);
}
}
这里的内容非常简单:就是加了四个接口而已。其中涉及到了事务的概念。可以参考文章:
https://editor.csdn.net/md?articleId=114012116
服务层user.service.ts内容如下:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, getRepository, EntityManager } from 'typeorm';
import { UserInfo } from 'src/entities/user.info.entity';
import { UserAccount } from '../entities/user.account.entity';
/*
使用Repository<>对象执行增删查改的操作
*/
@Injectable()
export class UserService {
constructor(
/*
使用InjectRepository装饰器并引入Repository<>对象
这样就可以使用typeorm的Repository Api相关的操作
Repository Api:https://typeorm.biunav.com/zh/repository-api.html#repositoryapi
UserAccount是主表对应的实体类
*/
@InjectRepository(UserAccount)
private readonly userRepository: Repository<UserAccount>,
) {}
/*
获取所有用户数据列表
*/
async findAll(): Promise<UserAccount[]> {
/*
第一种方式直接调用Repository Api的相关方法
联表查询:
第一个参数:relations: ['userinfo'] 属性
(注意:userinfo字段来源于UserAccount实体类中的userinfo字段)
*/
// let list = await this.userRepository.find({ relations: ['userinfo'] });
/*
第二种方式构建QueryBuilder查询
// 这里讲个个人理解:getConnection()、getManager()、getRepository()三者的区别
// getConnection().manager === getManager()
// getConnection().getRepository() === getRepository()
// getManager().getRepository() === getRepository()
// getConnection()获取连接,其实就是将数据库连接所有的内容都拿到了
// getManager()获取实体存储库集合,这样就可以管理任何实体,并且随意使用任何实体
// getRepository()获取具体实体,然后可以对具体实体操作
// 开发中三者使用方式:三个都能使用.createQueryBuilder()方法构建内容,但是构建的结构有所区别
// 例如:
/* 获取user表所有数据getConnection()方式
getConnection()
.createQueryBuilder()
.select('user')
.from(User, 'user')
.getMany();
*/
/* 获取user表所有数据getManager()方式
getManager()
.createQueryBuilder(User,'user')
.getMany();
*/
/* 获取user表所有数据getRepository()方式
getRepository(User)
.createQueryBuilder('user')
.getMany();
*/
let list = await getRepository(UserAccount)
.createQueryBuilder('UserAccount')
.leftJoinAndSelect('UserAccount.userinfo', 'UserInfo.id')
.getMany();
/*
第三种方式直接使用getManager().query("sql语句")执行原生sql操作即可。
补充:
1.三种方式中以第一种和第二种方式使用最多,
争取能用第一种就用第一种,否则可选择第二种,实在不行则使用第三种。
2.相关方法的参数请参考官网
*/
return list;
}
/*
获取单个用户详情
*/
async findOne(query): Promise<UserAccount> {
/*
联表查询:
第一个参数:传递{id:xx}
第二个参数:传递{relations: ['userinfo'] }
*/
let list = await this.userRepository.findOne(
{ id: query.id },
{ relations: ['userinfo'] },
);
return list;
}
/*
新增用户
rUser:用户传递的内容
manager:实体管理对象,参考官网实体管理器api
rUser格式
{
"account": "admin1",
"password": "000000",
"userinfo": {
"name": "zhang1",
"age": 12,
"phone": "15622222211"
}
}
*/
async addOne(rUser, manager: EntityManager): Promise<String> {
// 第一种方式:常规新增方式(typeorm的示例)---先增副表,绑定关联后再增主表
// 副表新增
let muser = new UserInfo();
muser.name = rUser.userinfo.name;
muser.age = rUser.userinfo.age;
muser.phone = rUser.userinfo.phone;
await manager.save(UserInfo, muser);
// 主表新增
let mAccount = new UserAccount();
mAccount.account = rUser.account;
mAccount.password = rUser.password;
// 绑定关联
mAccount.userinfo = muser;
// 保存。注意:只能是save方法,如果用inset将导致副表无外键(即关联无用)
await manager.save(UserAccount, mAccount);
/*
// 另一种方式,先增主表,然后得到主表信息后,再增副表
let mAccount = new UserAccount();
mAccount.account = rUser.account;
mAccount.password = rUser.password;
// 新增主表信息,并保存
// 注意:
// save方法保存后返回的是一个对象{ id: 12, account: 'awqe9527', password: '123654' }
// 而insert方法保存后则返回的是一个
// {
// identifiers: [ { id: 12 } ],
// generatedMaps: [ { id: 12, account: 'awqe9527', password: '123654' } ],
// raw: OkPacket {
// fieldCount: 0,
// affectedRows: 1,
// insertId: 12,
// serverStatus: 3,
// warningCount: 0,
// message: '',
// protocol41: true,
// changedRows: 0
// }
// }
const accountInfo: { [propName: string]: any } = await manager.save(
UserAccount,
mAccount,
);
console.log('accountInfo', accountInfo);
// 副表新增
let muser = new UserInfo();
muser.name = rUser.userinfo.name;
muser.age = rUser.userinfo.age;
muser.phone = rUser.userinfo.phone;
// 设置外键id内容
muser.account = accountInfo.id;
await manager.insert(UserInfo, muser);
*/
return '新增成功!';
}
/*
修改用户
uUser:用户传递的内容
manager:实体管理对象,参考官网实体管理器api
uUser格式
{
"id": "14",
"account": "admin21112222",
"password": "00000022",
"userinfo": {
"id":'10',
"name": "zhang111222",
"age": 12,
"phone": "156222222"
}
}
*/
async updateOne(uUser, manager: EntityManager): Promise<String> {
// 修改则无顺序要求,只需要拿到对应的id,然后到对应的表中修改数据即可,
let muser = new UserInfo();
muser.name = uUser.userinfo.name;
muser.age = uUser.userinfo.age;
muser.phone = uUser.userinfo.phone;
await manager.update(UserInfo, uUser.userinfo.id, muser);
let mAccount = new UserAccount();
mAccount.account = uUser.account;
mAccount.password = uUser.password;
await manager.update(UserAccount, uUser.id, mAccount);
return '修改成功!';
}
/*
删除用户
*/
async delOne(query, manager): Promise<String> {
/*
1.为什么这里传入的是account而不是accountId:注意前面注入的是实体类UserInfo,因此传入实体类中对应的account即可
2.删除必须先删副表,再删主表
3.使用remove方法可以做到一个方法连带删除
*/
manager.delete(UserInfo, { account: query.id });
manager.delete(UserAccount, query.id); // 这里的第二个参数等同于{ id: query.id }
return '删除成功!';
}
}
注意:记得在user.module.ts中注册
user.module.ts内容如下:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { UserAccount } from '../entities/user.account.entity';
import { UserInfo } from '../entities/user.info.entity';
@Module({
imports: [TypeOrmModule.forFeature([UserAccount, UserInfo])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}