昨天用更新User的兩個關聯資料為例,介紹RelationQueryBuilder,今天把UserService裡面全部用QueryBuilder改寫,另外值得提的是這樣做以後其實可以改注入entitymanager或是connection就可以了,兩者的優劣就可能就是個人偏好或是大家可以討論(應該是跟Performance有關吧!?)
用QueryBuilder全部改寫,意謂不會再用到TypeORM的Repository API,我們可以只注入EntityManager,習慣寫SQL多一點的人適合。
修改app.service constructor部分
@Injectable()
export class UsersService {
constructor(
//@InjectRepository(User) // 注入 typeorm repository
//private readonly userRepo: Repository<User>,
// 注入EntityManager可以指定Connection
// 會用default connection,多個connection要參考nestjs官網
@InjectEntityManager()
private readonly em: EntityManager,
private depService: DepartmentService,
private roleService: RolesService,
){}
...
}
app.module不用修改
這下關於userRepo的地方下面會有紅色小蚯蚓,把useRep按F2 Refactor成em
還有一個要修改的地方是
// 會有紅色小蚯蚓
this.em.createQueryBuilder('u')
// EntityManager必須要傳入Entity Type
this.em.createQueryBuilder(User, 'u')
使用EntityManager的好處是可以使用Transaction,未來有機會再介紹,簡單說可以實踐多個SQL Operation在一個Transaction
Refactor getUserById
async getUserById(userId): Promise<User>{
// 載入roles導覽屬性
// 設定eager=true後要把dep拿掉,重複載入SQL語法錯誤
//return await this.userRepo.findOne(id, {relations: ['dep', 'roles']});
// return await this.userRepo.findOneOrFail(id); // 以id搜尋,沒找到會丟出例外
return await this.em
.createQueryBuilder(User, 'u')
.leftJoinAndSelect('u.roles', 'r')
.leftJoinAndSelect('u.dep', 'd')
.whereInIds(userId)
.select([
'u.id',
'u.username',
'u.email',
'd.id',
'd.depName',
'r.id',
'r.roleName',
])
.getOne(); // 單筆使用getOne
}
Refactor addUser
async addUser(data: UserDTO): Promise<User>{
const user = new User();
user.username = data.username;
user.email = data.email;
// // user.depId = data.depId; 不能只指定id,必須傳入物件
// user.dep = await this.depService.getDepById(data.depId);
// // 先要取得role物件陣列,再指給user物件下的roles,save時才會儲存關聯
// user.roles = await this.roleService.getRolesByIds(data.roleIds);
// return await this.userRepo.save(user); // 新增一筆user資料
let userId;
await this.em.createQueryBuilder()
.insert() // 不接受任何引數
.into(User) //
.values(user) // 先更新關聯以外的欄位
.execute() // 必須execute才會產生SQL送到DB
.then(async (result) => {
Logger.log(result); // 到console看回傳的格式
userId = result.identifiers[0].id; // 取得新增後回傳的id
// 以下更新關聯資料
await this.em.createQueryBuilder()
.relation(User, 'roles')
.of(userId)
.add(data.roleIds)
.then(async () => {
await this.em.createQueryBuilder()
.relation(User, 'dep')
.of(userId)
.set(data.depId);
});
});
return this.getUserById(userId);
}
Refactor deleteUser
async deleteUser(id){
const userDeleted = this.getUserById(id); // 先把原本的User存起來
return this.em.createQueryBuilder()
.delete()
.from(User)
.whereInIds(id) //指定id for delete
.execute()
.then(result => userDeleted); // 回傳raw沒有資料
}
昨天少介紹到載入關聯物件,使用RelationQueryBuilder中的loadMany或是LoadOne方法
async updateUserById(userId, data: UserDTO){
// const user = await this.userRepo.findOne(userId, {relations: ['roles']});
const roles = await this.em.createQueryBuilder()
.relation(User, 'roles')
.of(userId)
.loadMany(); // 取得roles物件陣列
使用postman測試
取得user list的時候可以使用cache(Repository API也有),以QueryBuilder改寫getUsers
async getUsers(pageInfo: UserQueryDTO): Promise<User[]>{
return await this.em
.createQueryBuilder(User, 'u')
.leftJoinAndSelect('u.roles', 'r')
.leftJoinAndSelect('u.dep', 'd')
.select([
'u.id',
'u.username',
'u.email',
'd.id',
'd.depName',
'r.id',
'r.roleName',
])
.orderBy('d.depName', 'ASC')
.addOrderBy('u.username')
.skip((pageInfo.page - 1) * pageInfo.pageSize)
.take(pageInfo.pageSize) // 取pageSize筆數
.cache(60000) // 1 min內
.getMany();
}
使用postman測試
TypeORM還有最後一個值得分享是Listener跟有關embedded entity,再來就回到nestjs,database花了蠻多天....