본문 바로가기
SpringFramework | SpringBoot

MyBatis에서 mapper.xml 재기동 없이 반영하기.

by Lcoding 2023. 11. 8.
반응형

MyBatis에서 mapper.xml 재기동 없이 반영하기.

RefreshableSqlSessionFactoryBean.java

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
 
public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
 
    private static final Logger LOG = LoggerFactory.getLogger(RefreshableSqlSessionFactoryBean.class);
 
    private SqlSessionFactory proxy;
    private int interval = 1000;
 
    private Timer timer;
    private TimerTask task;
 
    private Resource[] mapperLocations;
 
    private boolean running = false;
 
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();
 
    public void setMapperLocations(Resource[] mapperLocations) {
        super.setMapperLocations(mapperLocations);
        this.mapperLocations = mapperLocations;
    }
 
    public void setInterval(int interval) {
        this.interval = interval;
    }
 
    public void refresh() throws Exception {
        w.lock();
        try {
            super.afterPropertiesSet();
        } finally {
            w.unlock();
        }
        
        LOG.info("sqlMapClient refreshed.");
    }
 
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();
 
        setRefreshable();
    }
 
    private void setRefreshable() {
        proxy = (SqlSessionFactory) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),
                new Class[] { SqlSessionFactory.class }, new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        return method.invoke(getParentObject(), args);
                    }
                });
 
        task = new TimerTask() {
            private Map<Resource, Long> map = new HashMap<Resource, Long>();
 
            public void run() {
                if (isModified()) {
                    try {
                        refresh();
                    } catch (Exception e) {
                        LOG.error("caught exception", e);
                    }
                }
            }
 
            private boolean isModified() {
                boolean retVal = false;
 
                if (mapperLocations != null) {
                    for (int i = 0; i < mapperLocations.length; i++) {
                        Resource mappingLocation = mapperLocations[i];
                        retVal |= findModifiedResource(mappingLocation);
                    }
                }
 
                return retVal;
            }
 
            private boolean findModifiedResource(Resource resource) {
                boolean retVal = false;
                List<String> modifiedResources = new ArrayList<String>();
 
                try {
                    long modified = resource.lastModified();
 
                    if (map.containsKey(resource)) {
                        long lastModified = ((Long) map.get(resource)).longValue();
 
                        if (lastModified != modified) {
                            map.put(resource, new Long(modified));
                            modifiedResources.add(resource.getDescription());
                            retVal = true;
                        }
                    } else {
                        map.put(resource, new Long(modified));
                    }
                } catch (IOException e) {
                    LOG.error("caught exception", e);
                }
                if (retVal) {
                    LOG.info("modified files : " + modifiedResources);
                }
                return retVal;
            }
        };
 
        timer = new Timer(true);
        resetInterval();
 
    }
 
    private Object getParentObject() throws Exception {
        r.lock();
        try {
            return super.getObject();
        } finally {
            r.unlock();
        }
    }
 
    public SqlSessionFactory getObject() {
        return this.proxy;
    }
 
    public Class<? extends SqlSessionFactory> getObjectType() {
        return (this.proxy != null ? this.proxy.getClass() : SqlSessionFactory.class);
    }
 
    public boolean isSingleton() {
        return true;
    }
 
    public void setCheckInterval(int ms) {
        interval = ms;
 
        if (timer != null) {
            resetInterval();
        }
    }
 
    private void resetInterval() {
        if (running) {
            timer.cancel();
            running = false;
        }
        if (interval > 0) {
            timer.schedule(task, 0, interval);
            running = true;
        }
    }
 
    public void destroy() throws Exception {
        timer.cancel();
    }
}




**context.xml 에 해당 내용 추가 [클래스 패스로 xml 파일의 경로를 체크하고 interval로 재기동 없이 지속적으로 반영할 시간을 조정한다.]

<!--<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">-->
<bean id="sqlSessionFactory" class="com.oingdaddy.RefreshableSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:/mybatis-config.xml" />
    <property name="mapperLocations" value="classpath:/mapper/**/*.xml" />
    <property name="interval" value="1000" />
</bean>

반응형

'SpringFramework | SpringBoot' 카테고리의 다른 글

REST API URI 작성 요령  (0) 2023.11.09
Properties에 디비 관련 설정[개발,운영]  (0) 2023.11.09
Map vs VO  (0) 2023.11.08
HttpURLConnection[GET,POST]  (0) 2023.11.08
fixedRate & fixedDelay_SpringScheduled  (0) 2023.11.07

# 로딩 화면 동작 코드(Code) 설정하기
loading