package org.voovan.tools;
import org.voovan.db.CallType;
import org.voovan.tools.json.JSON;
import org.voovan.tools.log.Logger;
import org.voovan.tools.reflect.TReflect;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
import java.util.Map.Entry;
/**
* SQL处理帮助类
*
* 注意所有的时间都用TDateTime.STANDER_DATETIME_TEMPLATE的格式
* @author helyho
*
* Voovan Framework.
* WebSite: https://github.com/helyho/Voovan
* Licence: Apache v2 License
*/
public class TSQL {
/**
* 从 SQL 字符串中,取 SQL 参数表
* @param sqlStr 原始 sql 字符串 (select * from table where x=::x and y=::y)
* @return sql 参数对照表 ([:x,:y])
*/
public static List<String> getSqlParams(String sqlStr){
String[] params = TString.searchByRegex(sqlStr, "::[^,\\s\\)]+");
ArrayList<String> sqlParams = new ArrayList<String>();
for(String param : params){
sqlParams.add(param);
}
return sqlParams;
}
/**
* 转换preparedStatement对象为可用的 sql 字符串(参数用?表示)
* @param sqlStr 原始 sql 字符串 (select * from table where x=:x and y=::y)
* @return 将所有的:引导的参数转换成? (select * from table where x=? and y=?)
*/
public static String preparedSql(String sqlStr){
return sqlStr.replaceAll("::[^,\\s\\)]+", "?");
}
/**
* 给preparedStatement对象设置参数
*
* @param preparedStatement preparedStatement对象
* @param sqlParams sql 参数表
* @param params 参数键值 Map
* @throws SQLException SQL 异常
*/
public static void setPreparedParams(PreparedStatement preparedStatement,List<String> sqlParams,Map<String, Object> params) throws SQLException{
for(int i=0;i<sqlParams.size();i++){
String paramName = sqlParams.get(i);
//去掉前面::号
paramName = paramName.substring(2,paramName.length());
Object data = params.get(paramName);
if(TReflect.isBasicType(data.getClass())) {
preparedStatement.setObject(i + 1, params.get(paramName));
}else{
//复杂对象类型,无法直接保存进数据库,进行 JSON 转换后保存
preparedStatement.setObject(i + 1, JSON.toJSON(params.get(paramName)));
}
if(Logger.isLogLevel("DEBUG")) {
Logger.simple("[SQL_Parameter]: " + sqlParams.get(i) + " = " + params.get(paramName));
}
}
}
/**
* 创建PreparedStatement
* @param conn 数据库连接
* @param sqlStr sql 自负穿
* @param params Map 参数
* @return PreparedStatement 对象
* @throws SQLException SQL 异常
*/
public static PreparedStatement createPreparedStatement(Connection conn,String sqlStr,Map<String, Object> params) throws SQLException{
//获取参数列表
List<String> sqlParams = TSQL.getSqlParams(sqlStr);
//将没有提供查询参数的条件移除
sqlStr = TSQL.removeEmptyCondiction(sqlStr,sqlParams,params);
Logger.debug("[SQL_Executed]: " + sqlStr);
//获取preparedStatement可用的 SQL
String preparedSql = TSQL.preparedSql(sqlStr);
PreparedStatement preparedStatement = (PreparedStatement) conn.prepareStatement(preparedSql);
//如果params为空,则新建一个
if(params==null){
params = new Hashtable<String, Object>();
}
//为preparedStatement参数填充
TSQL.setPreparedParams(preparedStatement,sqlParams,params);
return preparedStatement;
}
/**
* 创建PreparedStatement
* @param conn 数据库连接
* @param sqlStr sql 自负穿
* @param params Map 参数
* @param callTypes 调用参数类型
* @return PreparedStatement 对象
* @throws SQLException SQL 异常
*/
public static CallableStatement createCallableStatement(Connection conn,String sqlStr,Map<String, Object> params,CallType[] callTypes) throws SQLException{
Logger.debug("[SQL_Executed]: " + sqlStr);
//获取参数列表
List<String> sqlParams = TSQL.getSqlParams(sqlStr);
//获取preparedStatement可用的 SQL
String preparedSql = TSQL.preparedSql(sqlStr);
//定义 jdbc statement 对象
CallableStatement callableStatement = (CallableStatement) conn.prepareCall(preparedSql);
//如果params为空,则新建一个
if(params==null){
params = new Hashtable<String, Object>();
}
//callableStatement参数填充
TSQL.setPreparedParams(callableStatement,sqlParams,params);
//根据存储过程参数定义,注册 OUT 参数
ParameterMetaData parameterMetaData = callableStatement.getParameterMetaData();
for(int i=0;i<parameterMetaData.getParameterCount();i++){
int paramMode = parameterMetaData.getParameterMode(i+1);
if(paramMode == ParameterMetaData.parameterModeOut || paramMode == ParameterMetaData.parameterModeInOut) {
callableStatement.registerOutParameter(i + 1, parameterMetaData.getParameterType(i + 1));
}
}
return callableStatement;
}
/**
* 解析存储过程结果集
* @param callableStatement callableStatement对象
* @return 解析后的存储过程结果集
* @throws SQLException SQL 异常
*/
public static List<Object> getCallableStatementResult(CallableStatement callableStatement) throws SQLException{
ArrayList<Object> result = new ArrayList<Object>();
ParameterMetaData parameterMetaData = callableStatement.getParameterMetaData();
//遍历参数信息
for(int i=0;i<parameterMetaData.getParameterCount();i++){
int paramMode = parameterMetaData.getParameterMode(i+1);
//如果是带有 out 属性的参数,则对其进行取值操作
if(paramMode == ParameterMetaData.parameterModeOut || paramMode == ParameterMetaData.parameterModeInOut){
//取值方法名
String methodName = getDataMethod(parameterMetaData.getParameterType(i+1));
Object value;
try {
//获得取值方法参数参数是 int 类型的对应方法
Method method = TReflect.findMethod(CallableStatement.class,methodName,new Class[]{int.class});
//反射调用方法
value = TReflect.invokeMethod(callableStatement, method,i+1);
result.add(value);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
return result;
}
/**
* 使用数组参数的属性组装SQL
* @param sqlStr SQL 字符串
* @param args 拼装参数
* @return 拼装后的 SQL
*/
public static String assembleSQLWithArray(String sqlStr,Object[] args){
Map<String,Object> argMap = TObject.arrayToMap(args);
return assembleSQLWithMap(sqlStr,argMap);
}
/**
* 使用argObjectj参数的属性组装SQL
* @param sqlStr SQL 字符串
* @param argObjectj 拼装对象
* @return 拼装候的SQL
* @throws ReflectiveOperationException 反射异常
*/
public static String assembleSQLWithObject(String sqlStr,Object argObjectj) throws ReflectiveOperationException{
//获取对象 (属性-值)Map
Map<String,Object> argMap = TReflect.getMapfromObject(argObjectj);
return assembleSQLWithMap(sqlStr,argMap);
}
/**
* 使用argMap参数的KV组装SQL
* SQL字符串中以:开始的相同字符串将被替换
* @param sqlStr SQL 字符串
* @param argMap 拼装的 Map
* @return 拼装后的字符串
*/
public static String assembleSQLWithMap(String sqlStr,Map<String ,Object> argMap) {
for(Entry<String,Object> arg : argMap.entrySet())
{
sqlStr = sqlStr.replaceAll(":"+arg.getKey(),getSQLString(argMap.get(arg.getKey())));
}
return sqlStr;
}
/**
* 包装resultSet中单行记录成Map
* @param resultset 查询结果集
* @return 转后的 Map 对象
* @throws SQLException SQL 异常
* @throws ReflectiveOperationException 反射异常
*/
public static Map<String, Object> getOneRowWithMap(ResultSet resultset)
throws SQLException, ReflectiveOperationException {
HashMap<String, Object> resultMap = new HashMap<String,Object>();
HashMap<String,Integer> columns = new HashMap<String,Integer>();
//遍历结果集字段信息
int columnCount = resultset.getMetaData().getColumnCount();
for(int i=1;i<=columnCount;i++){
columns.put(resultset.getMetaData().getColumnLabel(i),resultset.getMetaData().getColumnType(i));
}
//组装Map
for(Entry<String, Integer> columnEntry : columns.entrySet())
{
String methodName =getDataMethod(columnEntry.getValue());
Object value = TReflect.invokeMethod(resultset, methodName, columnEntry.getKey());
resultMap.put(columnEntry.getKey(), value);
}
return resultMap;
}
/**
* 包装resultSet中单行记录成指定对象
* @param clazz 类对象
* @param resultset 查询结果集
* @return 转换后的对象
* @throws ReflectiveOperationException 反射异常
* @throws SQLException SQL 异常
* @throws ParseException 解析异常
*/
public static Object getOneRowWithObject(Class<?> clazz,ResultSet resultset)
throws SQLException, ReflectiveOperationException, ParseException {
Map<String,Object>rowMap = getOneRowWithMap(resultset);
//对象转换时,模糊匹配属性,去除掉所有的
HashMap<String,Object> newMap = new HashMap<String,Object>();
for(Entry<String,Object> entry : rowMap.entrySet()){
String key = entry.getKey().replaceAll("[^a-z|A-Z|0-9]","");
newMap.put(key,entry.getValue());
}
rowMap.clear();
return TReflect.getObjectFromMap(clazz, newMap,true);
}
/**
* 包装resultSet中所有记录成List,单行元素为Map
* @param resultSet 查询结果集
* @return 转后的 List[Map]
* @throws ReflectiveOperationException 反射异常
* @throws SQLException SQL 异常
*/
public static List<Map<String,Object>> getAllRowWithMapList(ResultSet resultSet)
throws SQLException, ReflectiveOperationException {
List<Map<String,Object>> resultList = new ArrayList<Map<String,Object>>();
while(resultSet!=null && resultSet.next()){
resultList.add(getOneRowWithMap(resultSet));
}
return resultList;
}
/**
* 包装resultSet中所有记录成List,单行元素为指定对象
* @param clazz 类
* @param resultSet 查询结果集
* @return 转换候的对象结合
* @throws ParseException 解析异常
* @throws ReflectiveOperationException 反射异常
* @throws SQLException SQL 异常
*/
public static List<Object> getAllRowWithObjectList(Class<?> clazz,ResultSet resultSet)
throws SQLException, ReflectiveOperationException, ParseException {
List<Object> resultList = new ArrayList<Object>();
while(resultSet!=null && resultSet.next()){
resultList.add(getOneRowWithObject(clazz,resultSet));
}
return resultList;
}
/**
* 将SQL 语句中,没有提供查询参数的条件移除
* @param sqlText SQL 字符串
* @param sqlParams sql 参数名集合
* @param params 参数集合
* @return 转换后的字符串
*/
public static String removeEmptyCondiction(String sqlText,List<String> sqlParams,Map<String, Object> params){
//如果params为空,则新建一个
if(params==null){
params = new Hashtable<String, Object>();
}
//转换存在参数的变量从::paramName 到 ``paramName
for(String paramName : params.keySet()){
sqlText = sqlText.replace("::"+paramName,"``"+paramName);
}
String sqlRegx = "((\\swhere\\s)|(\\sand\\s)|(\\sor\\s))[\\S\\s]+?(?=(\\swhere\\s)|(\\s\\)\\s)|(\\sand\\s)|(\\sor\\s)|(\\sorder\\s)|(\\shaving\\s)|$)";
String[] sqlCondiction = TString.searchByRegex(sqlText,sqlRegx);
for(String condiction : sqlCondiction){
String[] condictions = TString.searchByRegex(condiction,"::[^,\\s\\)]+");
if(condictions.length>0){
if(condiction.trim().toLowerCase().startsWith("where")){
sqlText = sqlText.replace(condiction.trim(),"where 1=1");
}else{
sqlText = sqlText.replace(condiction.trim(),"");
}
sqlParams.remove(condictions[0]);
}
}
//转换存在参数的变量从``paramName 到 ::paramName
return sqlText.replace("``","::");
}
/**
* 获取解析后的 SQL 的条件
* @param sqlText SQL 字符串
* @return 解析的 SQL 查询条件
*/
public static List<String[]> parseSQLCondiction(String sqlText) {
ArrayList<String[]> condictionList = new ArrayList<String[]>();
sqlText = sqlText.toLowerCase();
String sqlRegx = "((\\swhere\\s)|(\\sand\\s)|(\\sor\\s))[\\S\\s]+?(?=(\\swhere\\s)|(\\s\\)\\s)|(\\sand\\s)|(\\sor\\s)|(\\sorder\\s)|(\\shaving\\s)|$)";
String[] sqlCondiction = TString.searchByRegex(sqlText,sqlRegx);
for(String condiction : sqlCondiction){
condiction = condiction.trim();
String concateMethod = condiction.substring(0,condiction.indexOf(" ")+1).trim();
condiction = condiction.substring(condiction.indexOf(" ")+1,condiction.length()).trim();
String operatorChar = TString.searchByRegex(condiction, "(\\slike\\s*)|(\\sin\\s*)|(>=)|(<=)|[=<>]")[0].trim();
String[] condictionArr = condiction.split("(\\slike\\s*)|(\\sin\\s*)|(>=)|(<=)|[=<>]");
condictionArr[0] = condictionArr[0].trim();
condictionArr[1] = condictionArr[1].trim();
if(condictionArr[0].trim().indexOf(".")>1){
condictionArr[0] = condictionArr[0].split("\\.")[1];
condictionArr[0] = condictionArr[0].substring(condictionArr[0].lastIndexOf(" ")+1);
}
if(condictionArr.length>1){
if((condictionArr[1].trim().startsWith("'") && condictionArr[1].trim().endsWith("'")) ||
(condictionArr[1].trim().startsWith("(") && condictionArr[1].trim().endsWith(")"))
){
condictionArr[1] = condictionArr[1].substring(1,condictionArr[1].length()-1);
}
if(operatorChar.contains("in")){
condictionArr[1] = condictionArr[1].replace("'", "");
}
//System.out.println("操作符: "+concateMethod+" \t查询字段: "+condictionArr[0]+" \t查询关系: "+operatorChar+" \t查询值: "+condictionArr[1]);
condictionList.add(new String[]{concateMethod, condictionArr[0], operatorChar, condictionArr[1]});
}else{
Logger.error("Parse SQL condiction error");
}
}
return condictionList;
}
/**
* SQL的参数,将 JAVA 的类型转换成可在SQL中进行封装的字符串
* 例如:String类型的对象转换成 'chs'
* @param argObj 转换前的对象
* @return 封装后的字符串
*/
public static String getSQLString(Object argObj)
{
//处理List变成SQL语法的in操作字符串,包括两端的括号“()”
if(argObj instanceof List)
{
Object[] objects =((List<?>)argObj).toArray();
StringBuilder listValueStr= new StringBuilder("(");
for(Object obj : objects)
{
String sqlValue = getSQLString(obj);
if(sqlValue!=null) {
listValueStr.append(sqlValue);
listValueStr.append(",");
}
}
return TString.removeSuffix(listValueStr.toString())+")";
}
//处理String
else if(argObj instanceof String){
return "\'"+argObj.toString()+"\'";
}
//处理Boolean
else if(argObj instanceof Boolean){
if((Boolean)argObj)
return "true";
else
return "false";
}
//处理Date
else if(argObj instanceof Date){
SimpleDateFormat dateFormat = new SimpleDateFormat(TDateTime.STANDER_DATETIME_TEMPLATE);
return "'"+dateFormat.format(argObj)+"'";
}
//处理其他类型,全部转换成String
else
{
return argObj.toString();
}
}
/**
* 根据 SQL 类型判断 Result 该使用什么方法取值
* @param databaseType 数据库中的数据类型
* @return 方法名
*/
public static String getDataMethod(int databaseType){
switch(databaseType){
case java.sql.Types.CHAR :
return "getString";
case java.sql.Types.VARCHAR :
return "getString";
case java.sql.Types.LONGVARCHAR :
return "getString";
case java.sql.Types.NCHAR :
return "getString";
case java.sql.Types.LONGNVARCHAR :
return "getString";
case java.sql.Types.NUMERIC :
return "getBigDecimal";
case java.sql.Types.DECIMAL :
return "getBigDecimal";
case java.sql.Types.BIT :
return "getBoolean";
case java.sql.Types.BOOLEAN :
return "getBoolean";
case java.sql.Types.TINYINT :
return "getByte";
case java.sql.Types.SMALLINT :
return "getShort";
case java.sql.Types.INTEGER :
return "getInt";
case java.sql.Types.BIGINT :
return "getLong";
case java.sql.Types.REAL :
return "getFloat";
case java.sql.Types.FLOAT :
return "getFloat";
case java.sql.Types.DOUBLE :
return "getDouble";
case java.sql.Types.BINARY :
return "getBytes";
case java.sql.Types.VARBINARY :
return "getBytes";
case java.sql.Types.LONGVARBINARY :
return "getBytes";
case java.sql.Types.DATE :
return "getDate";
case java.sql.Types.TIME :
return "getTime";
case java.sql.Types.TIMESTAMP :
return "getTimestamp";
case java.sql.Types.CLOB :
return "getClob";
case java.sql.Types.BLOB :
return "getBlob";
case java.sql.Types.ARRAY :
return "getArray";
default:
return "getString";
}
}
/**
* 根据 JAVA 类型判断该使用什么 SQL 数据类型
* @param obj 对象
* @return 数据库中的数据类型
*/
public static int getSqlTypes(Object obj){
Class<?> objectClass = obj.getClass();
if(char.class == objectClass){
return java.sql.Types.CHAR;
}else if(String.class == objectClass){
return java.sql.Types.VARCHAR ;
}else if(BigDecimal.class == objectClass){
return java.sql.Types.NUMERIC;
}else if(Boolean.class == objectClass){
return java.sql.Types.BIT;
}else if(Byte.class == objectClass){
return java.sql.Types.TINYINT;
}else if(Short.class == objectClass){
return java.sql.Types.SMALLINT;
}else if(Integer.class == objectClass){
return java.sql.Types.INTEGER;
}else if(Long.class == objectClass){
return java.sql.Types.BIGINT;
}else if(Float.class == objectClass){
return java.sql.Types.FLOAT;
}else if(Double.class == objectClass){
return java.sql.Types.DOUBLE;
}else if(Byte[].class == objectClass){
return java.sql.Types.BINARY;
}else if(Date.class == objectClass){
return java.sql.Types.DATE;
}else if(Time.class == objectClass){
return java.sql.Types.TIME;
}else if(Timestamp.class == objectClass){
return java.sql.Types.TIMESTAMP;
}else if(Clob.class == objectClass){
return java.sql.Types.CLOB;
}else if(Blob.class == objectClass){
return java.sql.Types.BLOB;
}else if(Object[].class == objectClass){
return java.sql.Types.ARRAY;
}
return 0;
}
}