TDD / mybatis - 회원 등록, 수정 / useGeneratedKeys
회원 등록 - test 코드 작성
- 처음에 saveMemberTest()로 하려고 했으나, 하나의 메서드에서 하나의 일을 처리하는 것이 좋다고 하셔서 두 개로 나눠서 진행했다.
- insertMemberTest() : 등록 / updateMemberTest() : 수정
@Test
@DisplayName("회원 등록 테스트")
public void insertMemberTest() {
int result = 0; // 회원이 insert 되면 결과값은 정수. 영향 받는 행의 갯수이기 때문에
Member member = new Member();
result = service.save(member); // member 저장
// 회원이 등록 되었을 경우 행의 개수는 늘어나기 때문에 return값이 0보다 클 것이라고 예상하고 작성
assertThat(result).isGreaterThan(0);
}
회원 등록 - service 작성
public int save(Member member) {
// return 0;
return 1;
}
리턴 값이 0 보다 클 것이라고 예상했기 때문에
통과할 경우(1), 실패할 경우(0) 모두 작성하여 테스트를 진행했다.
일단은 두 가지 경우 모두 확인하고 리팩토링을 진행하시는 거 같다.
-
public int save(Member member) {
int result = 0;
SqlSession session = getSesstion();
if(member.getNo() ! = 0) {
// update
} else {
// insert
// dao에 member 객체 넘기고 받은 값을 result에 담기
// dao는 session과 member를 받아서 쿼리문을 실행하게 됨
result = dao.insertMember(session, member);
}
session.close();
// transaction 처리 : insert / update / delete 시 DB에 저장할 것인지
if(result > 0) {
// 변경이 되었으면(영향 받는 행의 갯수가 0보다 큼)
commint;
} else {
rollback;
}
return result;
}
회원 등록 - dao 작성
public int insertMember(SqlSession session, Member member) {
// return 0;
// return 1;
return session.insert("memberMapper.insertMember", member);
}
여기서도 마찬가지로 0, 1를 통해 테스트 성공할 경우 실패할 경우 확인해보기.
-
- namespace가 memberMapper이고 쿼리문의 id가 insertMember인 쿼리문 실행
- member 매개값 넘기기
회원 등록 - 쿼리문 작성
- dao에서 member 매개 값을 넘겨줬기 때문에 parameterType=”Member” 작성하는 거 잊지 말기.
- typealiases를 Member로 지정했기 때문에 Member로 작성
<insert id="insertMember" parameterType="Member">
INSERT INTO MEMBER (
NO,
ID,
PASSWORD,
ROLE,
NAME,
PHONE,
EMAIL,
ADDRESS,
HOBBY,
STATUS,
ENROLL_DATE,
MODIFY_DATE
) VALUES (
SEQ_UNO.NEXTVAL,
#{id},
#{password},
DEFAULT,
#{name},
#{phone},
#{email},
#{address},
#{hobby},
DEFAULT,
DEFAULT,
DEFAULT
)
</insert>
이렇게 작성을 마치고 테스트롤 돌려보면 테스트에 실패한다.
#{ } <- 여기에 들어갈 값을 지정해준 게 없이 때문에 모두 null이 들어가기 때문.
근데 NotNull 제약 조건 위배는 또 아니라고 한다..
-
<configuration>
<!-- 파라미터의 null 값에 대한 JDBC 타입 지정 -->
<settings>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
</configuration>
오라클에 넘길 null 값은 mybatis에서는 other이라는 타입으로 넘긴다.
OTHER에서 확인 가능.
오라클에는 OTHER 타입이 없기 때문에 이걸 value=”NULL”로 지정하여 null이 넘어가도록 한다.
그리고나서 테스트를 하면 NotNull 제약 조건이 위배되었다고 뜬다.
우선 이 상태로 만들어줘야한다고 하셨음!
NULL은 꼭 대문자로 작성할 것.
회원 등록 - test 코드 수정
- NotNull 제약 조건이 걸러였는 컬럼의 값 지정
@Test
@DisplayName("회원 등록 테스트")
public void insertMemberTest() {
int result = 0;
Member member = new Member();
member.setId("test1");
member.setPassword("1234");
member.setName("홍길동");
result = service.save(member);
assertThat(result).isGreaterThan(0);
}
NotNull 제약 조건이 걸려있는 컬럼에는 test 코드에서 직접 값을 넣어주고 테스트를 진행했다.
이제 테스트가 통과 된다.
이 작업까지 마치고 실제 DB에 잘 저장이 되었는지 확인한다.
회원 등록 - 등록 완료된 회원의 NO값 받아오기
- 시퀀스가 만들어주는 NO값
mapper.xml 수정
- 데이터를 insert한 후, PK값을 받아오기 위해서 useGeneratedKeys, keyColumn, keyProperty 속성 추가
- useGeneratedKeys : insert한 후, PK값을 받아오도록 허용
- keyColumn : PK 컬럼 지정
- keyProperty : PK값이 담길 파라미터 객체의 필드명 지정
<insert id="insertMember" parameterType="Member" useGeneratedKeys="true" keyColumn="NO" keyProperty="no">
INSERT INTO MEMBER (
NO,
ID,
PASSWORD,
...
) VALUES (
SEQ_UNO.NEXTVAL,
#{id},
#{password},
DEFAULT,
...
)
</insert>
-
test 코드 수정
- 수정 전에 일단 DB에서 test1 계정 삭제
@Test
@DisplayName("회원 등록 테스트")
public void insertMemberTest() {
int result = 0;
Member member = new Member();
member.setId("test1");
member.setPassword("1234");
member.setName("홍길동");
// 회원 저장 전 NO값
System.out.println(member.getNo());
result = service.save(member);
// 회원 저장 후 NO값
System.out.println(member.getNo());
assertThat(result).isGreaterThan(0);
// NO값은 0보다 클 것이라고 예상
assertThat(member.getNo()).isEqualTo(0);
}
테스트를 실행해서 NO값 2개를 확인했다.
10이 나왔기 때문에 테스트는 실패했다.
-
DB를 확인해보니 새로 저장된 test1 계정의 NO값이 10이었다.
-
assertThat(member.getNo()).isEqualTo(0);
NO값이 0일 것이라고 작성한 코드를 수정한다.
assertThat(member.getNo()).isGreaterThan(0);
다시 테스트를 실행하면 잘 통과되는 것이 확인 가능하다.
-
Member 생성자 추가 및 test 코드 수정
// Member
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member {
private int no;
private String id;
private String password;
private String role;
private String name;
private String phone;
private String email;
private String address;
private String hobby;
private String status;
private Date enrollDate;
private Date modifyDate;
// 생성자 추가
public Member(String id, String password, String name) {
super();
this.id = id;
this.password = password;
this.name = name;
}
}
-
// MemberServiceTest
@Test
@DisplayName("회원 등록 테스트")
public void insertMemberTest() {
int result = 0;
Member member = new Member("test1", "1234", "홍길동");
result = service.save(member);
assertThat(result).isGreaterThan(0);
assertThat(member.getNo()).isEqualTo(0);
}
회원등록 - 여러 회원 등록
- @ParameterizedTest / @CsvSource 어노테이션 추가
@ParameterizedTest
@CsvSource({ // 여러 개의 member를 등록할 때 사용
"test1, 1234, 송은이",
"test2, 4567, 김숙",
})
@DisplayName("회원 등록 테스트")
public void insertMemberTest(String id, String password, String name) { // 매개값 추가
int result = 0;
Member member = new Member(id, password, name);
// @CsvSource 사용 전에는 1명만 조회 가능
// Member member = new Member("test1", "1234", "송은이");
result = service.save(member);
assertThat(result).isGreaterThan(0);
assertThat(member.getNo()).isGreaterThan(0);
}
회원등록 - DB 저장 여부 확인
- DB에 잘 저장되었는지 확인하기
@ParameterizedTest
@CsvSource({
"test1, 1234, 송은이",
"test2, 4567, 김숙",
})
@DisplayName("회원 등록 테스트")
public void insertMemberTest(String id, String password, String name) {
int result = 0;
Member findMember = null;
Member member = new Member(id, password, name);
// @CsvSource 사용 전에는 1명만 조회 가능
// Member member = new Member("test1", "1234", "송은이");
result = service.save(member);
// 저장 여부를 확인하기 위해 ID를 통해 Member 조회
findMember = service.findMemberById(id);
assertThat(result).isGreaterThan(0);
assertThat(member.getNo()).isGreaterThan(0);
// 실제 DB에 저장되었는지 확인
// Member는 null이 아니고 Member의 name을 추출해서 저 위의 매개값 name과 같은지 확인
assertThat(findMember).isNotNull().extracting("name").isEqualTo(name);
}
회원 정보 수정 - test 코드 작성
- update()
@Test
@DisplayName("회원 정보 수정 테스트")
public void updateMemberTest() {
int result = 0;
Member member = null;
// id가 test1인 회원 조회
member = service.findMemberById("test1");
member.setPassword("5678");
member.setName("세종대왕");
result = service.save(member);
// 회원 수정(update)가 잘 되면 result(DB에서 영향 받는 행의 개수)가 0보다 클 것이라고 예상
assertThat(result).isGreaterThan(0);
}
이렇게만 하면 테스트 통과되지 않기 때문에 service 작성하러.
회원 정보 수정 - service 코드 작성
- 회원 등록할 때 사용했던 save 메서드 사용
public int save(Member member) {
int result = 0;
SqlSession session = getSession();
if(member.getNo() != 0) {
// update(수정)
result = dao.updateMember(session, member);
} else {
// insert(등록)
result = dao.insertMember(session, member);
}
// 트랜젝션 처리(commit / rollback)
if(result > 0) {
// 성공
session.commit();
} else {
// 실패
session.rollback();
}
session.close();
return result;
}
회원 정보 수정 - dao 코드 작성
- sesseion 객체의 update() 메서드 사용하기
public int updateMember(SqlSession session, Member member) {
return session.update("memberMapper.updateMember", member);
}
namespace.쿼리문 id, 파라미터.~~
회원 정보 수정 - 쿼리문 작성
- update 태그 사용
<update id="updateMember" parameterType="Member">
UPDATE MEMBER
SET
NAME = #{name},
PASSWORD = #{password},
PHONE = #{phone},
EMAIL = #{email},
ADDRESS = #{address},
HOBBY = #{hobby},
MODIFY_DATE=SYSDATE
WHERE NO = #{no}
</update>
id는 dao의 첫번째 매개값이랑 맞춰주기.
parameterType은 dao에서 작성한 두번째 매개값의 타입.
-
test1 회원의 이름이 세종대왕으로 잘 수정되었다.