package org.crazycake.formSqlBuilder; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.persistence.Column; import org.crazycake.formSqlBuilder.model.QueryNode; import org.crazycake.formSqlBuilder.model.Rule; import org.crazycake.formSqlBuilder.model.Sort; import org.crazycake.formSqlBuilder.model.SqlAndParams; import org.crazycake.formSqlBuilder.model.enums.Operator; import org.crazycake.formSqlBuilder.utils.ReflectUtils; import org.crazycake.formSqlBuilder.utils.RuleMatchUtils; import org.crazycake.utils.CamelNameUtils; /** * hibernate query对象的工具类 * @author alex.yang * */ public class SqlGenerator { /** * 往sql上拼接排序条件 * @param hql * @throws NoSuchMethodException * @throws SecurityException */ public static String appendSort(String sql,Object form,List<Sort> sorts) throws SecurityException, NoSuchMethodException{ if(sorts == null){ return sql; } for (int i = 0; i < sorts.size(); i++) { if(i!=0){ sql += ","; }else{ sql += " ORDER BY "; } Sort sort = sorts.get(i); //将sort字段转成数据库列名 String getterName = "get" + CamelNameUtils.capitalize(sort.getSort()); Method getter = form.getClass().getMethod(getterName); String sortCol = ""; Column colAnno = getter.getAnnotation(Column.class); if(colAnno != null){ sortCol = colAnno.name(); }else{ sortCol = CamelNameUtils.camel2underscore(sort.getSort()); } sql += sortCol + " " + sort.getOrder().getSql(); } return sql; } /** * 设置查询范围 * 如果 page=-1 或者 rows=-1 都表示不限定范围 * @param query * @param page 从1开始 * @param rows */ public static String appendPage(String sql,int page,int rows){ if(rows == 0 || page == 0){ return sql; } if(rows == -1 || page == -1){ return sql; } sql = sql + " LIMIT " + ((page-1)*rows) + "," + rows; return sql; } /** * 使用criterionResult生成 Query对象 * @param session * @param cr * @return * @throws SQLException */ public static PreparedStatement generatePs(Connection conn,String sql,Object[] params) throws SQLException { //生成query对象 PreparedStatement ps = conn.prepareStatement(sql); //遍历values并设置值 for(int i=0; i<params.length; i++){ ps.setObject((i+1), params[i]); } return ps; } /** * 生成sql语句和参数列表 * @param form * @param ruleScheme * @param tableName * @return * @throws InvocationTargetException * @throws IllegalAccessException * @throws NoSuchMethodException * @throws IllegalArgumentException * @throws NoSuchFieldException * @throws SecurityException */ public SqlAndParams generateSqlAndParams(Object form, Map<String, Rule> ruleScheme,String tableName) throws IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchFieldException{ StringBuffer sql = new StringBuffer(); sql.append("SELECT * FROM " + tableName + " "); SqlAndParams sqlAndParams = generateQueryBody(form, ruleScheme, sql); return sqlAndParams; } /** * 生成count语句和参数列表 * @param form * @param ruleScheme * @param tableName * @return * @throws InvocationTargetException * @throws IllegalAccessException * @throws NoSuchMethodException * @throws IllegalArgumentException * @throws NoSuchFieldException * @throws SecurityException */ public SqlAndParams generateCountSqlAndParams(Object form, Map<String, Rule> ruleScheme,String tableName) throws IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchFieldException{ StringBuffer sql = new StringBuffer(); sql.append("SELECT count(1) FROM " + tableName + " "); SqlAndParams sqlAndParams = generateQueryBody(form, ruleScheme, sql); return sqlAndParams; } /** * 构建参数列表和sql * @param form * @param ruleScheme * @param sql * @return * @throws InvocationTargetException * @throws IllegalAccessException * @throws NoSuchMethodException * @throws IllegalArgumentException * @throws NoSuchFieldException * @throws SecurityException */ private SqlAndParams generateQueryBody(Object form, Map<String, Rule> ruleScheme, StringBuffer sql) throws IllegalArgumentException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchFieldException { List<Object> params = new ArrayList<Object>(); //pick field List<QueryNode> collectedResult = pickFieldWithRule(form, ruleScheme); //use collectedResult to generate sql and params int paramCounter = 0; for(QueryNode queryNode : collectedResult){ //if queryNode is a group if(queryNode.getMembers().size()>0){ if(paramCounter > 0){ sql.append(queryNode.getRel() + " "); }else{ sql.append("WHERE "); } sql.append("( "); List<QueryNode> memberNodes = queryNode.getMembers(); for(int i=0;i<memberNodes.size();i++){ QueryNode node = memberNodes.get(i); if(i != 0){ sql.append(node.getRel() + " "); } sql.append(generateSql(form, node)); addParams(params, node); } sql.append(") "); paramCounter++; }else{ //a single query node if(paramCounter > 0){ sql.append(queryNode.getRel() + " "); }else{ sql.append("WHERE "); } sql.append(generateSql(form, queryNode)); addParams(params, queryNode); paramCounter++; } } //create sqlAndParams SqlAndParams sqlAndParams = new SqlAndParams(); sqlAndParams.setParams(params.toArray()); sqlAndParams.setSql(sql.toString()); //return it! return sqlAndParams; } /** * add params * @param params * @param queryNode */ private void addParams(List<Object> params, QueryNode queryNode) { if(queryNode.getValue() !=null && queryNode.getValue().getClass().getName().endsWith(".ArrayList")){ //if the value is a list, for example: in operator List<Object> vlist = (List)queryNode.getValue(); for(Object v:vlist){ params.add(v); } }else{ params.add(queryNode.getValue()); } } /** * generate sql * @param form * @param node * @return * @throws NoSuchMethodException */ private String generateSql(Object form, QueryNode node) throws NoSuchMethodException { String sql = ""; if(Operator.IN.getSql().equals(node.getOp()) || Operator.NOT_IN.getSql().equals(node.getOp())){ List<Object> vlist = (List)node.getValue(); StringBuilder sb = new StringBuilder(); sb.append("("); for(int i=0;i<vlist.size();i++){ if(i!=0){ sb.append(","); } sb.append("?"); } sb.append(")"); sql = ReflectUtils.guessColumnName(form,node.getField()) + " " + node.getOp() + " " + sb.toString() + " "; }else{ sql = ReflectUtils.guessColumnName(form,node.getField()) + " " + node.getOp() + " ? "; } return sql; } /** * 从form中获取value * if sourceField has value then use sourceField to getValue * if not use field to getValue * @param form * @param queryNode * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws SecurityException * @throws IllegalArgumentException */ // private Object getValue(Object form,QueryNode queryNode) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ // String sourceField = queryNode.getSourceField(); // if(sourceField != null && !"".equals(sourceField)){ // return ReflectUtils.getValue(form,sourceField); // }else{ // return ReflectUtils.getValue(form,queryNode.getField()); // } // } /** * pick field from class with rule * @param form * @param ruleScheme * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws SecurityException * @throws IllegalArgumentException */ private List<QueryNode> pickFieldWithRule(Object form, Map<String, Rule> ruleScheme) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ //遍历all fields to make a field map List<Field> fieldList = createFieldList(form); //query node list to save the collect result List<QueryNode> collectedResult = new ArrayList<QueryNode>(); //遍历rule,用rule来collect field map. Once a field been collected , it will be removed from field list Iterator<Map.Entry<String, Rule>> ruleIt = ruleScheme.entrySet().iterator(); while(ruleIt.hasNext()){ Map.Entry<String, Rule> ruleEntry = ruleIt.next(); Rule rule = ruleEntry.getValue(); //scan the field map and try to collect field List<QueryNode> pickResult = pickFields(fieldList, rule, form); collectedResult.addAll(pickResult); } return collectedResult; } /** * pick field without : 1. no getter 2. no value * @param form * @return */ private List<Field> createFieldList(Object form){ List<Field> fieldList = new ArrayList<Field>(); Field[] allFields = form.getClass().getDeclaredFields(); for(Field field : allFields){ //check if hasGetter Method getter = null; try { getter = ReflectUtils.getGetterByFieldName(form, field.getName()); } catch (Exception e) { continue; } //check this field whether it has value Object value = null; try { value = getter.invoke(form); } catch (Exception e) { continue; } if(value != null){ fieldList.add(field); } } return fieldList; } /** * 用一个rule从field列表里面检索出符合条件的 queryNode * @param fieldList * @param rule * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws SecurityException * @throws IllegalArgumentException */ private List<QueryNode> pickFields(List<Field> fieldList, Rule rule, Object form) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ List<QueryNode> pickResult = new ArrayList<QueryNode>(); if(rule.getMembers() != null && rule.getMembers().size()>0){ //it's a group List<Rule> members = rule.getMembers(); List<QueryNode> queryNodeMembers = new ArrayList<QueryNode>(); for(Rule member:members){ List<QueryNode> queryNodes = pickFieldsWithSingleRule(fieldList,member,form); queryNodeMembers.addAll(queryNodes); } if(queryNodeMembers.size()>0){ QueryNode groupQueryNode = new QueryNode(queryNodeMembers, rule.getRel().getSql()); pickResult.add(groupQueryNode); } }else{ //it's a single node List<QueryNode> queryNodes = pickFieldsWithSingleRule(fieldList,rule,form); pickResult.addAll(queryNodes); } return pickResult; } /** * pick fields with this rule * @param fieldList * @param rule * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws SecurityException * @throws IllegalArgumentException */ private List<QueryNode> pickFieldsWithSingleRule(List<Field> fieldList, Rule rule,Object form) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ List<QueryNode> pickResult = new ArrayList<QueryNode>(); Iterator<Field> it = fieldList.iterator(); while(it.hasNext()){ Field field = it.next(); QueryNode queryNode = matchRule(field,rule, form); if(queryNode != null){ pickResult.add(queryNode); it.remove(); } } return pickResult; } /** * try to match field with this rule * @param field * @param rule * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws SecurityException * @throws IllegalArgumentException */ private QueryNode matchRule(Field field,Rule rule, Object form) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ QueryNode queryNode = null; String fieldExpr = rule.getField(); if(fieldExpr.contains("*")){ //wildcard match queryNode = RuleMatchUtils.wildcardMatch(field, rule, form); }else if(fieldExpr.contains(":")){ //full name match queryNode = RuleMatchUtils.fullnameMatch(field, rule, form); }else{ //short nama match queryNode = RuleMatchUtils.shortnameMatch(field, rule, form); } return queryNode; } }