본문 바로가기
Back-end/JAVA

[JAVA] Mapper를 만들어보자 (2) - PreparedStatement

by 허도치 2020. 11. 18.
이전글

2020/11/18 - [Back-end/JAVA] - [JAVA] Mapper를 만들어보자 (1) - XML읽기

 

 

 

서론

  이전 시간에 XML파일에서 특정 SQL을 읽어오는 클래스를 작성해보았다. 일반적인 경우 읽어온 SQL을 그대로 사용하면 되지만, PreparedStatement를 사용하려면 변수를 입력받고 치환해주는 과정이 필요하다. 그래서, 이번 포스트에서는 SQL에 작성된 #{변수명}, ${변수명} 을 치환하는 방법에 대해서 다루어 보려고 한다.  

 

 

 

참조
1. DBManager.java

2020/11/15 - [Back-end/JAVA] - [JAVA] SQLite 무작정 시작하기 (1) - DATABASE 연결/해제

 

 

 

소스코드
1. MapperService.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
package com.dochi.db.ex.maaper;
 
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
 
import com.dochi.quartz.crawl_db.db.DBManager;
 
// 변환된 SQL과 DATA를 저장할 클래스
class SQLInfo {
    private String sql = null;
    private List<Object> data = null;
    
    public SQLInfo(String sql, List<Object> data) {
        this.sql = sql;
        this.data = data;
    }
    public String getSql() {
        return sql;
    }
    public void setSql(String sql) {
        this.sql = sql;
    }
    public List<Object> getData() {
        return data;
    }
    public void setData(List<Object> data) {
        this.data = data;
    }
}
 
public class MapperService extends DBManager {
    
    // 상수설정
    //   - Mapper 파일명
    private final String XML_FILE_NAME = "src/main/java/com/dochi/db/ex/maaper/MyMapper.xml";
 
    // 변수설정
    //   - Mapper 변수
    private SAXBuilder builder = new SAXBuilder();
    private Document document = null;
    private Mapper root = null;
    
    // 생성자
    //   - Mapper 객체 생성
    public MapperService() {
        try {
            
            document = builder.build(XML_FILE_NAME);
            root = new Mapper(document.getRootElement());
            
        } catch (JDOMException | IOException e) {
            e.printStackTrace();
        }
    }
    
    // 특정 키워드를 찾아서 PreparedStatement로 변환해주는 함수
    //   - ${변수명} => Integer 
    //   - #{변수명} => String
    public SQLInfo parsing(String sql, Map<String, Object> paramMap) {
        List<Object> data = new ArrayList<Object>();
        
        // 나중에 패턴이 더 추가될 수도 있으므로 List에 담았음
        List<Pattern> patterns = new ArrayList<Pattern>();
        patterns.add(Pattern.compile("((#|\\$)\\{([a-zA-Z_0-9]+)\\})", Pattern.DOTALL));
        
        Iterator<Pattern> iter = patterns.iterator();
        Matcher matcher = null;
        
        while( iter.hasNext() ) {
            matcher = iter.next().matcher(sql);
            
            while( matcher.find() ) {
                String type = matcher.group(2);
                String source = matcher.group(1);
                String targetKey = matcher.group(3);
                
                if( paramMap.containsKey(targetKey) ) {
                    // SQL에서 탐색된 변수는 '?'로 치환
                    sql = sql.replace(source, "?");
                    
                    data.add(paramMap.get(targetKey));
                }
            }
        }
        
        return new SQLInfo(sql, data);
    }
    
    // 현재일시를 조회하는 함수
    public Map<String, Object> selectDateTime(Map<String, Object> paramMap) {
        // 상수설정
        //   - 현재 실행중인 함수의 이름을 저장한 변수
        final String METHOD_NAME = Thread.currentThread().getStackTrace()[1].getMethodName();
        
        //   - XML에서 현재 함수명의 SQL을 조회하여 저장하 변수
        final String SQL = this.root.getSQL(METHOD_NAME);
 
        System.out.println"==== Before SQL" );
        System.out.println( SQL );
        
        // 변수설정
        //   - PreparedStatement SQL로 치환
        SQLInfo info = parsing(SQL, paramMap);
        
        System.out.println"==== After SQL" );
        System.out.println( info.getSql() );
        System.out.println( info.getData() );
        
        // 나중에 SQL이 실행된 결과를 반환
        return null;
    }
 
    // 현재일자를 조회하는 함수
    public Map<String, Object> selectDate(Map<String, Object> paramMap) {
        // 상수설정
        //   - 현재 실행중인 함수의 이름을 저장한 변수
        final String METHOD_NAME = Thread.currentThread().getStackTrace()[1].getMethodName();
        
        //   - XML에서 현재 함수명의 SQL을 조회하여 저장하 변수
        final String SQL = this.root.getSQL(METHOD_NAME);
 
        System.out.println"==== Before SQL" );
        System.out.println( SQL );
        
        // 변수설정
        //   - PreparedStatement SQL로 치환
        SQLInfo info = parsing(SQL, paramMap);
        
        System.out.println"==== After SQL" );
        System.out.println( info.getSql() );
        System.out.println( info.getData() );
        
        // 나중에 SQL이 실행된 결과를 반환
        return null;
    }
    
    public static void main(String[] args) {
        MapperService service = new MapperService();
        
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("testValue"10);
        
        service.selectDateTime(paramMap);
    }
}
cs

 

 

 

실행결과

[ MapperService 실행 결과 ]

 

 

 

마치며

  지금까지 XML파일에서 SQL을 읽어 PreparedStatement SQL로 변환시켜보았다. 생각보다 간단하게 구현할 수 있었다. 다음 포스트에서는  SQLite와 연결해서 실제로 사용해보도록 하겠다.

 

댓글