ABOUT ME

-

  • Nestjs-typeorm에서 트랜잭션 처리
    Nest.js 2023. 10. 10. 12:45
    728x90

    대가리 진짜 아프다... 분명 아래 출처대로 했을때 잘 되다가 다른거 작업하고 오니까 다시 또 안된다

        | TypeError: Cannot read properties of undefined (reading 'delete')
        |     at FeedsRepository.delete (/app/src/repository/Repository.ts:404:29)
        |     at FeedsRepository.deleteFeed (/app/src/models/repositories/feeds.repository.ts:70:41)
        |     at FeedsService.deleteFeed (/app/src/api/feeds/feeds.service.ts:77:6)
        |     at processTicksAndRejections (node:internal/process/task_queues:95:5)
        |     at FeedsController.deleteFeed (/app/src/api/feeds/feeds.controller.ts:100:3)

     

    대가리 뜨거워진다...

     

    private readonly feedsRepository: FeedsRepository,
    내가 사용하는 repository 패턴 사용을 위한 FeedsRepository
    @Injectable()
    export class FeedsRepository extends Repository<FeedEntity> {
    	constructor(
    		@InjectRepository(FeedEntity)
    		private readonly repository: Repository<FeedEntity>,
    	) {
    		super(repository.target, repository.manager, repository.queryRunner);
    	}
        
        ...
    queryRunner.manager.withRepository(this.feedRepository)

    이렇게 사용 했을 때 repository를 인식을 못한다

     

    query: START TRANSACTION
         | FeedsRepository {
         |   target: undefined,
         |   manager: undefined,
         |   queryRunner: undefined,
         |   repository: Repository {
         |     target: [class FeedEntity extends DefaultEntity],
         |     manager: EntityManager {
         |       '@instanceof': Symbol(EntityManager),
         |       repositories: [Map],
         |       treeRepositories: [],
         |       plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer {},
         |       connection: [DataSource]
         |     },
         |     queryRunner: undefined
         |   }
         | }
         | query: COMMIT

    target과 manager queryRunner가 셋다 undefined가 뜬다.

     

    그렇다면 커스텀 repository를 버리고 출력 했을땐 어떻게 될까

    	...
    @InjectRepository(FeedEntity)
    private readonly feedRepository: Repository<FeedEntity>
    	...
    queryRunner.manager.withRepository(this.feedRepository)
    Repository {
         |   target: [class FeedEntity extends DefaultEntity],
         |   manager: <ref *1> EntityManager {
         |     '@instanceof': Symbol(EntityManager),
         |     repositories: Map(0) {},
         |     treeRepositories: [],
         |     plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer {},
         |     connection: DataSource {
         |       '@instanceof': Symbol(DataSource),
         |       migrations: [],
         |       subscribers: [],
         |       entityMetadatas: [Array],
         |       entityMetadatasMap: [Map],
         |       name: 'default',
         |       options: [Object],
         |       logger: [AdvancedConsoleLogger],
         |       driver: [PostgresDriver],
         |       manager: [EntityManager],
         |       namingStrategy: [DefaultNamingStrategy],
         |       metadataTableName: 'typeorm_metadata',
         |       queryResultCache: undefined,
         |       relationLoader: [RelationLoader],
         |       relationIdLoader: [RelationIdLoader],
         |       isInitialized: true
         |     },
         |     queryRunner: PostgresQueryRunner {
         |       isReleased: false,
         |       isTransactionActive: true,
         |       data: {},
         |       loadedTables: [],
         |       loadedViews: [],
         |       sqlMemoryMode: false,
         |       sqlInMemory: [SqlInMemory],
         |       transactionDepth: 1,
         |       cachedTablePaths: {},
         |       driver: [PostgresDriver],
         |       connection: [DataSource],
         |       mode: 'master',
         |       broadcaster: [Broadcaster],
         |       manager: [Circular *1],
         |       databaseConnectionPromise: [Promise],
         |       databaseConnection: [Client],
         |       releaseCallback: [Function (anonymous)]
         |     }
         |   },
         |   queryRunner: undefined
         | }
         | query: COMMIT

    이렇게 정상적으로 나오는걸 알 수 있다. 그럼 도대체 내껀 왜 안될까??

     

    https://blog.naver.com/pjt3591oo/222927333364

     

     

    해결방법

    // service.ts
    constructor(
    		....
    		private dataSource: DataSource,
    	) {}
        
        
        
    async deleteFeed(feedId: string) {
    		// 피드가 있는지 확인.
    		await this.findFeedByIdOrThrow(feedId);
    
    		const queryRunner = this.dataSource.createQueryRunner();
    		await queryRunner.connect();
    		await queryRunner.startTransaction();
    
    		try {
    			const [mediaStatus, feedStatus] = await Promise.all([
    				await this.mediasService.deleteFeedMediasByFeedId(
    					feedId,
    					queryRunner.manager,
    				),
    				await this.feedsRepository.deleteFeed(feedId, queryRunner.manager),
    			]);
    
    			if (!mediaStatus || !feedStatus)
    				throw EntityConflictException(ERROR_DELETE_FEED_OR_MEDIA);
    
    			const medias = await this.mediasService.findMediaUrlByFeedId(feedId);
    			medias.map(async (media) => {
    				const fileName = extractFilePathFromUrl(media.url, 'feed');
    				if (!fileName) throw EntityNotFoundException(ERROR_FILE_DIR_NOT_FOUND);
    				await DeleteS3Media(fileName);
    			});
    
    			await queryRunner.commitTransaction();
    			//s3에 미디어 파일들 삭제.
    		} catch (error) {
    			await queryRunner.rollbackTransaction();
    			throw error;
    		} finally {
    			await queryRunner.release();
    		}
    	}
    
    
    // repository.ts
    async deleteFeed(feedId: string, manager?: EntityManager) {
    		if (manager) {
    			const { affected } = await manager.delete(FeedEntity, {
    				id: feedId,
    			});
    			return !!affected;
    		}
    		const { affected } = await this.delete({
    			id: feedId,
    		});
    
    		return !!affected;
    	}

     

     

     

     

     

     

     

     

     

    출처 -

    https://inchan.dev/posts/202302250414/

     

    typeORM transaction에서 repository 사용하기

    typeORM transaction시 버전차에 따른 방법 변화

    inchan.dev

    https://orkhan.gitbook.io/typeorm/docs/custom-repository

     

    Custom repositories - typeorm

    You can create a custom repository which should contain methods to work with your database. For example, let's say we want to have a method called findByName(firstName: string, lastName: string) which will search for users by a given first and last names.

    orkhan.gitbook.io

    https://blog.naver.com/pjt3591oo/222927333364

     

    [typeorm] 트랜잭션 관리방법 - dataSource.transaction 주의사항

    typeorm이 0.3으로 올라가면서 connection 대신 dataSource를 사용합니다. 이로인해 트랜잭션을 취급하는 ...

    blog.naver.com

     

    728x90
Designed by Tistory.