https://hyunalee.tistory.com/65
https://taetaetae.github.io/2017/04/04/mybatis-use-generated-keys/
https://sowon-dev.github.io/2021/07/26/210727MyBatis-keyProperty/
https://m.blog.naver.com/l1523/221706040458
- selectKey 다중 컬럼 지원(before) https://lng1982.tistory.com/228
useGeneratedKeys 속성
mapper insert 클래스 호출 시 MyBatis의 userGenerateKeys="true"를 통해 keyColumn 속성값과 같은 컬럼명의 값이 keyProperty의 속성값과 같은 VO 객체의 변수에 자동으로 담기게 됨
-> getter 이용
/ , 이용해 하나 이상의 값을 가져올 수 있음
* Mapper.xml
<insert id="insert" useGeneratedKeys="true" keyColumn="replyCd" keyProperty="replyCd">
INSERT INTO tbl_reply (replyCd, commentCd, movieCd, userid, reply)
VALUES (seq_reply.nextval, #{commentCd}, #{movieCd}, #{userid}, #{reply})
</insert>
* ServiceImpl
@Transactional
@Override
public Long register(ReplyVO vo) {
log.info("register...: " + vo); // > register...: ReplyVO(replyCd=null, commentCd=489, movieCd=20000006, userid=mem8, reply=ㅇㄴ, replyDate=null, updateDate=null)
// 댓글 개수 업데이트
commentMapper.updateReplyCnt(vo.getCommentCd(), 1);
mapper.insert(vo);
log.info(vo); // > - ReplyVO(replyCd=291, commentCd=489, movieCd=20000006, userid=mem8, reply=ㅇㄴ, replyDate=null, updateDate=null)
log.info(vo.getReplyCd()); // > 291
return vo.getReplyCd();
}
SelectKey 태그
: 직접 Select 쿼리를 이전/이후에 출력 가능
-> 똑같이 getter 이용해 반환 가능
이때 오직 Select절만, 하나의 컬럼만 가능. 두 개 이상은 vo 등의 객체를 resultType으로 지정할 것
order="AFTER"
<!-- useGeneratedKeys="true" keyColumn="contents" keyProperty="contents" -->
<update id="update" >
<!-- keyProperty="contents" resultType="java.lang.String" -->
<selectKey keyProperty="commentDate,contents" resultType="com.moviepedia.domain.CommentVO" order="AFTER">
SELECT contents, commentDate FROM tbl_comment WHERE commentCd = #{commentCd}
</selectKey>
UPDATE tbl_comment SET contents = #{contents}, updateDate = SYSDATE
WHERE commentCd = #{commentCd}
</update>
@Override
public String modify(CommentVO vo) {
log.info("modify.... : " + vo);
mapper.update(vo);
String modifiedContents = vo.getContents();
log.info("modifiedContents: " + modifiedContents);
log.info(vo.getCommentDate());
return modifiedContents;
}
중요) order="AFTER" 지정한 경우,
mapper.xml에서 insert/update 구문에서 지정한 ParameterType으로 값이 반환됨
-> 그러므로 VO, DTO를 사용한 경우, 해당 객체에 반환되는 값이 들어갈 변수가 객체에 있어야함
order="BEFORE"
public int updateActorRepMovieList(PeopleVO peopleVO);
// public int updateActorRepMovieList(@Param("peopleCd") String peopleCd);
// 객체가 아닌 String peopleCd 하나만 파라미터로 지정하면 keyProperty 변수 setter 찾을수없다는 에러 발생 -> @Param 지정 시 정상 실행
<update id="updateActorRepMovieList">
<selectKey keyProperty="repMovieList" resultType="java.lang.String" order="BEFORE">
<!-- <selectKey keyProperty="repMovieList" resultType="com.moviepedia.domain.PeopleVO" order="BEFORE"> -->
<!-- <selectKey keyProperty="repMovieList,repMovieListDate" resultType="hashmap" order="BEFORE"> -->
<!-- resultType 모두 가능 -->
SELECT LISTAGG(ft.movieCd ||'|'|| ft.movieNm, ',') WITHIN GROUP (ORDER BY ft.movieCd) AS repMovieList
<!-- , SYSDATE AS repMovieListDate -->
FROM (
SELECT ROWNUM, iv4.*
FROM (
SELECT iv3.peopleNm, iv3.updateDate, iv3.movieCd, iv3.movieNm
,iv3.commaCnt - REGEXP_COUNT(iv3.actorDetail, ',', iv3.peopleIdx) +1 AS actorOrder
,iv3.cntStarRating, iv3.avgStarRating
,iv3.actorDetail
FROM (
SELECT iv2.peopleNm, iv2.updateDate
, m.movieCd, m.movieNm, m.actorDetail
, REGEXP_INSTR(m.actorDetail, iv2.peopleNm) AS peopleIdx
, REGEXP_COUNT(m.actorDetail, ',') AS commaCnt
, COUNT(s.starRating) AS cntStarRating
, ROUND(AVG(s.starRating) , 1) AS avgStarRating
FROM (
SELECT iv1.peopleCd, iv1.peopleNm, iv1.updateDate, REGEXP_SUBSTR(iv1.actorFilmos, '[^,]+', 1, level) actorFilmo
FROM (
SELECT p.peopleCd, p.peopleNm, p.actorFilmos, p.updateDate
FROM tbl_people p
WHERE p.peopleCd= #{peopleCd}
) iv1
CONNECT BY REGEXP_SUBSTR(iv1.actorFilmos, '[^,]+', 1, level) IS NOT NULL
/* CONNECT BY는 WHERE 이전에 실행되므로 FROM절이 단일행이 아니면 전체 FULL SCAN하여 연삭속도 극히느려짐 */
) iv2
INNER JOIN tbl_movie m
ON (iv2.actorFilmo = m.movieCd)
LEFT OUTER JOIN tbl_starRating s
ON (m.movieCd = s.movieCd)
GROUP BY iv2.peopleNm, iv2.updateDate, m.movieCd, m.movieNm, m.actorDetail
) iv3
WHERE iv3.peopleIdx != 0 /* INSTR()은 찾고자 하는 문자열 없으면 0 반환 */
ORDER BY actorOrder ASC
) iv4
WHERE <![CDATA[ROWNUM <= 10]]>
ORDER BY iv4.cntStarRating DESC, NVL(iv4.avgStarRating,0) DESC, iv4.updateDate ASC
) ft
WHERE ROWNUM BETWEEN 1 AND 2
</selectKey>
UPDATE tbl_people
SET
repMovieList = #{repMovieList},
repMovieListDate = NVL2(#{repMovieList}, SYSDATE, NULL)
<!-- <if test="repMovieList != null">문법에러 발생 -->
<!-- <if test="#{repMovieList} != null">에러발생하지 않으나 원하는결과x.문법오류같음 -->
<!-- <if test="#{repMovieList != null} ">에러발생하지 않으나 원하는결과x.문법오류같음 -->
<!-- repMovieListDate = SYSDATE -->
<!-- </if> -->
<!-- repMovieListDate = #{repMovieListDate} -->
WHERE peopleCd = #{peopleCd}
</update>
중요) order="BEFORE" 지정한 경우,
mapper.xml에서 insert/update 구문에서 명시한 ParameterType으로 값이 반환되지 않음!!
-> ParameterType이 객체이고 resultType 또한 동일한 객체로 명시해줬다고 그 객체의 변수에 저장되지 않음
그래서 INSERT/UPDATE된 값을 포함하는 객체를 얻고 싶다면 service단에서 다시 SELECT해야함
=> 그러므로 상기 예제에서 ParameterType을 객체로 받을 필요가 없다. 그냥 String peopleCd만 받으면됨.
하지만 이때 mapper 메서드의 파라미터엔 @Param 지정 필수(이유는 모름..)
그렇지 않으면
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.ExecutorException: No setter found for the keyProperty 'repMovieList' in java.lang.String.
와 같이 keyProperty 변수의 setter 찾을 수 없다는 에러 발생
만약 order="BEFORE" 지정해 SELECT문 실행 후 얻은 값을 INSERT/UPDATE에 사용하고자 할때 그 변수를 keyProperty 속성값에 지정하지 않았다면, parameterType를 객체로 지정했을 경우 변수가 객체의 변수임을 정확히 명시해야함.
-> 만약 WHERE peopleCd = #{peopleCd}
로 변수명으로만 지정하면,
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'peopleCd' not found. Available parameters are [peopleVO, param1, repMovieList]
와 같이 해당 파라미터 찾을 수 없다는 에러 발생함.
-> WHERE peopleCd = #{peopleVO.peopleCd}
와 같이 peopleVO의 변수 peopleCd임을 명시해 줘야함.
selectKey 쿼리는 하나의 row만 반환받을 수 있는 것 같다.. 확실친 않음
하나의 <INSERT/UPDATE> 태그 안엔 하나의 만 가능
시행착오) selecctKey의 keyProperty값 이용해 조건문 수행 불가..한 것같다.
-> NVL, NVL2 함수를 이용해 NULL 체크 가능함을 이용할 것
UPDATE tbl_people
SET
repMovieList = #{repMovieList},
repMovieListDate = NVL2(#{repMovieList}, SYSDATE, NULL)
<!-- <if test="repMovieList != null">문법에러 발생 -->
<!-- <if test="#{repMovieList} != null">에러발생하지 않으나 원하는결과x.문법오류같음 -->
<!-- <if test="#{repMovieList != null} ">에러발생하지 않으나 원하는결과x.문법오류같음 -->
<!-- repMovieListDate = SYSDATE -->
<!-- </if> -->
<!-- repMovieListDate = #{repMovieListDate} -->
WHERE peopleCd = #{peopleCd}
시행착오) 여러 변수 반환 받기
-. useGeneratedKeys
insert, update의 파라미터 vo 객체에 값이 없는 변수는 값이 반환되지 않음
ex) commentVO. 수정할 내용 contents 는 담았으나 updateDate는 담지않음
-> keyProperty="updateDate, contents" 하였으나 updateDate는 null값 반환했음
-. SelectKey 태그
왜인지는 모르겠지만 keyProperty 속성의 값 순서에 따라 에러가 발생하거나 정상적으로 반환하거나 했다.
ex) keyProperty="commentDate,contents" : 정상 실행 / keyProperty="contents, commentDate" : 에러 발생(CommentVO에 commentDate 찾을 수 없다고)
->, 다음 공백 때문에 발생한 에러였다!! 변수명을 공백까지 포함해서 인식함. 공백 없이 작성할 것
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.ExecutorException: Error selecting key or setting result to parameter object. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ' repMovieList' in 'class com.moviepedia.domain.PeopleVO'