1. DB 테이블
CREATE TABLE SAVED_QUERY (
QUERY_ID NUMBER PRIMARY KEY,
QUERY_NAME VARCHAR2(100),
SQL_TEXT CLOB,
DESCRIPTION VARCHAR2(255)
);
2. 쿼리 저장 화면 - saveQuery.jsp
<form action="saveQuery.do" method="post">
쿼리명: <input type="text" name="queryName"><br>
설명: <input type="text" name="description"><br>
SQL 입력:<br>
<textarea name="sqlText" rows="10" cols="80"></textarea><br>
<input type="submit" value="저장">
</form>
<c:if test="${not empty error}">
<div style="color:red;">${error}</div>
</c:if>
3. 쿼리 저장 결과 - saveSuccess.j
<h3>쿼리 저장 완료</h3>
<p>쿼리 ID: ${queryId}</p>
<h4>예상 컬럼</h4>
<table border="1">
<tr>
<c:forEach var="col" items="${columns}">
<th>${col}</th>
</c:forEach>
</tr>
</table>
<a href="runSavedQuery.do?queryId=${queryId}">쿼리 실행</a>
4. 쿼리 실행 화면 - runSavedQuery.jsp
<form action="runSavedQuery.do" method="post">
<select name="queryId" onchange="this.form.submit();">
<option value="">쿼리 선택</option>
<c:forEach var="q" items="${savedQueries}">
<option value="${q.queryId}" ${q.queryId == selectedId ? 'selected' : ''}>${q.queryName}</option>
</c:forEach>
</select>
</form>
<c:if test="${not empty paramList}">
<form action="runSavedQuery.do" method="post">
<input type="hidden" name="queryId" value="${selectedId}">
<c:forEach var="param" items="${paramList}">
${param}: <input type="text" name="params[${param}]"><br>
</c:forEach>
<input type="submit" value="실행">
</form>
</c:if>
<c:if test="${not empty resultList}">
<table border="1">
<tr>
<c:forEach var="col" items="${columnNames}">
<th>${col}</th>
</c:forEach>
</tr>
<c:forEach var="row" items="${resultList}">
<tr>
<c:forEach var="col" items="${columnNames}">
<td>${row[col]}</td>
</c:forEach>
</tr>
</c:forEach>
</table>
</c:if>
<c:if test="${not empty error}">
<div style="color:red;">${error}</div>
</c:if>
5. Controller - QueryController.java
@Controller
public class QueryController {
@Autowired
private QueryService queryService;
@RequestMapping("/saveQuery.do")
public String saveQuery(
@RequestParam String queryName,
@RequestParam String sqlText,
@RequestParam String description,
HttpSession session,
Model model
) {
String role = (String) session.getAttribute("userRole");
Map<String, Object> sessionParams = Map.of(
"userId", session.getAttribute("userId"),
"companyId", session.getAttribute("companyId")
);
try {
int queryId = queryService.saveQuery(queryName, sqlText, description, role);
List<String> columns = queryService.extractColumns(sqlText, sessionParams);
model.addAttribute("queryId", queryId);
model.addAttribute("columns", columns);
return "saveSuccess";
} catch (IllegalArgumentException e) {
model.addAttribute("error", e.getMessage());
return "saveQuery";
}
}
@RequestMapping("/runSavedQuery.do")
public String runQuery(
@RequestParam int queryId,
@RequestParam(required = false) Map<String, String> params,
HttpSession session,
Model model
) {
String role = (String) session.getAttribute("userRole");
String sql = queryService.getSqlById(queryId);
List<String> hashParams = queryService.extractHashParams(sql);
List<String> dollarParams = queryService.extractDollarParams(sql);
if (params == null || params.isEmpty()) {
model.addAttribute("paramList", hashParams);
model.addAttribute("savedQueries", queryService.getAllSavedQueries());
model.addAttribute("selectedId", queryId);
return "runSavedQuery";
}
Map<String, Object> mergedParams = new HashMap<>(params);
for (String key : dollarParams) {
mergedParams.put(key, session.getAttribute(key));
}
try {
List<Map<String, Object>> result = queryService.runDynamicQuery(sql, mergedParams, role);
model.addAttribute("resultList", result);
model.addAttribute("columnNames", result.isEmpty() ? List.of() : new ArrayList<>(result.get(0).keySet()));
} catch (IllegalArgumentException e) {
model.addAttribute("error", e.getMessage());
}
model.addAttribute("savedQueries", queryService.getAllSavedQueries());
model.addAttribute("selectedId", queryId);
return "runSavedQuery";
}
}
6. Service - QueryServiceImpl.jav
@Service
public class QueryServiceImpl implements QueryService {
@Autowired
private QueryDAO dao;
private static final List<String> FORBIDDEN_KEYWORDS = List.of(
"DROP", "DELETE", "TRUNCATE", "ALTER", "UPDATE"
);
private void validateSafeSql(String sql, String role) {
String upperSql = sql.toUpperCase();
for (String keyword : FORBIDDEN_KEYWORDS) {
if (upperSql.matches(".*\\b" + keyword + "\\b.*")) {
if (!"ROLE_ADMIN".equals(role)) {
throw new IllegalArgumentException("금지된 SQL 문법이 포함되어 있습니다: " + keyword);
}
}
}
}
@Override
public int saveQuery(String name, String sql, String desc, String role) {
validateSafeSql(sql, role);
return dao.insert(name, sql, desc);
}
@Override
public String getSqlById(int id) {
return dao.findById(id);
}
@Override
public List<Map<String, Object>> runDynamicQuery(String sql, Map<String, Object> params, String role) {
validateSafeSql(sql, role);
for (String key : extractDollarParams(sql)) {
sql = sql.replace("${" + key + "}", "'" + String.valueOf(params.getOrDefault(key, "")) + "'");
}
List<Object> paramList = new ArrayList<>();
Matcher m = Pattern.compile("#\\{(\\w+)}").matcher(sql);
while (m.find()) paramList.add(params.get(m.group(1)));
sql = sql.replaceAll("#\\{(\\w+)}", "?");
return dao.execute(sql, paramList.toArray());
}
@Override
public List<String> extractColumns(String sql, Map<String, Object> sessionParams) {
for (String key : extractDollarParams(sql)) {
sql = sql.replace("${" + key + "}", "'" + String.valueOf(sessionParams.getOrDefault(key, "")) + "'");
}
List<Object> dummy = new ArrayList<>();
Matcher m = Pattern.compile("#\\{(\\w+)}").matcher(sql);
while (m.find()) dummy.add("");
sql = sql.replaceAll("#\\{(\\w+)}", "?");
return dao.extractColumns("SELECT * FROM (" + sql + ") WHERE 1=0", dummy.toArray());
}
public List<String> extractHashParams(String sql) {
List<String> list = new ArrayList<>();
Matcher m = Pattern.compile("#\\{(\\w+)}").matcher(sql);
while (m.find()) list.add(m.group(1));
return list;
}
public List<String> extractDollarParams(String sql) {
List<String> list = new ArrayList<>();
Matcher m = Pattern.compile("\\$\\{(\\w+)}").matcher(sql);
while (m.find()) list.add(m.group(1));
return list;
}
public List<SavedQuery> getAllSavedQueries() {
return dao.findAll();
}
}
7. DAO - QueryDAO.java
@Repository
public class QueryDAO {
@Autowired
private JdbcTemplate jdbc;
public int insert(String name, String sql, String desc) {
return jdbc.update("INSERT INTO SAVED_QUERY (QUERY_ID, QUERY_NAME, SQL_TEXT, DESCRIPTION) VALUES (SEQ_QUERY.NEXTVAL, ?, ?, ?)",
name, sql, desc);
}
public String findById(int id) {
return jdbc.queryForObject("SELECT SQL_TEXT FROM SAVED_QUERY WHERE QUERY_ID = ?", String.class, id);
}
public List<Map<String, Object>> execute(String sql, Object[] params) {
return jdbc.queryForList(sql, params);
}
public List<String> extractColumns(String sql, Object[] params) {
return jdbc.query(sql, params, (ResultSet rs) -> {
ResultSetMetaData meta = rs.getMetaData();
List<String> cols = new ArrayList<>();
for (int i = 1; i <= meta.getColumnCount(); i++) {
cols.add(meta.getColumnName(i));
}
return cols;
});
}
public List<SavedQuery> findAll() {
return jdbc.query("SELECT QUERY_ID, QUERY_NAME FROM SAVED_QUERY",
(rs, rowNum) -> new SavedQuery(rs.getInt("QUERY_ID"), rs.getString("QUERY_NAME")));
}
}
'잡학사전' 카테고리의 다른 글
컴퓨터 원격 사용을 위한 pc 원격 부팅 설정 (asrock메인보드 바이오스 설정) (0) | 2023.02.17 |
---|---|
USB 및 외장하드 등 저장 매체 포맷 형식 (0) | 2023.02.15 |
부가가치세(부가세) 개요 및 계산 방법 (0) | 2023.01.27 |