반응형

https://github.com/antop-dev/example/tree/master/mybatis-rowbounds-example

 

Mybatis에 RowBounds 클래스를 이용해서 페이징 처리를 할 수 있다.

package org.antop.mybatis.mapper;
import org.antop.mybatis.model.Employee;
import org.apache.ibatis.session.RowBounds;
import java.util.List;

public interface EmployeeMapper {
	List<Employee> select(RowBounds rowBounds);
}

 

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.antop.mybatis.mapper.EmployeeMapper">
	<resultMap id="BaseResultMap" type="org.antop.mybatis.model.Employee">
    	<id property="no" column="emp_no" />
        <result property="gender" column="gender" typeHandler="org.antop.mybatis.handler.GenderTypeHandler" />
        <association property="name" javaType="org.antop.mybatis.model.Name">
        	<result property="first" column="first_name" />
            <result property="last" column="last_name" />
		</association>
	</resultMap>
    
    <select id="select" resultMap="BaseResultMap">
    	select
        	*
		from
        	employees
		order by
        	emp_no asc
	</select>
</mapper>

 

위와 같이 맵퍼와 XML 이 있다.

RowBounds rowBounds = new RowBounds(5, 10);
// 15건을 가져와서 앞에 5건 건너띔
List select = mapper.select(rowBounds);

결과는 10건이 나왔지만 어떻게 동작할까?

여기저기 찾아보고 소스도 대충(?) 보면 offset + limit 만큼 가져와서 offset 만큼 건너띤다.

페이징이 뒤로 갈 수록 느려지게 된다.

RowBounds rowBounds = new RowBounds(29990, 10);
// 30000건을 가져와서 앞에 29990건 건너띔
List select = mapper.select(rowBounds);

데이터의 양이 적다면 쿼리에서 페이징을 하지 않고 RowBouns를 이용하면 빠르게 개발할 수 있을 것이다.

하긴 요즘 수천만건의 데이터를 페이징에서 마지막 페이지를 볼 일이 많을까? -_-/

 

XML 쿼리문에 페이징을 넣지 않고 RowBounds 클래스를 사용하면 자동으로 페이징 쿼리가 실행되게 할 수 없을까?

방법이 있다! Mybatis Intercepter를 이용하면 된다. (인터셉터의 자세한 사용법은 다루지 않겠다 ㅠㅠ)

  1. Mybatis에서 쿼리를 날리기 전에 가로챈다.
  2. RowBounds가 있으면 쿼리에 페이징 문장를 적용한다.
  3. RowBounds를 제거하여 Mybatis에서 페이징 처리를 하지 않도록 한다.

아래 소스는 쿼리에 MySQL용으로 limit 문을 붙여준다.

package org.antop.mybatis.intercepter;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MysqlRowBoundsInterceptor implements Interceptor {
	private static final Logger logger = LoggerFactory.getLogger(MysqlRowBoundsInterceptor.class);
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
    
    public Object intercept(Invocation invocation) throws Throwable {
    	StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
        String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
        RowBounds rb = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
        logger.debug("originalSql = {}", originalSql);
        logger.debug("RowBounds = {}", rb);
        if (rb == null || rb == RowBounds.DEFAULT) { // RowBounds가 없으면 그냥 실행
        	return invocation.proceed();
		}
        
        // RowBounds가 있다!
        // 원래 쿼리에 limit 문을 붙여준다.
        StringBuffer sb = new StringBuffer();
        sb.append(originalSql);
        sb.append(" limit ");
        sb.append(rb.getOffset());
        sb.append(", ");
        sb.append(rb.getLimit());
        
        logger.debug("sql = {}", sb.toString());
        // RowBounds 정보 제거
        metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
        metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
        // 변경된 쿼리로 바꿔치기
        metaStatementHandler.setValue("delegate.boundSql.sql", sb.toString());
        
        return invocation.proceed();
	}
    
    public Object plugin(Object target) {
    	return Plugin.wrap(target, this);
	}
    
    public void setProperties(Properties properties) {
    }
    
}

SqlSessionFactory 설정하는 부분에 인터셉터를 적용하면 된다.

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
	
    <mybatis:scan base-package="org.antop.mybatis"/>
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    	<property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath*:mybatis/*.xml"/>
        <property name="plugins">
        	<list>
            	<!-- 인터셉터 설정 -->
                <bean class="org.antop.mybatis.intercepter.MysqlRowBoundsInterceptor"/>
			</list>
		</property>
	</bean>
    
    <bean id="dataSource" ... />
</beans>

 

실행시켜 보면 쿼리문에 페이징이 적용되서 쿼리가 날라가는 것을 확인할 수 있다.

 

select * from employees order by emp_no asc limit 29990, 10

 

 

출처

http://www.programering.com/a/MTM5gTNwATQ.html

http://hjw1456.tistory.com/10

반응형
//

현지어로 언어명 보여주기

Posted at 2014. 2. 9. 15:34 | Posted in Java+/Example
반응형

Introduction


다국어를 지원하는 홈페이지에서 언어 선택하는 부분을 보면 대표하는 언어로 다국어명이 나오는 것을 볼 수 있다.



이거 따라해 보자 -_-;;




Locale 에서 getDisplayCountry() 뽑아낼 때 인자로 현재 Locale 을 넣어주면 자신의 Locale 언어 그대로 나온다.


for (Locale locale : Locale.getAvailableLocales()) { if (locale.getCountry().isEmpty() == false) { System.out.println("--"); System.out.println(locale.getCountry()); // 이거 System.out.println(locale.getDisplayCountry(locale)); } }


그리고 다국어 처리를 하려면 View 부분이 UTF-8 이어야 한다. 당연한건가? -_-;




Sample


locale.war


↑ 소스 포함 WAR 파일


locale.zip


↑ 메이븐 프로젝트 파일


Spring MVC 의 인터셉터 부분에서 Locale 부분을 처리하여 ModelAndView 에 넣어주는 방식으로 해봤다.



실행해 보면 아래와 같이 나온다.





반응형

'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
//