환경 :
Spring-version : 3.2.18RELEASE
Ibatis-version : 2.3.4.726
- 새로고침(F5) 누를시 변경된 쿼리 실시간으로 적용하기 -
★ 제일하단 복붙할수있게 메모장으로 따로 첨부파일 빼놨음 ★
1. pom.xml에 dependency 추가
<dependency>
<groupId>backport-util-concurrent</groupId>
<artifactId>backport-util-concurrent</artifactId>
<version>3.1</version>
</dependency>
2. 해당 경로 및 applicationContext.xml 설정
2-1. applicationContext.xml 코드
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true" />
<!-- DriverManagerDataSource는 커넥션(connection pool)을 지원하지 않는다. -->
<!-- 그러므로 이후엔 아파치가 제공하는 BasicDataSource를 사용한다. -->
<!-- oracle 기준 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
<property name="username" value="hr"/>
<property name="password" value="hr"/>
</bean>
<!-- iBATIS는 스프링 프레임워크에서 제공해준다. -->
<bean id="sqlMapClient" class="com.jy.core.RefreshableSqlMapClientFactoryBean"> <!-- com.jy.core 패키지안에 해당 클래스 만들기 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:/sqlMapConfig.xml"></property>
<property name="lobHandler" ref="lobHandler" />
<property name="checkInterval" value="1000"/> <!-- 1초 단위로 변경된 쿼리 검사 -->
</bean>
<!-- <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">-->
<!-- <property name="dataSource" ref="dataSource"></property>-->
<!-- <property name="configLocation" value="classpath:sqlmap/sqlMapConfig.xml"></property>-->
<!-- </bean>-->
<bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
<property name="sqlMapClient" ref="sqlMapClient"></property>
</bean>
</beans>
3. com.jy.core.(본인패키지 경로) RefreshableSqlMapClientFactoryBean.java 클래스 파일 만들기
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.jy.core;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock;
import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantReadWriteLock;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
import org.springframework.orm.ibatis.SqlMapClientFactoryBean;
public class RefreshableSqlMapClientFactoryBean extends SqlMapClientFactoryBean implements SqlMapClientRefreshable, DisposableBean {
private static final Log logger = LogFactory.getLog(RefreshableSqlMapClientFactoryBean.class);
private SqlMapClient proxy;
private int interval;
private Timer timer;
private TimerTask task;
private Resource[] configLocations;
private Resource[] mappingLocations;
private boolean running = false;
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r;
private final Lock w;
public RefreshableSqlMapClientFactoryBean() {
this.r = this.rwl.readLock();
this.w = this.rwl.writeLock();
}
public void setConfigLocation(Resource configLocation) {
super.setConfigLocation(configLocation);
this.configLocations = configLocation != null ? new Resource[]{configLocation} : null;
}
public void setConfigLocations(Resource[] configLocations) {
super.setConfigLocations(configLocations);
this.configLocations = configLocations;
}
public void setMappingLocations(Resource[] mappingLocations) {
super.setMappingLocations(mappingLocations);
this.mappingLocations = mappingLocations;
}
public void refresh() throws Exception {
this.w.lock();
try {
super.afterPropertiesSet();
} finally {
this.w.unlock();
}
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
this.setRefreshable();
}
private void setRefreshable() {
this.proxy = (SqlMapClient)Proxy.newProxyInstance(SqlMapClient.class.getClassLoader(), new Class[]{SqlMapClient.class, ExtendedSqlMapClient.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(RefreshableSqlMapClientFactoryBean.this.getParentObject(), args);
}
});
this.task = new TimerTask() {
private Map map = new HashMap();
public void run() {
if (this.isModified()) {
try {
RefreshableSqlMapClientFactoryBean.this.refresh();
} catch (Exception var2) {
RefreshableSqlMapClientFactoryBean.logger.error("caught exception", var2);
}
}
}
private boolean isModified() {
boolean retVal = false;
int i;
Resource mappingLocation;
for(i = 0; i < RefreshableSqlMapClientFactoryBean.this.configLocations.length; ++i) {
mappingLocation = RefreshableSqlMapClientFactoryBean.this.configLocations[i];
retVal |= this.findModifiedResource(mappingLocation);
}
if (RefreshableSqlMapClientFactoryBean.this.mappingLocations != null) {
for(i = 0; i < RefreshableSqlMapClientFactoryBean.this.mappingLocations.length; ++i) {
mappingLocation = RefreshableSqlMapClientFactoryBean.this.mappingLocations[i];
retVal |= this.findModifiedResource(mappingLocation);
}
}
return retVal;
}
private boolean findModifiedResource(Resource resource) {
boolean retVal = false;
ArrayList modifiedResources = new ArrayList();
try {
long modified = resource.lastModified();
if (this.map.containsKey(resource)) {
long lastModified = (Long)this.map.get(resource);
if (lastModified != modified) {
this.map.put(resource, new Long(modified));
modifiedResources.add(resource.getDescription());
retVal = true;
}
} else {
this.map.put(resource, new Long(modified));
}
} catch (IOException var8) {
RefreshableSqlMapClientFactoryBean.logger.error("caught exception", var8);
}
if (retVal) {
RefreshableSqlMapClientFactoryBean.logger.info("modified files : " + modifiedResources);
}
return retVal;
}
};
this.timer = new Timer(true);
this.resetInterval();
List mappingLocationList = this.extractMappingLocations(this.configLocations);
if (this.mappingLocations != null) {
mappingLocationList.addAll(Arrays.asList(this.mappingLocations));
}
this.mappingLocations = (Resource[])((Resource[])mappingLocationList.toArray(new Resource[0]));
}
private List extractMappingLocations(Resource[] configLocations) {
List mappingLocationList = new ArrayList();
SqlMapExtractingSqlMapConfigParser configParser = new SqlMapExtractingSqlMapConfigParser();
for(int i = 0; i < configLocations.length; ++i) {
try {
InputStream is = configLocations[i].getInputStream();
mappingLocationList.addAll(configParser.parse(is));
} catch (IOException var6) {
logger.warn("Failed to parse config resource: " + configLocations[i], var6.getCause());
}
}
return mappingLocationList;
}
private Object getParentObject() {
this.r.lock();
SqlMapClient var1;
try {
var1 = super.getObject();
} finally {
this.r.unlock();
}
return var1;
}
public SqlMapClient getObject() {
return this.proxy;
}
public Class getObjectType() {
return this.proxy != null ? this.proxy.getClass() : SqlMapClient.class;
}
public boolean isSingleton() {
return true;
}
public void setCheckInterval(int ms) {
this.interval = ms;
if (this.timer != null) {
this.resetInterval();
}
}
private void resetInterval() {
if (this.running) {
this.timer.cancel();
this.running = false;
}
if (this.interval > 0) {
this.timer.schedule(this.task, 0L, (long)this.interval);
this.running = true;
}
}
public void destroy() throws Exception {
this.timer.cancel();
}
}
3-1. SqlMapClientRefreshable (Interface 만들기)
package com.jy.core;
public interface SqlMapClientRefreshable {
void refresh() throws Exception;
void setCheckInterval(int ms);
}
3-2. SqlMapExtractingSqlmapConfigParser.java 클래스 만들기
package com.jy.core;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.w3c.dom.Node;
import com.ibatis.common.xml.Nodelet;
import com.ibatis.common.xml.NodeletParser;
import com.ibatis.common.xml.NodeletUtils;
import com.ibatis.sqlmap.client.SqlMapException;
import com.ibatis.sqlmap.engine.builder.xml.SqlMapClasspathEntityResolver;
//import com.ibatis.sqlmap.engine.builder.xml.XmlParserState;
@SuppressWarnings("unchecked")
public class SqlMapExtractingSqlMapConfigParser {
protected final NodeletParser parser = new NodeletParser();
// private XmlParserState state = new XmlParserState();
private List sqlMapList = new ArrayList();
private ResourceLoader resourceLoader = new DefaultResourceLoader();
public SqlMapExtractingSqlMapConfigParser() {
parser.setValidation(true);
parser.setEntityResolver(new SqlMapClasspathEntityResolver());
addSqlMapNodelets();
}
public List parse(Reader reader) {
try {
parser.parse(reader);
return sqlMapList;
}
catch (Exception e) {
throw new RuntimeException("Error occurred. Cause: " + e, e);
}
}
public List parse(InputStream inputStream) {
try {
parser.parse(inputStream);
return sqlMapList;
}
catch (Exception e) {
throw new RuntimeException("Error occurred. Cause: " + e, e);
}
}
protected void addSqlMapNodelets() {
parser.addNodelet("/sqlMapConfig/sqlMap", new Nodelet() {
public void process(Node node) throws Exception {
// state.getConfig().getErrorContext().setActivity(
// "loading the SQL Map resource");
Properties attributes = NodeletUtils.parseAttributes(node,
// state.getGlobalProps());
null);
String resource = attributes.getProperty("resource");
String url = attributes.getProperty("url");
if (resource != null) {
sqlMapList.add(resourceLoader.getResource(resource));
}
else if (url != null) {
sqlMapList.add(resourceLoader.getResource(url));
}
else {
throw new SqlMapException(
"The <sqlMap> element requires either a resource or a url attribute.");
}
}
});
}
}
: 3번 RefreshableSqlMapClientFactoryBean.java 클래스 코드를 보면
3-1.SqlMapClientRefreshable(Interface)를 상속받고
3-2.SqlMapExtractingSqlmapConfigParser.java 객체를 생성함
그러므로 com.jy.core (본인패키지경로) 안에 클래스2, 인터페이스1 만들어줘야함
간단하게 서버내릴필요없이 새로고침 한번으로 변경된 쿼리를 적용할수있음
(단, 새로추가한 쿼리같은경우 target폴더에 올라가있지 않기에 관리되지않음
즉, 기존에 쿼리에서 수정한것들은 적용되며 새로 추가한 쿼리는 '서버내렸다 올려야됨')
↓ 복붙해서 사용하기 편하게 첨부파일 ↓
'Spring Framework' 카테고리의 다른 글
인텔리제이 - jsp에서 css, js 파일경로 참조하기 (0) | 2021.07.18 |
---|---|
Mybatis - RefreshSqlSession..(새로고침으로 쿼리수정된내용 바로적용) (0) | 2021.05.31 |
xml파일 사용시 주의점 (0) | 2021.05.06 |
@RequestBody와 @ResponseBody의 사용 (0) | 2021.03.04 |
리팩토링이란? (0) | 2021.03.02 |