본문 바로가기

JAVA/Spring

MyBatis Mapper

반응형

MyBatis Mapper


MyBatis만을 이용해서 프로젝트를 구성할 때 DAO  구성을 좀 더 간단하게 할 수 있는 방법으로  Mapper 인터페이스를 이용해서 자동으로 객체를 생성하는 방법


기존방식

1) DAO 인터페이스 작성

2) Mapper XML 생성 및  SQL 처리

3) DAO 인터페이스를 구현한 구현클래스의 작성


변경

1) Mapper 인터페이스 작성

2) MyBaits 애노테이션 또는 Mapper XML 작성


mybatis-spring이 자동으로 Mapper 인터페이스를 구현한 프록시 객체를 만들어준다.


XML  네임스페이스 지정

root-context.xml 파일의 네임스페이스를 지정할때 mybaits-spring을 지정


DataSource, SqlSessionFactory 설정

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

<property name="url" value=jdbc:mysql://127.0.0.1:3306/book_ex"></property>

<property name="username" value=zerock"></property>

<property name="password" value=zerock"></property>

</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

<property name="dataSource" ref=dataSource"></property>

</bean>



Mapper 인터페이스 작성

 public interface SampleMapper {

@Select("select now()")

public String getTime();

}



@Select, @Insert, @Update, @Delete 애노테이션 사용


인터페이스 Mapper의 인식


mabatis-spring의 기능을 활용

<bean id="sampleMapper" class="org.mybatis.spring.SqlSessionFactoryBean">

<property name="mapperInterface" value="org.zerock.persistence.SampleMapper"></property>

<property name="sqlSessionFactory" ref=sqlSessionFactory"></property>

</bean>


MapperFactoryBean을 이용하면 개발자가 직접 DAO를 설정하지 않고 자동으로 Mapper 인터페이스를 활용하는 객체를 생성하게 된다.


자동으로 매퍼 인식

MyBatis는 이를 자동으로 처리할 수 있는  Mapper-Scan 기능을 제공


root-context.xml

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

<property name="dataSource" ref=dataSource"></property>

</bean>


<mybatis-spring:scan base-package="org.zerock.persistence" />

mybatis-spring:scan은 자동으로 해당 패키지 안에서 선언된 인터페이스를 Mapper로 인식하는 역할을 하게 된다.



테스트

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})

public class SampleMapperTest {

@Inject

private SampleMapper mapper;


@Test

public void testTime() {

System.out.println(mapper.getClass().getName());

System.out.println(mapper..getTime());

}

}

testTime() 메소드의 내부를 보면 현재 만들어진 mapper 변수의 진짜 클래스 이름을 알아 볼 수 있다.

특이한점은 실제 클래스명이 com.sun.proxy.$Proxy21과 같은 형식으로  보여진다는 점이다.

즉 실제 클래스는 SampleMapper 인터페이스를 자동으로 구현한 클래스가 되는 것이다.


이렇게 자동으로 만들어진 클래스는 DAO 인터페이스를 매번 SqlSessionTemplate을 이용해서 직접 코드에 사용하는 것과 비교가 된다.


@Param을 이용한 다중 파라미터 처리

두개 이상의 파라미터를 DAO에서 처리하는 경우가 종종 있는데, 기존의 경우 가장 많이 사용하는 방법은 별도의 Map을 구성해서 처리하거나, 클래스를 구성해서 처리하는 방식


 MyBatis의 애노테이션을 사용한다면 두개 이상의 파라미터에서 각각 @Param 이름의 애노테이션을 붙여서 처리할 수 있다.

 예를 들어 회원의 아이디와 패스워드를 이용해서 회원의 메일 주소를 파악한다면 다음과 같이 작성할 수 있다.

  public interface SampleMapper {

  @Select("select now()")

  public String getTime();


  @Select("select email from tbl_member where userid = #{id} and userpw = ${pw}")

  public String getEmail(

  @Param("id") String id,

  @Parma("pw") String pw);

 

}


인터페이스 메소드의 파라미터를 보면 @Param을 이용해서 직접 값을 부여할 수 있다.

@Param의 값으로 지정된 문자열은 #{id}와 같이 사용가능하다.


  @Test

public void testMail() {

String email = mapper.getEmail("user10", "user10");

System.out.println(email);

}



Mapper 인터페이스와  XML을 같이 활용하기


Mapper 인터페이스를 이용하는 경우의 또 다른 장점은 기존 XML을 그대로 사용할 수 있다는 점이다.

두가지 규칙만 명심하면된다..


 - Mapper 인터페이스의 이름과 XML  Mapper의 네임스페이스를 반드시 일치하도록 작성할 것

 -  XML의 id는  Mapper 인터페이스의 메소드 이름과 동일하게 작성할 것

  public interface SampleMapper {

  @Select("select now()")

  public String getTime();


  @Select("select email from tbl_member where userid = #{id} and userpw = ${pw}")

  public String getEmail(

  @Param("id") String id,

  @Parma("pw") String pw);


  public String getUserName(

  @Param("id") String id,

  @Param('pw') String pw); 

}


새롭게 추가된 getUserName()의 경우 애노테이션으로 처리되는 부분이 없다.

getUserName()에서 사용할 SQL은 XML Mapper를 생성해서 처리한다.


src/main/resources 밑에 mappers 폴더를 만들고 sampleMapper.xml을 추가한다.


<?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.persistence.SampleMapper">


<select id="getUserName" returnType="String">

select username from tbl_member

where userid = ${id} and userpw = ${pw}

</select>

</mapper>



root-context.xml 파일의 수정

XML Mapper 파일을 제대로 인식하기 위해서 기존과 동일한 방법으로 XML Mapper를 인식할 수 있도록 준비하는 작업이 필요하다.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

<property name="dataSource" ref=dataSource"></property>

<property name="mapperLocations" value="classpath:mappers/**/*Mapper.xml"></property>

</bean>


  @Test

public void testUserName() {

String name = mapper.getUserName("user10", "user10");

System.out.println(name);

}




Dynamic SQL과  @SelectProvider


인터페이스를 Mapper로 활용하는 방식은 기존의 XML을 그대로 활용할 수 있기 때문에 거부감 없이 프로젝트에 적용할 수 있다.


XML을 이용하는 가증 큰 이유중 하나는 동적 SQL을 처리하기 위해서인데, MyBatis의 경우 이 처리 역시 XML을 이용해서 작성하거나, @SelectProvider라는 것을 이용해서 처리할 수 있다.


@SelectProvider는 말그대로 Select 구문을 만들어 내는 역할을 한다.

@SelectProvider에게 가장 중요한 속성은  type과 method이다.


-type : SQL문을 만들어내는 클래스

-method: SQL문이 반환되는 메소드의 이름


@SelectProvider의 대상이 되는 클래스의 제약은 없다. 어떤 종류의 클래스이건 사용 가능하고 method는 문자열이 반환되면 된다.


한가지주의해야하는 조건은 메소드가 static으로 작성되어야 한다는점이다.

@SelectProvider는 객체를 생성하지 않도록 설계되어 있다.


public class SampleProvider {

public static String searchUserName(Map<String, Object> params) {


String searchFront =

"select username "+ "from tbl_member " + "where 1=1 ";

if(params.get("type").equals("id")) {

searchFront += " and userid = #{keyword}";

}


return searchFront;

}

}


메소드의 파라미터는 두 개 이상의 데이터를 던질 수  있도록 Map<String, Object>로 설정한다.


이를 이용하는 Mapper 인터페이스는 다음과 같다

@SelectProvider(type=SampleProvider.class, method="searchUserName")

public String search(

@Param("type") String type,

@Parma("keyword") String keyword);


@SelectProvider 의 경우 Java 코드를 이용해서 SQL문을 생성해 내기 때문에 복잡한  SQL문을 생성해야 하는 경우 유용하게 사용할 수 있다.


#{id}와 ${id}, @SelectProvider


MyBatis의 동작은 기본적으로 JDBC의 PreparedStatement이다.


PreparedStatement는 그이름 처럼 미리 SQL 문을 생성해놓고, 주어진 변수를 대입시켜서 실행하게 된다.

가끔 동적 SQL 문이 복잡한 경우 ${}을 쓰는 경우가 있는데, JDBC의 Statement와 유사하게 동작한다.


SQL Injection 공격의 여지가 있는 Statement는 절대로 사용해서는 안되지만, 가끔 다양한 체크박스가 제공하는 화면에서 처리를 쉽게 하기 위해서 '${}'를 사용하는 경우가 있다.


만일 MyBatis만을 이용해서 프로젝트를 구성한다면 @SelectProvider를 이용하면 SQL Injection 공격에 대한 염려 없는 좋은 대안이 된다.

반응형

'JAVA > Spring' 카테고리의 다른 글

스프링3 개론  (0) 2018.03.05
Spring Boot  (0) 2017.12.19
Spring login with cookie  (0) 2017.12.14
Spring Interceptor HttpSession Login  (0) 2017.12.13
Spring Interceptor  (0) 2017.12.13