Cording

[Oracle]selectkey

우주녕 2025. 6. 16. 12:39

문제발생

 

Spring boot-React 사이드 프로젝트 진행 중,

ID를 받아서, 그 ID를 조회한 후에 ID에 해당하는 아이템도 조회해서, 본인의 컬렉션으로 복사하는 기능을 제작하고 있었다 

 

	@Override
    @Transactional 
    public CollectionDTO copyCollection(int originalCollectionId, int userId) {
        CollectionDAO dao = sqlSession.getMapper(CollectionDAO.class);
//        log.info("CollectionService.copyCollection 시작: originalCollectionId={}, userId={}", originalCollectionId, userId);
        // 1. 원본 컬렉션 조회
        CollectionDTO originalCollection = dao.selectCollection(originalCollectionId);
        if (originalCollection == null) {
            throw new IllegalArgumentException("원본 컬렉션을 찾을 수 없습니다: " + originalCollectionId);
        }

//        log.info("원본 컬렉션 조회 성공: originalCollectionName={}", originalCollection.getName());
        // 2. 새 컬렉션 DTO 생성 (복사본)
        CollectionDTO newCollection = new CollectionDTO();
        newCollection.setUserId(userId); // 현재 로그인된 사용자 ID로 설정
        newCollection.setName("Copied - " + originalCollection.getName());
        newCollection.setDescription(originalCollection.getDescription());
        newCollection.setIsPublic(false); // 복사본은 기본적으로 비공개로 설정

        dao.insertCollection(newCollection);
        int newCollectionId = newCollection.getCollectionId(); // 새로 생성된 컬렉션 ID
//        log.info("새 컬렉션 삽입 성공. 새로 생성된 collectionId={}", newCollection.getCollectionId());

        // 4. 원본 컬렉션 아이템 조회
        List<CollectionItemDTO> originalItems = dao.findByCollectionId(originalCollectionId);

        // 5. 새 컬렉션에 아이템 복사
        if (originalItems != null && !originalItems.isEmpty()) {
            int newItemOrder = 0; 
            for (CollectionItemDTO originalItem : originalItems) {
                CollectionItemDTO newItem = new CollectionItemDTO();
                newItem.setCollectionId(newCollectionId); 
                newItem.setContentTitle(originalItem.getContentTitle());
                newItem.setContentType(originalItem.getContentType());
//                newItem.setContentId(originalItem.getContentId());
                newItem.setItemOrder(newItemOrder++); // 새 순서 부여
                
                dao.insertCollectionItem(newItem);
            }
        }

        // 6. 복사된 컬렉션의 전체 정보 (아이템 포함)를 다시 조회하여 반환
        // 새로 생성된 컬렉션을 다시 조회하여 아이템 리스트까지 포함된 완전한 DTO 반환
        return dao.selectCollection(newCollectionId);
    }

 

컬렉션을 복사한 다음에, 그 컬렉션 id에 item을 넣으려고했는데, getCollectionId() 해 보니 계속해서 0이 나왔다.

 

 

그 이유는 MyBatis가 새롭게 생성된 collectionId 값을 자바의 CollectionDTO 객체에 다시 주입(set)해주지 않았기 때문!

<insert id="insertCollection" parameterType="com.boot.collection.dto.CollectionDTO">
    INSERT INTO collections (collectionId, userId, name, description, isPublic) 
    VALUES (
        (SELECT NVL(MAX(collectionId), 0) + 1 FROM collections), -- ⭐ 여기서 ID가 생성됩니다. ⭐
        #{userId},
        #{name},
        #{description},
        #{isPublic}
    )
</insert>
  • MyBatis는 newCollection 객체를 받아 이 INSERT 쿼리를 실행
  • VALUES 절 내에서 (SELECT NVL(MAX(collectionId), 0) + 1 FROM collections)를 통해 새로운 collectionId가 데이터베이스 내부에서 계산되어 삽입
  • 하지만, Mybatis는 이 시점에서 새로 생성된 collectionId 값을 자바의 newCollection 객체로 다시 가져와 setCollectionId() 에 적용이 안되기 때문에 = 0으로 나오는 것이다

mybaits에서 최대값 계산해서 collectionId 값을 넣는데, 자바에서는 newCollection 객체에 그 id값이 없어서 안 됐던 것!!

해결 방법: selectKey 사용

새롭게 생성된 ID를 자바 객체에 다시 주입하려면 Mybatis의 <selectKey> 엘리먼트를 사용하면 된다!

 

MAX()+1 방식으로 ID를 생성하고 있기 때문에, 삽입 후(AFTER) 가장 최근에 생성된 ID를 조회하여 DTO에 설정하도록 <selectKey>를 구성하여 해결하였다! 

<insert id="insertCollection" parameterType="com.boot.collection.dto.CollectionDTO">
    <selectKey keyProperty="collectionId" resultType="int" order="AFTER">
        SELECT MAX(collectionId) FROM collections WHERE userId = #{userId} AND name = #{name}
    </selectKey>
        INSERT INTO collections (collectionId, userId, name, description, isPublic) 
        VALUES (
            (SELECT NVL(MAX(collectionId), 0) + 1 FROM collections),
            #{userId},   -- DTO의 userId 필드와 매핑
            #{name},
            #{description},
            #{isPublic}
        )
    </insert>

컬렉션도 복사하고, 아이템도 복사가 잘 됐다!