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>