상세 컨텐츠

본문 제목

스프링과 마이바티스 자료 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' 카테고리의 다른 글

Class<SpringJUnit4ClassRunner> cannot be resolved to a type STS 20230926  (0) 2023.09.26
Java 버전별 major version 번호.  (0) 2023.09.09
Factory Method Pattern(팩토리 메소트 패턴)  (0) 2023.04.10
SOLID 원칙  (0) 2023.04.04
AOP 설명  (0) 2023.03.17

관련글 더보기

댓글 영역