개발 환경 : STS(3.9.5), spring(5.1.2), mybatis, openjdk1.8, mariaDB 등


sts에서 spring legacy project중에 spring mvc project를 생성한다.


pom.xml 사용 라이브러리 : 

- jstl : jsp에서 jstl 태그를 사용하기 위함

- mariadb-java-clien : mariaDB와 connnection을 맺기 위함

- mybatis-spring  : Spring에서 연동을 지원하는 mybatis

- mybatis              

- spring-jdbc : Spring에서 지원하는 JDBC

- commons-dbcp : 커넥션풀을 담당하는 Apache Commons DBCP

- commons-lang3 : 문자열 라이브러리를 apache commons lang으로 통일하기 위함

- log4jdbc-remix : mybatis 로그를 출력하기 위함

- lombok : @data, @slf4 어노테이션 등을 사용하기 위함

- springloaded : java 소스 수정 시 tomcat을 자동으로 reload해주기 위함(tomcat restart를 안해도 된다)


<pom.xml 소스>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>1.1.7</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency><dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.0</version>
</dependency>
<dependency>
    <groupId>org.lazyluke</groupId>
    <artifactId>log4jdbc-remix</artifactId>
    <version>0.2.7</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>springloaded</artifactId>
    <version>1.2.8.RELEASE</version>
</dependency>


위와 같이 셋팅 한 후에 mybatis 설정을 위해 config파일들을 추가해야 한다.

추가 경로(이클립스 경로) : src/main/webapp/WEB-INF/spring/

추가 경로 하위에 mybatis-config-base.xml와 mybatis-context.xml을 추가한다.


<mybatis-context.xml 소스 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<p><?xml version="1.0" encoding="UTF-8"?>
 
    <bean id="jdbcProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:META-INF/jdbc.properties" />
    </bean>
 
    <bean id="dataSourceSpied" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
     
    <bean id="dataSource" class="net.sf.log4jdbc.Log4jdbcProxyDataSource">
        <constructor-arg ref="dataSourceSpied" />
        <property name="logFormatter">
            <bean class="net.sf.log4jdbc.tools.Log4JdbcCustomFormatter">
                <property name="loggingType" value="MULTI_LINE" />
                <property name="sqlPrefix" value="SQL         :  "/>
            </bean>
        </property>
    </bean>
     
    <bean id="sqlSessionFactory" class="com.spring.test.util.RefreshableSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com.spring.test"/>
        <property name="configLocation" value="WEB-INF/spring/mybatis-config-base.xml" />
        <property value="classpath:com/spring/**/dao/mapper/*Mapper.xml" name="mapperLocations" />
    </bean>
     
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.spring.test" />
        <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
    </bean>
 
</beans>
</p>

기본적인 mybatis셋팅에서 dataSource를 셋팅하는 부분이 살짝 바뀌었다. 이는 mybatis 로그를 출력하기 위한 셋팅이다.

또한 sqlSessionFactory 부분에서 RefreshableSqlSessionFactoryBean.java를 참조하여 셋팅하였는데 mapper 수정 시 tomcat restart를 하지 않고 바로 적용되도록 하기 위함이다.


<RefreshableSqlSessionFactoryBean.java 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<p>package com.spring.test.util;
 
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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.core.io.Resource;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean {
 
    private static final Log log = LogFactory.getLog(RefreshableSqlSessionFactoryBean.class);
 
    private SqlSessionFactory proxy;
    private int interval = 500;
 
    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;
    }
 
    /**
     * @throws Exception
     */
    public void refresh() throws Exception {
        if (log.isInfoEnabled()) {
            log.info("refreshing sqlMapClient.");
        }
        w.lock();
        try {
            super.afterPropertiesSet();
 
        } finally {
            w.unlock();
        }
    }
 
    /**
     * 싱글톤 멤버로 SqlMapClient 원본 대신 프록시로 설정하도록 오버라이드.
     */
    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 {
                        // log.debug("method.getName() : " + method.getName());
                        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) {
                    if (log.isInfoEnabled()) {
                        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();
    }
}</p>


<mybatis-config-base.xml 소스코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<p><?xml version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 
<configuration>
    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="lazyLoadingEnabled" value="false"/>
        <setting name="multipleResultSetsEnabled" value="true"/>
        <setting name="useColumnLabel" value="true"/>
        <setting name="useGeneratedKeys" value="false"/>
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <setting name="defaultStatementTimeout" value="25000"/>
    </settings>
     
    <typeHandlers> <!-- java.sql.Timestamp 를 java.util.Date 형으로 반환 -->
        <typeHandler javaType="java.sql.Timestamp" handler="org.apache.ibatis.type.DateTypeHandler"/>
        <typeHandler javaType="java.sql.Time" handler="org.apache.ibatis.type.DateTypeHandler"/>
        <typeHandler javaType="java.sql.Date" handler="org.apache.ibatis.type.DateTypeHandler"/>
    </typeHandlers>
</configuration><br></p>

위의 소스는 mybatis 설정 정보 파일로 필요한 옵션을 추가해서 사용하면 되겠다.(mybaits.org 또는 구글링)에서 참조


위의 파일을 추가한 후 설정을 읽어오기 위해 root-context.xml파일을 수정한다.

<root-context.xml 소스 코드>

1
2
3
4
5
6
7
8
9
<p><?xml version="1.0" encoding="UTF-8"?>
     
    <!-- Root Context: defines shared resources visible to all other web components -->
    <import resource="mybatis-context.xml" />
</beans>
</p>


root-context.xml의 소스를 수정한 후 mybatis-context.xml에서 설정한 dataSource 정보를 읽어오기 위해 jdbc.properties 파일을 추가한다.

추가 경로 : src/main/resource/META-INF/

<jdbc.properties 소스 코드>

1
2
3
4
5
<p># Maria DB
jdbc.driverClassName=org.mariadb.jdbc.Driver
jdbc.username = javaboja
jdbc.password = javaboja</p>

마지막으로 mybatis 쿼리 로그를 출력하기 위해 log4j.xml을 수정하자.

<log4j.xml 소스 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p: %c - %m%n" />
        </layout>
    </appender>
     
    <!-- Application Loggers -->
    <logger name="com.spring.test">
        <level value="info" />
    </logger>
     <!-- Query Loggers -->
    <logger name="jdbc.sqlonly" additivity="false">
        <level value="INFO" />
        <appender-ref ref="console-infolog" />
    </logger>
    <logger name="jdbc.resultsettable" additivity="false">
        <level value="INFO" />
        <appender-ref ref="console" />
    </logger>
    <!-- 3rdparty Loggers -->
    <logger name="org.springframework.core">
        <level value="info" />
    </logger>
     
    <logger name="org.springframework.beans">
        <level value="info" />
    </logger>
     
    <logger name="org.springframework.context">
        <level value="info" />
    </logger>
 
    <logger name="org.springframework.web">
        <level value="info" />
    </logger>
 
    <!-- Root Logger -->
    <root>
        <priority value="warn" />
        <appender-ref ref="console" />
    </root>
     
</log4j:configuration>

위 소스 코드에서 각 패키지를 입력하는 부분은 자기 자신의 프로젝트 패키지 명에 맞게 수정해줘야 한다.

여기까지 진행했다면 셋팅은 끝났다. 이제 게시판을 만들어 보자.

'spring' 카테고리의 다른 글

spring quartz 스케쥴링 java config  (3) 2019.02.15
spring 스케쥴링 설정  (0) 2019.02.12
view에서 특정 함수 반복 실행 방지  (0) 2019.02.12
spring mybatis 게시판  (1) 2019.02.12

+ Recent posts