網站搜尋

node.js - TypeORM/MySQL - QueryFailedError:在 'where 子句' 中未知的欄位 'NaN'


我需要幫助。我正在使用 Express 和 NodeJS 搭配 TypeScript 開發一個 API,並且我使用 TypeORM 進行資料庫操作。我正試圖建立一個函式,列出並回傳特定使用者所建立的所有預約,但這個錯誤一直發生,我不知道為什麼。我已經搜尋過答案並閱讀了一些 StackOverflow 的問題與解答,但都沒有用,這就是為什麼我決定尋求幫助。

發生例外狀況:QueryFailedError:在 'where 子句' 中未知的欄位 'NaN',在 QueryFailedError.TypeORMError [作為建構函式] (D:\Pessoal\Vinicius\TCC\hexperience-api\src\error\TypeORMError.ts:7:9) 於 new QueryFailedError (D:\Pessoal\Vinicius\TCC\hexperience-api\src\error\QueryFailedError.ts:9:9) 於 Query.onResult (D:\Pessoal\Vinicius\TCC\hexperience-api\src\driver\mysql\MysqlQueryRunner.ts:195:37) 於 Query.execute (D:\Pessoal\Vinicius\TCC\hexperience-api\node_modules\mysql2\lib\commands\command.js:31:14) 於 PoolConnection.handlePacket (D:\Pessoal\Vinicius\TCC\hexperience-api\node_modules\mysql2\lib\connection) js:456:32)於 PacketParser.onPacket(D:\Pessoal\Vinicius\TCC\hexperience-api\node_modules\mysql2\lib\connection.js:85:12)在 PacketParser.executeStart(D:\Pessoal\Vinicius\TCC\hexperience-api\node_modules\mysql2\lib\packet_parser.js:75:16)在 Socket.(D:\Pessoal\Vinicius\TCC\hexperience-api\node_modules\mysql2\lib\connection.js:92:25)在 Socket.emit(events.js:315:20)在 addChunk(internal/streams/readable.js:309:12)在 readableAddChunk(internal/streams/readable.js:284:9)在 Socket.Readable.push(internal/streams/readable.js:223:10)在 TCP.onStreamRead(internal/stream_base_commons.js:188:23)在 TCP.callbackTrampoline(internal/async_hooks.js:131:14)

這是程式碼:

使用者.ts

import {Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, OneToOne} from 'typeorm';
import { Exclude } from 'class-transformer';

import Host from './Host';

export enum typeEnum {
  user = 'user',
  host = 'host',
  admin = 'admin'
}

@Entity('User')
class User {
  @PrimaryGeneratedColumn('increment')
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;

  @Column()
  @Exclude()
  password: string;

  @Column()
  avatar: string;

  @Column()
  phone_number: string;

  @Column()
  bio: string;

  @Column({ type: 'boolean' })
  is_blocked: boolean;

  @Column({
    type: 'enum',
    enum: typeEnum,
    default: typeEnum.user
  })
  type: typeEnum;

  @CreateDateColumn()
  created_at: Date;

  @UpdateDateColumn()
  updated_at: Date;

  @OneToOne(type => Host, host => host.user)
  host: Host;
}

export default User;

Appointment.ts

import {Column,Entity,PrimaryGeneratedColumn,CreateDateColumn,UpdateDateColumn,ManyToOne,JoinColumn} from "typeorm";

import Schedule from "../../../../experiences/infra/typeorm/entities/Schedule";
import User from "../../../../users/infra/typeorm/entities/User";

@Entity('Appointment')
class Appointment {
  @PrimaryGeneratedColumn('increment')
  id: number;

  @Column()
  guests: number;

  @Column({ type: 'boolean' })
  paid: boolean;

  @Column({ type: 'decimal', precision: 12, scale: 2 })
  final_price: number;

  @CreateDateColumn()
  created_at: Date;

  @UpdateDateColumn()
  updated_at: Date;

  @ManyToOne(() => User)
  @JoinColumn({ name: 'user_id' })
  user: User;

  @ManyToOne(() => Schedule)
  @JoinColumn({ name: 'schedule_id' })
  schedule: Schedule;
}

export default Appointment;

AppointmentsRepository.ts

import { getRepository, Repository } from "typeorm";

import Appointment from "../entities/Appointment";

import IAppointmentsRepository from "@modules/appointments/repositories/IAppointmentsRepository";
import ICreateAppointmentDTO from "@modules/appointments/dtos/ICreateAppointmentDTO";

class AppointmentsRepository implements IAppointmentsRepository {
  private ormRepository: Repository<Appointment>;

  constructor () {
    this.ormRepository = getRepository(Appointment);
  }

  // this function has the same idea as the one that has the problem but this one works normally
  public async findByExperienceId(exp_id: number): Promise<Appointment[]> {
    const appointments = await this.ormRepository.find({
      relations: ['user', 'schedule', 'schedule.experience'],
      where: {
        schedule: {
          experience: {
            id: exp_id,
          }
        }
      }
    });

    return appointments
  }

  // here is the function with the problem
  public async findByUserId(user_id: number): Promise<Appointment[]> {
    // this was my last attempt to fix the problem, didn't work
    const appointments = await this.ormRepository.query(
      'SELECT * FROM Appointment a ' +
      'LEFT JOIN `User` u ON u.id  = a.user_id ' +
      'LEFT JOIN Schedule s ON s.id = a.schedule_id ' +
      'LEFT JOIN Experience e ON e.id = s.exp_id ' +
      'WHERE a.user_id = ' + Number(user_id)
    );
    // This was the original code
    // const appointments = await this.ormRepository.find({
    //   relations: ['user', 'schedule', 'schedule.experience'],
    //   where: {
    //     user: {
    //       id: user_id
    //     }
    //   }
    // });

    return appointments;
  }
}

export default AppointmentsRepository;

列出使用者預約服務.ts

import { inject, injectable } from "tsyringe";

import AppError from "@shared/errors/AppError";

import Appointment from "../infra/typeorm/entities/Appointment";

import IUsersRepository from "@modules/users/repositories/IUsersRepository";
import IAppointmentsRepository from "../repositories/IAppointmentsRepository";

interface IRequest {
  id: number
}

@injectable()
class ListUserAppointmentsService {
  constructor (
    @inject('UsersRepository')
    private usersRepository: IUsersRepository,

    @inject('AppointmentsRepository')
    private appointmentsRepository: IAppointmentsRepository
  ) {}

  public async execute({ id }: IRequest): Promise<Appointment[]> {
    const user = await this.usersRepository.findById(id);

    if (!user) {
      throw new AppError('User does not exists');
    }

    // originally, I was passing the parameter id
    const appointments = await this.appointmentsRepository.findByUserId(user.id);

    return appointments;
  }
}

export default ListUserAppointmentsService;

答案#1

在 NestJS 中我也有這個問題,
這是我的程式碼

@Injectable()
export class NavSiteCategoryService {
 
this.navSiteCategoryRepository.createQueryBuilder('category')
'sites.name','sites.desc','sites.url'])

  constructor(
    @InjectRepository(NavSiteCategory)
    private navSiteCategoryRepository: Repository<NavSiteCategory>,
  ) {
  }

  async create(createNavSiteCategoryDto: CreateNavSiteCategoryDto) {
    const newCategory =  this.navSiteCategoryRepository.create(createNavSiteCategoryDto);
    return  await this.navSiteCategoryRepository.save(newCategory)
  }

  async findAll() {
    return this.navSiteCategoryRepository.createQueryBuilder('category')
      .select(["category.id","category.name","category.desc"])
      .leftJoinAndSelect("category.sites", "sites").getMany()
  }

  async categoryOptions() {
    return this.navSiteCategoryRepository.createQueryBuilder('option')
      .select(["option.id","option.name","option.desc"])
      .getMany()
  }

  async findOne(id: number) {
    return this.navSiteCategoryRepository.createQueryBuilder('category')
      .select(["category.id","category.name","category.desc"])
      .leftJoinAndSelect("category.sites", "sites")
      .where({id})
      .getOne()
  }

  async update(id: number, updateNavSiteCategoryDto: UpdateNavSiteCategoryDto) {
    await  this.navSiteCategoryRepository.update(id, updateNavSiteCategoryDto );
  }

  async remove(id: number) {
    await this.navSiteCategoryRepository.delete(id);
  }
}

當 service 中的 findAll 方法顯示成功時,categoryOptions 方法會像你上述描述的那樣出現錯誤,

我不知道原因,控制器是唯一的不同,
錯誤代碼是(注意:@Get('/options'),@Get())

    @ApiTags('站点分类')
@Controller('nav-site-category')
export class NavSiteCategoryController {
  constructor(
    @Inject(forwardRef(() => NavSiteCategoryService))
    private readonly navSiteCategoryService: NavSiteCategoryService
  ) { }
  @Get()
  findAll() {
    return this.navSiteCategoryService.findAll();
  }


  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.navSiteCategoryService.findOne(+id);
  }

  @Post()
  create(@Body() createNavSiteCategoryDto: CreateNavSiteCategoryDto) {
    return this.navSiteCategoryService.create(createNavSiteCategoryDto);
  }


  @Patch(':id')
  update(@Param('id') id: string, @Body() updateNavSiteCategoryDto: UpdateNavSiteCategoryDto) {
    return this.navSiteCategoryService.update(+id, updateNavSiteCategoryDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.navSiteCategoryService.remove(+id);
  }

  @Get('/options')
  options() {
    return this.navSiteCategoryService.categoryOptions()
  }

}

解決方案是將 Options 方法放在最上面,錯誤就修正了,我不知道為什麼,希望這能幫助其他人

成功代碼:

@ApiTags('站点分类')
@Controller('nav-site-category')
export class NavSiteCategoryController {
  constructor(
    @Inject(forwardRef(() => NavSiteCategoryService))
    private readonly navSiteCategoryService: NavSiteCategoryService
  ) { }
  @Get()
  findAll() {
    return this.navSiteCategoryService.findAll();
  }

  @Get('/options')
  options() {
    return this.navSiteCategoryService.categoryOptions()
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.navSiteCategoryService.findOne(+id);
  }

  @Post()
  create(@Body() createNavSiteCategoryDto: CreateNavSiteCategoryDto) {
    return this.navSiteCategoryService.create(createNavSiteCategoryDto);
  }


  @Patch(':id')
  update(@Param('id') id: string, @Body() updateNavSiteCategoryDto: UpdateNavSiteCategoryDto) {
    return this.navSiteCategoryService.update(+id, updateNavSiteCategoryDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.navSiteCategoryService.remove(+id);
  }

}