코드로 배우는 스프링 웹 프로젝트 보면서 함!!!
06 스프링 + mybatis
6.1 테이블 생성 및 개발 준비
6.1.1 데이터베이스의 테이블 생성
create table tbl2_member ( userid varchar(50) not null, userpw varchar(50) not null, username varchar(50) not null, email varchar(100), regdate timestamp default now(), primary key(userid) ); |
*열 잘못 입력했을 때
1)열 속성 변경
alter table 테이블명 modify 열 정의
ex) alter table tbl_member modify regdate timestamp default now();
2)열 추가
Alter table 테이블명 add 열 정의
Ex)alter table tbl_member add regdate timestamp default now();
3)열 삭제
Alter table 테이블명 drop 열명
Ex) alter table tbl_member drop updatedate;
*에러
Regdate timestamp default now(),
Updatedate timestamp default now()는 아래와 같은 에러가 발생함.
alter table tbl_member modify updatedate timestamp default now() Error Code: 1293. Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause
=> CURRENT_TIMESTAMP가있는 단 하나의 TIMESTAMP 열만있을 수 있습니다
내 mysql 버전은 5.5인데 current_timestamp를 하나의 테이블에서 2번 쓰고 싶으면 mysql 5.6 이상의 버전을 써야 한다.
*테이블 적용 명령어:commit;
6.1.2 도메인 객체를 위한 클래스 설계
개발을 할 때 가장 중요한 용어가 될 만한 명사를 흔히 ‘도메인(domain)’이라고 표현합니다. 예컨대 쇼핑몰의 경우 ‘회원, 상품, 배송’ 등과 같은 용어가 도메인이라고 할 수 있습니다.
도메인의 경우 1차 도메인, 2차 도메인.. 등과 같이 가장 우선하고, 중요한 의미를 가지는 순서에 따라 정해집니다. 어떤 비즈니스 로직에 있어서 반드시 필요한 것이 1차도메인이 되고, 점차 필요한 로직에 따라서 2차,3차로 확대됩니다.
도메인이라는 단위는 다른 의미로 여러 물리적인 환경으로 분리가 가능한 단위라고 할 수 있습니다. 예를 들어 회원 DB와, 상품 DB를 분리해서 운영할 수 있듯이, 도메인은 하나의 온전한 시스템의 단위가 될 수 있습니다.
개발자에게 도메인 클래스는 일반적으로 특정 테이블과 유사한 속성을 가지는 클래스를 의미합니다. 위에서 설계한 tbl_member 테이블을 기준으로 작성되는 도메인 클래스는 다음과 같이 작성합니다.
package org.zerock.domain;
import java.util.Date;
public class MemberVO {
private String userid; private String userpw; private String username; private String email; private Date regdate;
public String getUserid() { return userid; }
public void setUserid(String userid) { this.userid = userid; }
public String getUserpw() { return userpw; }
public void setUserpw(String userpw) { this.userpw = userpw; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override public String toString() { return "MemberVO [userid=" + userid + ", userpw=" + userpw + ", username=" + username + ", email=" + email + ", regdate=" + regdate + "]"; }
}
|
l Getter/setter, toString() 불러오는 방법
private String userid;
private String userpw;
private String username;
private String email;
private Date regdate;
를 입력 해준 다음
6.2 DAO 인터페이스의 작성
데이터베이스에 테이블의 구성과 도메인 클래스에 구성이 끝났다면 실제로 실행해야 하는 작업을 인터페이스로 정의하도록 합니다. DAO로 작성하면 관련기술이 변경되더라도 DAO만을 변경해서 처리할 수 있도록 하기 위해서 입니다.
예제를 위해서 간단한 데이터베이스의 현재 시간을 체크하는 기능과, tbl_member 테이블에 데이터를 추가하는 기능을 구성합니다.
일반적인 경우라면 DAO는 ‘dao’라는 이름의 패키지를 구성하거나, ‘persistence’라는 패키지를 작성합니다.
데이터베이스의 현재 시간을 체크하는 기능과 tbl_member테이블에 데이터를 추가하는 기능 구성
package org.zerock.persistence;
import org.zerock.domain.MemberVO;
public interface MemberDAO {
public String getTime();
public void insertMember(MemberVO vo);
} |
6.3 XML Mapper의 작성
Dao 클래스가 작성되었다면 이를 사용하는 sql문을 작성해야 합니다.
mybatis에서는 sql 문을 저장하는 존재를 mapper라는 용어로 표현합니다.
Mapper는 xml과 인터페이스를 이용할 수 있는데, xml을 사용하는 경우라면 다음과 같은 순서로 작업을 진행합니다.
- Xml로 작성된 mapper의 위치(저장 경로) 결정
- Xml mapper 파일을 작성하고 필요한 dtd 추가
- Sql 작성
Mapper 파일의 저장 경로
Xml로 작성되는 mapper 파일의 경우 java로 작성된 클래스와 정보를 분리해 주는 것이 유지보수에 있어서 필수적 입니다.
Java 파일이 아닌 경우에는 아래의 ‘resources’와 같은 경로를 이용합니다. Mapper를 설정해두는 폴더를 mapper와 같이 생성합니다.
6.3.2 xml mapper의 작성
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.MemberMapper">
<select id="getTime" resultType="string"> select now() </select>
<insert id="insertMember"> insert into tbl_member (userid, userpw, username, email) values (#{userid}, #{userpw}, #{username}, #{email}) </insert> </mapper> |
Xml mapper를 작성할 때는 ‘namespace’라는 설정에 가장 신경을 많이 써야합니다. (<mapper>). ‘namesapce’ 속성은 클래스의 패키지와 유사한 용도로, Mybatis 내에서 원하는 sql문을 찾아서 실행할 때 동작합니다.
6.3.3 mybatis-spring에서 xml mapper 인식
Mybatis가 동작하면 조금 전에 작성된 xml mapper를 인식해야만 정상적인 동작이 가능하므로, root-context.xml을 이용해서 아래와 같이 설정을 변경합니다.
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:/mybatis-config.xml"></property> <property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property> </bean> |
변화된 설정의 핵심은 mapperLocations라는 속성을 추가하고, 작성된 mappers 폴더 내에 어떤 폴더이건 관계없이 파일의 이름이 ‘Mapper.xml’로 끝나면 자동으로 인식하도록 설정하는 것입니다.
6.4 DAO 인터페이스의 구현
DAO 인터페이스와 Mapper의 작성이 완료됐다면 실제 이를 구현하는 구현 클래스를 작성해야만 합니다. Mybatis에서 dao를 이용하는 경우는 sqlsessiontemplate이라는 것을 이용해서 dao를 구현하므로, 우선적으로 sqlsessiontemplate을 설정하는 작업부터 시작합니다.
6.4.1 sqlsessiontemplate의 설정
Dao의 작업에서 가장 번거로운 작업은 데이터베이스와 연결을 맺고, 작업이 완료된 후에 연결을 close()하는 작업입니다. 다행스럽게도 mybatis-spring 라이브러리에는 이것을 처리할 수 있는 sqlsessiontemplate이라는 클래스를 제공하므로, 이를 이용하면 개발자들이 직접 연결을 맺고, 종료하는 작업을 줄일 수 있습니다.
Mybatis-spring에서 제공하는 sqlsessiontemplate은 mybatis의 sqlsession 인터페이스를 구현한 클래스로 기본적인 트랜잭션의 관리자 쓰레드 처리의 안정성 등을 보장해 주고, 데이터베이스의 연결과 종료를 책임집니다.
Sqlsessiontemplate은 sqlsessionfactory를 생성자로 주입해서 설정합니다.
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg> </bean> |
6.4.2 구현 클래스 작성하기
MemberDAO 인터페이스를 구현하는 클래스는 앞에서 설정된 SqlSessionTemplate을 주입 받아서 사용하도록 구성합니다.
package org.zerock.persistence;
import javax.inject.Inject;
import org.apache.ibatis.session.SqlSession; import org.springframework.stereotype.Repository; import org.zerock.domain.MemberVO;
@Repository public class MemberDAOImpl implements MemberDAO {
@Inject private SqlSession sqlSession;
private static final String namespace = "org.zerock.mapper.MemberMapper";
@Override public String getTime() { // TODO Auto-generated method stub return sqlSession.selectOne(namespace + ".getTime"); }
@Override public void insertMember(MemberVO vo) { sqlSession.insert(namespace + ".insertMember", vo);
}
} |
@Repository는 dao를 스프링에 인식시키기 위해 사용합니다.
Mybatis의 sqlsession에서는 insert, update, delete 등의 sql문 처리와 selectone, selectlist와 같이 select의 형태를 지웝합니다.
6.5 스프링에 빈으로 등록하기
Memberdaoimpl이 @repository 애노테이션이 설정되더라도 스프링에서 해당 패키지를 스캔하지 않으면 제대로 스프링의 빈으로 등록되지 못합니다. 이 처리는 root-context.xml을 이용해서 아래와 같이 설정합니다
<context:component-scan base-package="org.zerock.persistence"> </context:component-scan> |
설정된 패키지의 MemberDAOImpl에 스프링의 빈으로 등록되었다는 아이콘이 보여지는 반드시 확인하기
(S가 없으면 한번 실행해보기)
6.6 테스트 코드의 작성
최종적으로 작성된 코드는 테스트 과정을 거쳐서 설정과 동작 여부를 확인합니다. 테스트 코드가 있는 폴더에 아래의 코드를 작성합니다.
memberdaotest에서는 memberdao를 주입하고, testTime() 메소드를 사용해서 Memberdao의 설정이 아무런 문제가 없는지 확인합니다.
package org.zerock.web;
import javax.inject.Inject;
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.zerock.domain.MemberVO; import org.zerock.persistence.MemberDAO;
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/**/root-context.xml" })
public class MemberDAOTest {
@Inject private MemberDAO dao;
@Test public void testTime() throws Exception {
System.out.println(dao.getTime()); }
@Test public void testInsertMember() throws Exception {
MemberVO vo = new MemberVO(); vo.setUserid("user00"); vo.setUserpw("user00"); vo.setUsername("USER00"); vo.setEmail("user00@aaa.com");
dao.insertMember(vo);
}
}
|
6.7 mybatis의 로그 log4jdbc-log4j2 (자세한 로그)
Mybatis를 사용해서 개발하다가 보면 가끔 잘못된 sql이나 잘못된 속성의 이름으로 인해서 예외가 발생하는 경우가 종종 있습니다. 이런 경우를 대비해서 mybatis의 로그를 보다 자세히 조사할 수 있도록 로그를 설정해주는 것이 좋습니다.
자세한 로그를 보기 위해서는 우선 log4jdbc-log4j2 라이브러리가 필요합니다.
Pom.xml에 다음 라이브러리를 추가합니다.
<dependency> <groupId>org.bgee.log4jdbc-log4j2</groupId> <artifactId>log4jdbc-log4j2-jdbc4</artifactId> <version>1.16</version> </dependency> |
라이브러리가 추가된 후에는 데이터베이스와 연결되는 드라이버 클래스와 연결 URI을 아래와 같이 수정해야 합니다.
Root-context.xml의 수정 (위치:src/main/webapp/WEB-INF/spring/root-context.xml)
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
<property name="url" value="jdbc:log4jdbc:mysql://127.0.0.1:3306/book_ex?useSSL=false"></property>
<property name="username" value="zerock"></property> <property name="password" value="zerock"></property>
</bean> |
과거에 JDBC 드라이버의 클래스가 ‘com.mysql.jdbc.Driver’였지만, 이를 ‘net.sf.log4jdbc.DriverSpy’로 변경해야 하고, 연결 URL에는 중간에 log4jdbc라는 단어가 들어가야 합니다.
Log4jdbc-log4j2가 제대로 동작하려면 /src/main/resources 폴더에 log4jdbc.log4j2.properties 파일과 logback.xml 파일을 추가해야 합니다.
1) Log4jdbc.log4j2.properties 파일
.properties 파일 생성
Resources 마우스 오른 쪽 new -> Other
Logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/loggin/logback/base.xml" />
<!-- log4jdbc-log4j2 --> <logger name="jdbc.sqlonly" lovel="DEBUG" /> <logger name="jdbc.sqltiming" level="INFO" /> <logger name="jdbc.audit" level="WARN" /> <logger name="jdbc.resulttest" level="ERROR" /> <logger name="jdbc.resultsettable" level="ERROR" /> <logger name="jdbc.connection" level="INFO" />
</configuration> |
실행해보면 아래와 같이 상세한 로그가 출력되는 것을 확인할 수 있다.
6.8 mybatis의 #{} 문법
Mybatis의 경우 기본적으로 preparedstatement를 이용해서 처리됩니다. 개발자가 preparedstatement에 들어가는 파라미터를 사용할 때는 ‘#{}’ 기호를 이용해서 처리합니다.
‘#{}’ 다음과 같은 규칙으로 적용됩니다.
- 파라미터가 여러 속성을 가진 객체인 경우 ‘#{num”}’은 getNum()혹은 setNum()을 의미합니다.
- 파라미터가 하나이고, 기본자료형이나 문자열인 경우 값이 그대로 전달됩니다.
- 파라미터가 Map 타입인 경우 ‘#{num}’은 Map 객체의 키 값이 ‘num’인 값을 찾습니다.
스프링의 경우 하나의 설정이 잘못된 경우에도 모든 동작이 실행되지 않기 때문에 각 단계마다 어떤 결과가 나와야 하는지를 명확히 알고 진행해야 합니다.
반드시 테스트를 거쳐야 하는 항목들을 지정하면 다음과 같습니다.
- 1.5의 프로젝트 생성 및 tomcat 설정
- 3.3의 mysql 데이터베이스 연동 테스트
- 4.3의 mybatis + spring의 연동 테스트
- 5.2의 스프링 mvc의 컨트롤러 테스트
스프링을 처음 접하는 사람들이 고생하는 대부분은 설정과 관련된 에러입니다. 내용의 중간에 잇는 테스트 코드는 처음에는 귀찮고 번잡스럽게 생각될 수 있지만 규모가 커지고, 여러 명이 작업하는 경우에는 반드시 필요한 부분이므로 빠짐없이 테스트를 진행해 줘야 합니다.
'JAVA > spring' 카테고리의 다른 글
Spring CreateTest (0) | 2019.06.13 |
---|---|
Spring Connection(DataSource) (0) | 2019.06.11 |
spring + mybatis + mysql - 2 (0) | 2019.06.04 |
spring + MyBatis + MySQL - 1 (0) | 2019.06.03 |
spring DB connect (0) | 2019.05.28 |
댓글