001/* 002 * JKNIV, whinstone one contract to access your database. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.whinstone.commands; 021 022import java.sql.Statement; 023import java.util.Collections; 024import java.util.Iterator; 025import java.util.List; 026 027import net.sf.jkniv.cache.Cacheable; 028import net.sf.jkniv.sqlegance.LanguageType; 029import net.sf.jkniv.sqlegance.RepositoryException; 030import net.sf.jkniv.sqlegance.Selectable; 031import net.sf.jkniv.sqlegance.Sql; 032import net.sf.jkniv.sqlegance.builder.xml.SqlTag; 033import net.sf.jkniv.sqlegance.builder.xml.TagFactory; 034import net.sf.jkniv.sqlegance.dialect.SqlFeatureSupport; 035import net.sf.jkniv.whinstone.Param; 036import net.sf.jkniv.whinstone.QueryFactory; 037import net.sf.jkniv.whinstone.Queryable; 038 039/** 040 * 041 * @author Alisson Gomes 042 * @since 0.6.0 043 */ 044public abstract class DefaultQueryHandler extends DefaultCommandHandler 045{ 046 public DefaultQueryHandler(CommandAdapter cmdAdapter) 047 { 048 super(cmdAdapter); 049 } 050 051 @Override 052 @SuppressWarnings({ "unchecked", "rawtypes" }) 053 public <T> T run() 054 { 055 checkSqlConstraints(); 056 if (LOG.isTraceEnabled()) 057 LOG.trace("Executing [{}] as {} command", queryable, sql.getSqlType()); 058 059 List<?> list = Collections.emptyList(); 060 sql.getValidateType().assertValidate(queryable.getParams()); 061 Selectable selectable = sql.asSelectable(); 062 if (!queryable.isBoundSql()) 063 queryable.bind(selectable); 064 Cacheable.Entry entry = null; 065 066 if (!queryable.isCacheIgnore()) 067 entry = selectable.getCache().getEntry(queryable); 068 try 069 { 070 if (entry == null) 071 { 072 try 073 { 074 preCallback(); 075 Command command = asCommand(); 076 list = command.execute(); 077 if(queryable.hasFilter()) 078 filtering(list); 079 if(queryable.hasSorter()) 080 Collections.sort(list, queryable.getSorter()); 081 postCallback(); 082 if (selectable.hasCache() && !list.isEmpty()) 083 selectable.getCache().put(queryable, list); 084 085 paging(list); 086 } 087 catch(Exception e) 088 { 089 queryable.setTotal(Statement.EXECUTE_FAILED); 090 postException(); 091 handleableException.handle(e); 092 } 093 } 094 else 095 { 096 queryable.cached(); 097 queryable.setTotal(Statement.SUCCESS_NO_INFO); 098 list = (List<?>) entry.getValue(); 099 if (LOG.isDebugEnabled()) 100 LOG.debug("{} object(s) was returned from [{}] cache using query [{}] since {} reach [{}] times", 101 list.size(), selectable.getCache().getName(), sql.getName(), entry.getTimestamp(), 102 entry.hits()); 103 } 104 } 105 finally 106 { 107 this.cmdAdapter.close(); 108 } 109 //queryable.setTotal(list.size()); 110 if (LOG.isDebugEnabled()) 111 LOG.debug("Executed [{}] query as {} command, {} rows fetched", queryable.getName(), sql.getSqlType(), 112 list.size()); 113 114 return (T) list; 115 } 116 117 private void filtering(List<?> list) 118 { 119 Iterator<?> it = list.iterator(); 120 while(it.hasNext()) 121 { 122 if(!queryable.getFilter().isEqual(it.next())) 123 { 124 it.remove(); 125 } 126 } 127 } 128 129 private void paging(List<?> list) 130 { 131 if (queryable.isPaging()) 132 { 133 Sql dynamicSql = queryable.getDynamicSql(); 134 if (dynamicSql.getSqlDialect().supportsFeature(SqlFeatureSupport.PAGING_ROUNDTRIP)) 135 { 136 try 137 { 138 Command command = cmdAdapter.asSelectCommand(createQueryableForPaging(), null); 139 List<Number> rows = command.execute(); 140 if (rows.isEmpty()) 141 queryable.setTotal(0); 142 else 143 queryable.setTotal(rows.get(0).longValue()); 144 } 145 catch (RepositoryException e) 146 { 147 // FIXME BUG select count with ORDER BY 148 // The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. 149 queryable.setTotal(Statement.SUCCESS_NO_INFO); 150 LOG.error("Could not count the total of rows from full query [{}]", queryable.getName(), e); 151 } 152 /* 153 StatementAdapter<Number, ResultSet> adapterStmtCount = cmdAdapter.newStatement(queryable.queryCount(), dynamicSql.getLanguageType()); 154 queryable.bind(adapterStmtCount).on(); 155 adapterStmtCount.returnType(Number.class).scalar(); 156 try 157 { 158 Long rows = adapterStmtCount.rows().get(0).longValue(); 159 queryable.setTotal(rows); 160 } 161 catch (RepositoryException e) 162 { 163 // FIXME BUG select count with ORDER BY 164 // The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. 165 queryable.setTotal(Statement.SUCCESS_NO_INFO); 166 LOG.error("Cannot count the total of rows from full query [{}]", queryable.getName(), e); 167 } 168 */ 169 } 170 else 171 queryable.setTotal(Statement.SUCCESS_NO_INFO); 172 } 173 else if (queryable.getTotal() < 0) 174 queryable.setTotal(list.size()); 175 } 176 177 private Queryable createQueryableForPaging() 178 { 179 String queryName = "#paging_"+System.currentTimeMillis()+"_for_"+queryable.getName(); 180 //String[] paramNames = queryable.getDynamicSql().extractNames(queryable.getParams()); 181 Param[] paramValues = queryable.values();//(paramNames); 182 // TODO improve get values as array 183 Object[] paramArray = new Object[paramValues.length]; 184 for(int i=0; i<paramValues.length; i++) 185 paramArray[i] = paramValues[i].getValue(); 186 187 Queryable paging = QueryFactory.ofArray(queryName, queryable.getRegisterType(), paramArray); 188 Selectable selectable = TagFactory.newSelect(queryName, LanguageType.NATIVE, queryable.getDynamicSql().getSqlDialect()); 189 if (selectable instanceof SqlTag) 190 { 191 SqlTag sqlTag = (SqlTag)selectable; 192 sqlTag.addTag(queryable.queryCount()); 193 paging.bind(selectable); 194 } 195 paging.scalar(); 196 return paging; 197 } 198 199 private void checkSqlConstraints() 200 { 201 Selectable selectable = this.sql.asSelectable(); 202 LanguageType type = selectable.getLanguageType(); 203 if ((type == LanguageType.JPQL || type == LanguageType.HQL) && selectable.getGroupBy().trim().length() > 0) 204 { 205 throw new IllegalArgumentException("JPQL cannot have group by, just NATIVE or STORED, change the type [" 206 + type + "] from SQL [" + selectable.getName() + "] to execute"); 207 } 208 } 209 210}