Mybatis에 DAO 와 Mapper
SpringFramework 을 이용하다보면 데이터베이스 액세스를 위해서 MyBatis 를 사용하곤 한다. SQL 매퍼라고 불리기도 하는 것인데, 이를 이용하면 손쉽게 자바 코드와 SQL 문을 분리해줄 수 있을뿐만 아니라 MyBatis 에서 제공하는 여러가지 추가적인 기능을 이용해 데이터베이스를 좀 더 유연하게 사용할 수 있다.
DAO
보통 MyBatis 를 이용할대는 DAO 를 구조를 사용하곤 했다. Data Access Object 라고 불리는 것으로 말 그대로 데이터 접근을 위한 객체로서 sqlSession 객체를 이용해 데이터베이스 조회만 전담하는 객체다.
이 구조는 인터페이스와 그것을 구현한 구현체 클래스가 있어야 한다. 이를 위해서 Spring의 컨텍스트 설정을 해줘야 하는데 대략 다음과 같다.
1 2 3 4 5 6 7 8 9 10 |
<!-- MyBatis --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:sqlMap/*.xml" /> <property name="configLocation" value="classpath:config/mybatisV3-config.xml"/> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> |
sqlSession 을 빈으로 등록해준다. 그리고 다음과 같이 DAO 를 작성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Repository public class UserDAOImpl implements UserDAO { @Autowired private SqlSession sqlSession; private static final String NS = "systemv.user."; @Override public UserVO getUser(String username) { logger.debug("username: {}", username); return sqlSession.selectOne(NS+"getUser", username); } |
UserDAOImpl 은 UserDAO 인터페이스의 구현체다. SqlSession 의 빈을 가지고 오기 위해서 와이어를 걸어서 가지고 왔고, SQL 매퍼를 찾기위한 네임스페이스와 파라메터 인자를 주고 selectOne 메소드를 호출하고 있다.
이제 이것을 Service 계층에서, 역시나, 와이어를 걸어서 가지와서 데이터베이스에 데이터를 값을 호출하고 있다.
1 2 3 4 5 |
@Service public class UserServiceImpl implements UserService { @Autowired private UserDAO userDAO; |
많은 프로젝트에서 이와 유사한 구조를 자주 보게 된다.
Mapper
그런데, 신기하게도 Mapper 을 이용해서 DAO 를 사용할 수도 있다. 이 기능은 MyBatis 3.0 이상부터 지원하기 시작한 것으로 Mapper 인터페이스만 구현하고 Service 계층에서 바로 와이어를 걸어서 사용할 수 있도록 했다.
하지만 DAO 구조에서도 얼마든지 Mapper 를 사용할 수 있다. sqlSession 에 메소드의 파라메터를 Mapper 클래스로 넘기면 된다. 먼저 Mapper 인터페이스를 다음과 같이 만든다.
1 2 3 4 |
@Mapper public interface UserTableMapper { public UserVO getUser(@Param("username") String username); } |
그리고 앞에 DAO 구현체에 메소드를 다음과 같이 바꾼다.
1 2 3 4 5 6 |
@Override public UserVO getUser(String username) { logger.debug("username: {}", username); UserTableMapper mapper = sqlSession.getMapper(UserTableMapper.class); return mapper.getUser(username); } |
sqlSession 객체에는 getMapper 는 메소드가 존재하고 이는 MyBatis 가 지원하는 Mapper 인터페이스를 받게 되어 있다.
문제는 MyBatis 의 namespace 인데, Mapper 인터페이스의 경로를 적어주면 되며 id 가 Mapper 인터페이스의 메소드 이름과 매핑된다.
1 2 |
<mapper namespace="io.systemv.dao.UserTableMapper"> <select id="getUser" resultType="UserVO"> |
이렇게 함으로써 Mapper 를 이용하면서도 DAO 구조를 그대로 유지할 수 있다. 문제는 DAO 에서 Mapper 인터페이스 객체를 매번 생성해 줘야 한다는 것이다. sqlSession.getMapper 를 이용해서 매번 가지고 오게 되는데, 이렇게 하는 것보다는 Spring Context 에서 빈으로 등록해 와이어로 가지고 오게 되면 DAO 가 아닌 Service 계층에서 호출이 가능하다.
다음과 같이 빈으로 등록한다.
1 2 3 4 |
<bean id="UserTableMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="io.systemv.dao.UserTableMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> |
이제 Service 계층에서 다음과 같이 와이어로 가지고 오고 바로 사용할 수 있다.
1 2 3 4 5 |
@Service public class UserServiceImpl implements UserService { @Autowired public UserTableMapper userTableMapper; |
하지만 이렇게 할 경우에 Mapper 인터페이스 객체 하나하나 전부 Spring Context 에서 빈으로 만들어 줘야 한다. 그래서 이렇게 하지말고 Mapper 인터페이스를 스캔하도록 할 수 있는데 이를 위해서 mybatis-spring 라이브러리 패키지와 xsd 설정을 해줘야 한다. xsd 과 설정은 Spring Context 파일에 다음과 같이 해준다.
1 2 3 4 |
xmlns:mybatis-srping="http://mybatis.org/schema/mybatis-spring" xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd"> <mybatis-spring:scan base-package="io.systemv.mapper" /> |
Mapper 인터페이스 클래스 파일들을 전부 위 설정에 나온 패키지 경로로 모두 옮긴다. 그리고 각 Mapper 인터페이스에 @Mapper 어노테이션을 붙여준다. 또한 매퍼 XML 에 네임스페이스 경로도 모두 위 설정으로 바꿔 준다.
이렇게 하면 DAO 관련 파일들은 전부 삭제해도 잘 동작 한다.