package com.primeton.dsp.upgrader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.ParseSettings;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 为了升级元数据,兼容 hibernate 的 API
*
*
*
* Created by zhaopx.
* User: zhaopx
* Date: 2020/6/3
* Time: 11:12
*
*
*
* @author zhaopx
*/
@Slf4j
public class DAOServiceImpl {
/**
* SQL #var# 类型的替换
*/
public static final Pattern SQL_VAR_PATTERN = Pattern.compile("(#(\\w|\\.|-|_)+#)");
public final JdbcTemplate jdbcTemplate;
/**
* SQL statement 缓存
*/
private final static Map sqlCache = new HashMap<>();
public DAOServiceImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
if (sqlCache.isEmpty()) {
// 未初始化,则初始化一次
try {
init(jdbcTemplate);
} catch (Exception e) {
throw new IllegalStateException("无法加载 SQL 声明文件。", e);
}
}
}
/**
* 注意:静态,同步方法
*
* @param jdbcTemplate
* @throws Exception
*/
private synchronized static void init(JdbcTemplate jdbcTemplate) throws Exception {
if (!sqlCache.isEmpty()) {
return;
}
Connection connection = jdbcTemplate.getDataSource().getConnection();
try {
DatabaseMetaData metaData = connection.getMetaData();
String productName = metaData.getDatabaseProductName();
log.info("database product name: {}", productName);
String sqlXmlPath = String.format("META-INF/sqlxml/%s/upgradeSql.xml", Optional.ofNullable(productName).orElse("mysql").toLowerCase());
ClassPathResource pathResource = new ClassPathResource(sqlXmlPath);
try (InputStream in = pathResource.getInputStream()) {
String content = IOUtils.toString(in, StandardCharsets.UTF_8);
sqlCache.putAll(readProperties(content));
}
log.info("load sql xml file: {} success.", sqlXmlPath);
} finally {
try {
connection.close();
} catch (Exception e) {
}
}
}
protected static Map readProperties(String xml) {
Map prop = new HashMap<>();
// 保持 properties 中的配置是大小写区分的,否则全是小写的
Parser parser = Parser.xmlParser();
// 大小写敏感
parser.settings(ParseSettings.preserveCase);
Document doc = Jsoup.parse(xml, "http://www.primeton.com/dsp", parser);
Elements statements = doc.select("statement");
if (statements.size() > 0) {
for (Element statement : statements) {
prop.put(statement.attr("id"), statement.text());
}
}
return prop;
}
/**
* 获取一个无须替换参数的 SQL
*
* @param name
* @return
*/
public String getSQL(String name) {
return getSQL(name, new HashMap());
}
/**
* 获取 SQL, 替换模板
*
* @param name
* @param params
* @return
*/
public String getSQL(String name, Map params) {
params = Optional.ofNullable(params).orElse(new HashMap());
String sql = sqlCache.get(name);
if (params.isEmpty()) {
return sql;
}
Map varParam = new HashMap<>(params.size());
for (Map.Entry entry : params.entrySet()) {
varParam.put(entry.getKey(), "?");
}
return getFullString(sql, varParam);
}
/**
* 根据 String 的 #field# 按照 map 中的参数替换,组成一个新的 SQL 返回。
*
* @param sql 支持 #field# 的变量替换
* @param params 参数,如果没有参数则会原路返回
* @return 返回新的变量替换的 SQL
*/
public static String getFullString(String sql, Map params) {
Matcher matcher = SQL_VAR_PATTERN.matcher(sql);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String group = matcher.group();
String field = StringUtils.trim(group.substring(1, group.length() - 1));
String val = params.get(field);
if (val == null) {
continue;
}
matcher.appendReplacement(sb, val);
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* 执行查询 SQL 无参数
*
* @param sql
* @return
*/
public List