상세 컨텐츠

본문 제목

스프링과 마이바티스 자료 Spring + Mybatis 에서 Query 변경 서버 재 시작 없이 Reload (퍼옴)

보안/java

by MustThanks 2023. 7. 17. 20:10

본문

반응형

Spring + Mybatis 에서 Query 변경시 서버 재시작 없이 Reload 하기 (tistory.com)   에서 가지고 왔습니다.

 

spring context support : 어플리케이션 컨텍스트 컨테이너에서 사용되지만 자주 사용되지 않는 부가적인 기능을 분리해 놓은 것으로 캐쉬,메일,스케줄링,UI 등과 관련된 것이 있음 spring-context 를 포함하고 있어 dependency 설정 과정에서

spring context support로 선언 하면 편리하니 참고하세요

 

----------------- 아래 부터는 가지고 온 것입니다.  문제 시 삭제 하도록 하겠습니다. -------------------

 

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();
 
}
 
}

일단은 이 유명한 RefreshableSqlSessionFactoryBean 클래스를 적당한 곳에 생성을 해 놓는다. 

 

그리고 SqlSessionFactory를 설정하는 곳에서 다음과 같이 설정을 해준다. 

****-context.xml (spring application context 설정부)

 
<!--<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>

1 line에 있는 기존의 org.mybatis.spring.SqlSessionFactoryBean 대신 RefreshableSqlSessionFactoryBean을 끼워준다. 그리고 RefreshableSqlSessionFactoryBean 에서 설정 가능한 interval이라는 속성도 넣어준다. Refresh를 해주는 주기이다. (ms) 즉 1초다마 계속 Refresh를 해주겠다는거다. 이정도면 고치고 저장을 하면 거의 실시간으로 변경된 내용이 반영이 된다고 보면 된다.

 

이렇게 설정까지 만들었다면 WAS가 기동된 상태에서 특정 쿼리를 수정해보자. 그리고 바로 결과가 잘 반영이 되는지 확인을 해보자. RefreshableSqlSessionFactoryBean 관련된 로그가 나오며 update 되었다는 문구가 나오면 성공이다. 

'보안 > java' 카테고리의 다른 글

관련글 더보기

댓글 영역