Notice
Recent Posts
Recent Comments
Today
Total
02-22 19:04
관리 메뉴

ALEPHGEM

MyBatis 동적 쿼리의 기본 본문

IT

MyBatis 동적 쿼리의 기본

알레프 젬 2024. 2. 7. 22:06

MyBatis

Mybatis는 내부적으로 JDBC를 이용해서 preparedStatement 실행시키는 방식으로 구동되는 프레임워크입니다.

매퍼 xml 파일에 SQL문을 작성하고 관리하기 때문에 자바 소스에서 SQL문을 작성할 필요가 없어서 SQL을 수정했을 때 프로젝트를 다시 빌드를 할 필요가 없습니다.

리소스 생성과 해제, try-catch 등 JDBC를 사용할 때의 반복 작업을 줄여줍니다.

SQL를 직접 작성하기 때문에 ORM(JPA) 방식보다 SQL쿼리를 튜닝하는 등 복잡한 쿼리를 다룰 때 더 적합합니다.

 

 

MyBatis 구조

  • 설정 파일(mybatis-config.xml): 데이터 베이스 설정과 트랜잭션 등 동작 규칙을 정의하는 파일입니다.
  • 매퍼 xml 파일: SQL 구문을 정의하고 namespace와 id를 통해 자바 메서드와 상호작용 할 수 있습니다.
  • 결과 매핑과 매핑 구문: SQL 실행 결과를 자바 객체에 설정하는 규칙을 나타내는 결과 매핑과 SQL을 XML에 정의한 것을 매핑 구문이라고 합니다.
  • 지원 파라메터 타입과 반환 결과 타입: Map객체, 자바 모델 객체, 원시 타입(int, String 등), 자바 컬렉션을 지원합니다.

 

 

JAVA (Maven) 프로젝트 생성

Mybatis는 보통 Web Application에서 많이 쓰이지만, 여기서는 일반 JAVA application 프로젝트에서 Mybatis를 설정하는 예제를 다루겠습니다.

  1. 인텔리제이 IDEA에서 File > New > Project를 선택합니다.
  2. Maven을 선택하고, Create from archetype을 선택합니다.
  3. org.apache.maven.archetypes:maven-archetype-quickstart를 선택합니다.
  4. 프로젝트 설정 정보를 입력하고 Finish 합니다.

 

 

Maven 의존성 추가

생성된 프로젝트의 pom.xml 파일을 열어서 Mybatis와 JDBC 드라이버 의존성을 추가합니다.

JDBC 드라이버의 경우 DBMS에 접속에 사용하는 적절한 드라이버와 호환되는 버전으로 설정해야 합니다.

아래 예제는 Mybatis와 MySQL8, Postgresql24, Oracle21에 대한 예제입니다.

<dependencies>
    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.x.x</version> <!-- 3.x.x 대신 정확한 버전 정보 입력 -->
    </dependency>
    
    <!-- postgresql JDBC 드라이버 -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.x.x</version> <!-- 42.x.x 대신 정확한 버전 정보 입력 -->
    </dependency>
    
    <!-- oracle JDBC 드라이버 -->
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc8</artifactId>
        <version>21.x.x.x</version> <!-- 21.x.x.x 대신 정확한 버전 정보 입력 -->
    </dependency>

</dependencies>

 

 

MyBatis 설정

자바 네이티브 애플리케이션 프로젝트의 경우 src/main/resources 폴더에 설정 파일(mybatis-config.xml)을 생성합니다.

일반적인 Web Application의 경우 server.xml 이나 root-context.xml등 에 DB 커넥션 풀링 정보를 설정하기 때문에 mybatis 설정 파일에서 DBMS 접속 정보를 보통 설정하지는 않습니다.

 

postgresql의 경우 (mybatis-config.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="org.postgresql.Driver"/>
                <property name="url" value="jdbc:postgresql://localhost:5432/postgres"/>
                <property name="username" value="유저이름"/>
                <property name="password" value="패스워드"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="프로젝트/패키지/경로/mapper/*.xml"/>
    </mappers>
</configuration>

각자에 맞는 적절한 DBMS 접속 정보를 입력하면 되며, postgresql의 기본 접속 포트는 5432입니다.

 

오라클의 경우  (mybatis-config.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="url" value="jdbc:oracle:thin:@localhost:1521/orcl"/>
                <property name="username" value="유저이름"/>
                <property name="password" value="패스워드"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="프로젝트/패키지/경로/mapper/*.xml"/>
    </mappers>
</configuration>

Oracle의 경우 기본 접속 포트 번호는 1521입니다.

 

properties 파일에 DB접속 정보를 저장해 두고 mybatis-config.xml 파일에서 변수처리할 수도 있습니다.

#오라클의 경우 예제
#dbms.properties 파일 생성 후 DB접속 정보를 저장해둡니다.
username=유저네임
password=패스워드
driver=oracle.jdbc.driver.OracleDriver
url=jdbc\:oracle\:thin\:@localhost\:1521\:orcl
<?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>
	<!-- db 정보 파일을 로드 -->
	<properties resource="패키지/경로/dbms.properties" />
    
    <typeAliases>
        <typeAlias type="모델.패키지.경로.클래스이름" alias="별칭" />
    </typeAliases>
    
	<!-- db 연결 -->
	<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>

	<!-- sql 매퍼 파일(예: sql.xml) 단독 지정 예제. 클래스 패스 기준-->
	<mappers>
		<mapper resource="소스/패키지/경로/sql.xml" />
	</mappers>
	
</configuration>

보다 자세한 mybatis 설정 정보는 다른 게시물에서 따로 언급하겠습니다.

 

매퍼 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">
<!--자바 소스에서  sqlsession을 통해 쿼리를 실행할 때 매퍼의 네임스페이스로 연결됩니다.  -->
<mapper namespace="패키지.경로.Mapper">
    <!-- select 쿼리 예제로 id 값을 통해 자바 소스와 매핑됩니다. -->
    <select id="getUser" resultType="패키지.이름.모델클래스이름">
        SELECT * FROM users WHERE id = #{id}
    </select>
</mapper>

 

 

모델 객체 생성

src/main/java 폴더에 매퍼에서 사용할 모델 클래스를 작성합니다.

이 모델 클래스는 getter와 setter를 포함하는 자바 객체를 사용합니다. 

 

 

SqlSessionFactory 생성

자바 소스 코드에서 sql session factory를 통해 Mybatis를 사용할 수 있습니다.

private SqlSessionFactory getSqlSessionFactory(){
    String resource = "mybatis-config.xml";
    InputStream inputStream;
    try{
        inputStream = Resources.getResourceAsStream(resource);
    }catch(IOException e){
        throw new IllegalArgumentException(e);
    }
    return new SqlSessionFactoryBuilder().build(inputStream);
}

 

 

MyBatis 동적 쿼리

MyBatis는 Map<key, value>를 통해 매개 변수를 자바로부터 전달 받아 동적으로 SQL 쿼리를 구성 후 실행 할 수 있습니다.

아래는 MyBatis에서 사용하는 mapper xml의  Select문 예제입니다. 

<select id="selectExample" resultType="result type 객체(모델 or 자바 컬렉션)">
    SELECT * 
    FROM 테이블이름
    WHERE 1=1
    <!-- where 조건 추가 -->
    <if test="whereCondition1 != null">
        AND col1 = #{whereCondition1}
    </if>
    <!-- Order By 절을 동적으로 구성 -->
    <if test="orderBy != null and orderBy != ''">
        ORDER BY ${orderBy}
    </if>
</select>

이처럼 MyBatis의 매퍼 xml에서는 조건문을 <if test="조건식"> 태그를 이용할 수 있습니다.

 

자바 코드에서 위 매퍼 xml의 SQL을 호출할 때는 Map<key, value>에 put 해서 매개변수들을 전달할 수 있습니다.

여기서 Map의 key를 통해 매퍼 xml의 #{key}에 value를 바인딩시켜서 사용하는 것입니다.

예를 들어 자바 코드에서 다음과 같이 할 수 있습니다.

Map<String, Object> params = new HashMap<>();
params.put("whereCondition1", "예제");
params.put("orderBy", "col1 DESC");

List<ResultType객체> resultList = sqlSession.selectList("mapper네임스페이스이름.selectExample", params);

 

자바 소스에서 Map에 저장된 whereCondition1에 해당하는 "예제"라는 값이 매퍼 xml에서 #{whereCondtion1}에 값이 바인딩되어 SQL select 문의 where절이 구성됩니다.

마찬가지로 자바에서 Map에 저장된 orderBy에 해당하는 "col1 DESC" 값이 매퍼 xml에서 ${orderBy}에 값이 바인딩되어 order by 절이 구성됩니다. 

이렇게 하면 필요에 따라서 where 절과 order by 절을 자바 소스를 통해 동적으로 SQL을 구성해서 실행시킬 수 있게 됩니다.

위 예제는 최종적으로 아래와 같은 쿼리가 동적으로 완성되어 DBMS에서 실행됩니다.

SELECT * 
FROM 테이블이름
WHERE 1=1
AND col1 = '예제'
ORDER BY col1 DESC

 

 

MyBatis 매퍼 xml에서의 변수 처리

위 예제 매퍼 xml의 내용 중에서 order by 절에 사용한 변수  ${orderBy}를 #{orderBy}으로 전달하면 오류는 아니지만 원하는 결과대로 실행되지 않습니다.

왜냐하면 #{whereCondtion1}와 ${orderBy}는 근본적으로 차이점이 있는데 MyBatis에서 #{ }와 ${ }는 각각 다른 종류의 데이터와 바인딩되기 때문입니다. 

 

#{ } 은 Prepared Statement 의 ? 인자를 바인딩하는 용도로 사용합니다.

따라서 악의 적인 사용자 입력 값에 의도적으로 삽입된 SQL문을 실행하는 공격인 SQL Injection 공격으로부터 방어할 수 있습니다.  

SELECT * FROM 유저테이블 WHERE name = #{name}

 

반면 ${ } 은 String 문자열을 단순 치환하는 용도로 사용합니다.

사용자 입력값을 그대로 ${ }으로 바인딩해서 사용하는 경우 SQL Injection 공격으로 원하지 않은 쿼리가 삽입되어 실행될 수 있습니다.

그래서 이 경우 따로 사용자 입력 값을 검증하는 절차가 필요합니다.

SELECT * FROM 유저테이블 WHERE name = '${name}'

 

이렇게 같은 내용을 전달하더라도 내부적으로 동작 방식이 다르므로 order by 절에 ${ } 변수로 전달했던 것입니다.

 

 

 

 

 

 

 

 

'IT' 카테고리의 다른 글

postgresql14 사용자 원격 접속 설정  (26) 2024.02.14
Web application 에서 popup window의 대안  (29) 2024.02.05