MyBatis/Best Practice JOIN Query

MyBatis Best Practice of JOIN Query

Soul-Learner 2014. 4. 5. 12:30
MyBatis Emp example


FROM


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
<mapper namespace="BlogBestPractice">
     
   <resultMap id="resultBlog" type="Blog">
        <id property="id" column="idBlog"/>
        <result property="name" column="blogName"/>
        <result property="url" column="url"/>
        <association property="author" column="idBlog" javaType="Author">
            <id property="id" column="idAuthor"/>
            <result property="name" column="authorName"/>
            <result property="email" column="email"/>
        </association>
        <collection property="posts" column="idBlog" javaType="ArrayList" ofType="Post">
            <id property="id" column="idPost"/>
            <result property="title" column="title"/>
            <collection property="tags" column="idBlog" javaType="ArrayList" ofType="Tag">
                <id property="id" column="idTag"/>
                <result property="value" column="value"/>
            </collection>
        </collection>
    </resultMap>
     
     
    <select id="selectBlogBestPractice" resultMap="resultBlog">
        SELECT
            B.idBlog as idBlog, B.name as blogName, B.url as url,
            A.idAuthor as idAuthor, A.name as authorName, A.email as email ,
            P.idPost as idPost, P.title as title,
            T.idTag as idTag, T.value as value
        FROM BLOG as B
            left outer join Author A on B.idBlog = A.idBlog
            left outer join Post P on P.idBlog = B.idBlog
            left outer join Post_Tag PT on P.idPost = PT.idPost
            left outer join Tag T on PT.idTag = T.idTag
    </select>
     
</mapper>



위의 예제를 참고하여 오라클에서 Emp 테이블을 대상으로 JOIN 문장을 작성하고 그 결과를 3개의 클래스와 매핑하는 예제를 작성해본다

최종적으로 MyBatis 으로부터 얻어 내고자 하는 것은 다음과 같이 3개의 모델 오브젝트에 JOIN 문장의 결과가 저장되어 리턴되도록 하는 것이다.

또한, 1 : N 의 관계설정으로 특정 사원의 모든 부하직원에 대한 정보도 함께 가져오도록 작성하려고 한다


JOIN 문장의 결과를 저장할 모델 클래스

public class Employee {

private int empno;                          //Emp 테이블의 정보 매핑

private String ename;

private Dept dept;                        //DEPT 테이블의 정보 매핑

private Salgrade salgrade;          //Salgrade 테이블의 정보 매핑

private List<Employee> member; // 해당직원의 모든 부하직원 정보 매핑

     .........

    .......(Setter, Getter 생략)

}



3 Model Objects At a Query

JOIN 문장이 실행된 후에 메모리에 생성된 가상의 테이블에는 다수개의 테이블로부터 복사된 값들이 존재하므로 각각의 컬럼 값들은 몇개의 서로 다른 클래스와 연결하여 사용할 필요가 있다. 즉, EMP 테이블에서 가져온 데이터는 Employee 테이블에 저장하고, DEPT 테이블에서 가져온 데이터는 Dept 클래스에 저장하고 SALGRADE 테이블에서 가져온 데이터는 Salgrade 클래스에 저장하여 사용하는 것이 바람직할 것이다.



Configuration.xml

<?xml version="1.0" encoding="UTF-8" ?>


<!DOCTYPE configuration 

PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 

"http://mybatis.org/dtd/mybatis-3-config.dtd">


<configuration>

<properties resource="emp/db.properties" />


<typeAliases>

<typeAlias type="emp.Emp" alias="Emp" />

<typeAlias type="emp.Employee" alias="Employee" />

<typeAlias type="emp.Dept" alias="Dept" />

<typeAlias type="emp.Salgrade" alias="Salgrade" />

</typeAliases>


<environments default="development">

<environment id="development">

<transactionManager type="JDBC" />

<dataSource type="POOLED">

<property name="driver" value="${driver}" />

<property name="url" value="${url}" />

<property name="username" value="${username}" />

<property name="password" value="${password}" />

</dataSource>

</environment>

</environments>

<mappers>

<mapper resource="emp/EmpMapper.xml" />

<mapper resource="emp/EmployeeMapper.xml" />

</mappers>


</configuration>




JOIN 문장 정의 및 결과 매핑 설정


JOIN 문장이 실행되어 리턴되는 컬럼의 이름은 서로 다르게 해야 한다. 컬럼명이 동일한 경우에는 별칭을 사용하여 유일한 컬럼명을 사용하도록 주의해야 한다

<association>, <connection> 요소의 속성 중 column 속성의 값은 ofType, 혹은 javaType 속성의 값으로 지정한 클래스와 매핑될 컬럼 중에서 임의의 컬럼을 지정하면 된다.

결과 매핑에 사용된 <id> 요소는 MyBatis 실행성능에 도움이 되도록 설정하는 것이며 각 레코드를 식별할 수 있는 유일성을 가진 컬럼명을 지정하면 된다. 지정하지 않아도 오류는 발생하지 않지만 성능을 위해 <id>요소를 지정하도록 한다. <id>로 지정한 속성에도 컬럼의 값이 매핑되는 점은 <result>의 기능과 같다


EmployeeMapper.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="emp.EmployeeMapper">


    <resultMap id="resultEmployee" type="Employee">

         <id property="empno" column="empno"/>

         <result property="ename" column="ename"/>


        <association property="dept" column="deptno" javaType="Dept">

            <id property="deptno" column="deptno"/>

            <result property="dname" column="dname"/>

            <result property="loc" column="loc"/>

        </association>

        

        <association property="salgrade" column="grade" javaType="Salgrade">

            <id property="grade" column="grade"/>

            <result property="losal" column="losal"/>

            <result property="hisal" column="hisal"/>

        </association>

        

        <collection property="member" column="e2_empno" javaType="ArrayList" ofType="Employee">

            <id property="empno" column="e2_empno"/>

            <result property="ename" column="e2_ename"/>

        </collection> 

    </resultMap>

    

    <select id="getEmployee" resultMap="resultEmployee">

   SELECT e.empno, e.ename, e.sal, d.deptno, d.dname, d.loc, 

    s.grade, s.losal, s.hisal,

    e2.empno as e2_empno, e2.ename  as e2_ename

    FROM emp e 

INNER JOIN dept d ON e.deptno=d.deptno

INNER JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal

LEFT OUTER JOIN emp e2 ON e.empno=e2.mgr

    WHERE e.empno=#{empno}

    </select>


</mapper>


위의 SQL 문장을 실행할 때 사번에 7698을 적용하면 다음과 같이 5개의 레코드가 리턴되며 각 컬럼의 값은 다른 레코드와 동일하지만 e2.empno, e2.ename 만 서로 다르다. 그러므로 MyBatis 가 한개의 Employee 객체에 아래의 SQL 실행 결과를 저장할 때 레코드마다 값이 다른 컬럼은 List 와 같은 컬렉션에 담아서 Employee 에 할당하면 되므로 아래와 같은 결과도 한개의 Employee 객체에 담을 수가 있다

7698 BLAKE 2850 30 SALES CHICAGO 4 2001 3000 7499 ALLEN

7698 BLAKE 2850 30 SALES CHICAGO 4 2001 3000 7521 WARD

7698 BLAKE 2850 30 SALES CHICAGO 4 2001 3000 7654 MARTIN

7698 BLAKE 2850 30 SALES CHICAGO 4 2001 3000 7844 TURNER

7698 BLAKE 2850 30 SALES CHICAGO 4 2001 3000 7900 JAMES



위의 SQL 문장에서 동일한 컬럼명이 사용되는 것을 피하기 위해 별칭을 사용한 것과 결과 매핑시에 그 별칭을 사용하여 특정 클래스와 매핑이 되도록 설정한 점에 주의해야 한다. 위 문장의 경우에는 e.empno, e2.empno, e.ename, e2.ename 부분이 컬럼명이 동일하기 때문에 별칭을 지정하여 e2_empno, e2_ename 과 같이 지정하여 사용하고 있다


EmployeeMapper.java

package emp;


public interface EmployeeMapper {

public Employee getEmployee(int empno);

}



Employee.java
package emp;

import java.util.List;

public class Employee {
private int empno;
private String ename;
private Dept dept;
private Salgrade salgrade;
private List<Employee> member;
public int getEmpno() {
return empno;
}
public void setEmpno(int empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}

public Salgrade getSalgrade() {
return salgrade;
}

public void setSalgrade(Salgrade salgrade) {
this.salgrade = salgrade;
}

public List<Employee> getMember() {
return member;
}

public void setMember(List<Employee> member) {
this.member = member;
}
}


Dept.java

package emp;


public class Dept {

private int deptno;

private String dname;

private String loc;

public int getDeptno() {

return deptno;

}

public void setDeptno(int deptno) {

this.deptno = deptno;

}

public String getDname() {

return dname;

}

public void setDname(String dname) {

this.dname = dname;

}

public String getLoc() {

return loc;

}

public void setLoc(String loc) {

this.loc = loc;

}

}



Salgrade.java

package emp;


public class Salgrade {

private int grade;

private int losal;

private int hisal;

public int getGrade() {

return grade;

}

public void setGrade(int grade) {

this.grade = grade;

}

public int getLosal() {

return losal;

}

public void setLosal(int losal) {

this.losal = losal;

}

public int getHisal() {

return hisal;

}

public void setHisal(int hisal) {

this.hisal = hisal;

}

}



EmpMybatisDAO.java

package emp;


import java.io.*;

import java.util.*;


import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.*;


public class EmpMybatisDAO {

private static EmpMybatisDAO instance = new EmpMybatisDAO();


private String resource = "emp/Configuration.xml";

private Reader reader;

private SqlSessionFactory sqlMapper;

public static EmpMybatisDAO getInstance() {

return instance;

}

private EmpMybatisDAO() {

try {

reader = Resources.getResourceAsReader(resource);

sqlMapper = new SqlSessionFactoryBuilder().build(reader);

} catch (IOException e) {

e.printStackTrace();

}

}


public Employee getEmployee(int empno){

SqlSession session = sqlMapper.openSession();

try {

EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

Employee employee = mapper.getEmployee(empno);

return employee;

} catch(Exception ex){

ex.printStackTrace();

}finally {

session.close();

}

return null;

}

}