1 10 11 package com.triactive.jdo.store; 12 13 import com.triactive.jdo.ClassNotPersistenceCapableException; 14 import com.triactive.jdo.PersistenceManager; 15 import com.triactive.jdo.model.ClassMetaData; 16 import com.triactive.jdo.model.FieldMetaData; 17 import com.triactive.jdo.util.IntArrayList; 18 import com.triactive.jdo.util.MacroString; 19 import java.sql.Connection ; 20 import java.sql.PreparedStatement ; 21 import java.sql.ResultSet ; 22 import java.sql.ResultSetMetaData ; 23 import java.sql.SQLException ; 24 import java.util.ArrayList ; 25 import java.util.Collection ; 26 import java.util.HashSet ; 27 import java.util.Iterator ; 28 import java.util.Map ; 29 import java.util.List ; 30 import javax.jdo.Extent; 31 import javax.jdo.JDODataStoreException; 32 import javax.jdo.JDOFatalInternalException; 33 import javax.jdo.JDOFatalUserException; 34 import javax.jdo.JDOUserException; 35 import org.apache.log4j.Category; 36 37 38 46 47 public class TJDOSQLQuery extends Query 48 { 49 private static final Category LOG = Category.getInstance(TJDOSQLQuery.class); 50 51 private transient final String tjdoSqlText; 52 53 private transient String jdbcSqlText = null; 54 private transient int[] fieldNumbers = null; 55 private transient ColumnMapping[] fieldMappings = null; 56 private transient List fieldColumnNames = null; 57 private transient List parameterOccurrences = null; 58 59 60 68 69 public TJDOSQLQuery(PersistenceManager pm, StoreManager storeMgr, String tjdoSqlText) 70 { 71 super(pm, storeMgr); 72 73 candidateClass = null; 74 filter = null; 75 imports = null; 76 variables = null; 77 parameters = null; 78 ordering = null; 79 80 this.tjdoSqlText = tjdoSqlText; 81 } 82 83 84 protected void discardCompiled() 85 { 86 super.discardCompiled(); 87 88 jdbcSqlText = null; 89 fieldNumbers = null; 90 fieldMappings = null; 91 fieldColumnNames = null; 92 parameterOccurrences = null; 93 } 94 95 96 public boolean equals(Object obj) 97 { 98 if (obj == this) 99 return true; 100 101 if (!(obj instanceof TJDOSQLQuery) || !super.equals(obj)) 102 return false; 103 104 return tjdoSqlText.equals(((TJDOSQLQuery)obj).tjdoSqlText); 105 } 106 107 108 120 121 public void setCandidates(Extent pcs) 122 { 123 throw new JDOUserException("Candidate extents not applicable to TJDOSQL queries"); 124 } 125 126 127 139 140 public void setCandidates(Collection pcs) 141 { 142 throw new JDOUserException("Candidate collections not applicable to TJDOSQL queries"); 143 } 144 145 146 158 159 public void setFilter(String filter) 160 { 161 throw new JDOUserException("Filter strings not applicable to TJDOSQL queries"); 162 } 163 164 165 177 178 public void declareVariables(String variables) 179 { 180 throw new JDOUserException("Variables are not applicable to TJDOSQL queries"); 181 } 182 183 184 196 197 public void setOrdering(String ordering) 198 { 199 throw new JDOUserException("Ordering must be set via explicit ORDER BY in the SQL text for TJDOSQL queries"); 200 } 201 202 203 209 210 public void compile() 211 { 212 if (!isCompiled) 213 { 214 super.compile(); 215 216 generateQueryStatement(); 217 218 isCompiled = true; 219 } 220 } 221 222 223 private void generateQueryStatement() 224 { 225 if (candidateClass == null) 226 throw new JDOUserException("No candidate class provided"); 227 228 final ClassMetaData cmd = ClassMetaData.forClass(candidateClass); 229 230 if (cmd == null) 231 throw new ClassNotPersistenceCapableException(candidateClass); 232 233 if (cmd.requiresExtent()) 234 throw new JDOUserException("Invalid candidate class for TJDOSQL, must not have an extent (use requires-extent=\"false\" in the JDO metadata): " + candidateClass.getName()); 235 236 if (cmd.getIdentityType() != ClassMetaData.NO_IDENTITY) 237 throw new JDOUserException("Invalid candidate class for TJDOSQL, must use non-durable identity (use identity-type=\"nondurable\" in the JDO metadata): " + candidateClass.getName()); 238 239 if (cmd.getPCSuperclass() != null) 240 throw new PersistentSuperclassNotAllowedException(candidateClass); 241 242 int fieldCount = cmd.getFieldCount(); 243 IntArrayList fn = new IntArrayList(fieldCount); 244 fieldMappings = new ColumnMapping[fieldCount]; 245 fieldColumnNames = new ArrayList(fieldCount); 246 247 for (int fieldNumber = 0; fieldNumber < fieldCount; ++fieldNumber) 248 { 249 FieldMetaData fmd = cmd.getFieldRelative(fieldNumber); 250 String fieldName = fmd.getName(); 251 Class fieldType = fmd.getType(); 252 253 switch (fmd.getPersistenceModifier()) 254 { 255 case FieldMetaData.PERSISTENCE_MODIFIER_NONE: 256 default: 257 throw new JDOFatalInternalException("Invalid persistence modifier on field " + fieldName); 258 259 case FieldMetaData.PERSISTENCE_MODIFIER_TRANSACTIONAL: 260 break; 261 262 case FieldMetaData.PERSISTENCE_MODIFIER_PERSISTENT: 263 Mapping m = dba.getMapping(fieldType); 264 265 if (!(m instanceof ColumnMapping)) 266 throw new JDOFatalUserException("Mapping " + m + " not suitable for a TJDOSQL result column, field = " + fieldName); 267 268 fieldMappings[fieldNumber] = (ColumnMapping)m; 269 fn.add(fieldNumber); 270 fieldColumnNames.add(new ColumnIdentifier(dba, fieldName, fieldType, Role.NONE).getSQLIdentifier()); 271 break; 272 } 273 274 } 275 276 if (fn.isEmpty()) 277 throw new JDOFatalUserException("View class has no persistent fields: " + candidateClass.getName()); 278 279 fieldNumbers = fn.toArray(); 280 281 287 parameterOccurrences = new ArrayList(); 288 289 MacroString ms = new MacroString(candidateClass, imports, tjdoSqlText); 290 291 jdbcSqlText = ms.substituteMacros(new MacroString.MacroHandler() 292 { 293 public void onIdentifierMacro(MacroString.IdentifierMacro im) 294 { 295 if (im.clazz.equals(candidateClass)) 296 { 297 if (im.fieldName == null) 298 throw new JDOUserException("Invalid macro, query result classes have no table: " + im); 299 if (im.subfieldName != null) 300 throw new JDOUserException("No such field in query result class " + im.clazz.getName() + ": " + im); 301 int fieldNumber = cmd.getRelativeFieldNumber(im.fieldName); 302 303 if (fieldNumber < 0) 304 throw new JDOUserException("No such field in query result class " + im.clazz.getName() + ": " + im); 305 im.value = (String )fieldColumnNames.get(fieldNumber); 306 } 307 else 308 storeMgr.resolveIdentifierMacro(im); 309 } 310 311 public void onParameterMacro(MacroString.ParameterMacro pm) 312 { 313 parameterOccurrences.add(pm.parameterName); 314 } 315 } 316 ); 317 } 318 319 320 330 331 public Object executeWithMap(Map parameters) 332 { 333 compile(); 334 335 if (parameters.size() != parameterNames.size()) 336 throw new JDOUserException("Incorrect number of parameters: " + parameters.size() + ", s/b " + parameterNames.size()); 337 338 QueryResult qr = null; 339 340 try 341 { 342 Connection conn = pm.getConnection(false); 343 344 try 345 { 346 PreparedStatement ps = conn.prepareStatement(jdbcSqlText); 347 348 try 349 { 350 351 Iterator i = parameterOccurrences.iterator(); 352 int stmtParamNum = 1; 353 354 while (i.hasNext()) 355 { 356 String paramName = (String )i.next(); 357 Class paramType = (Class )parameterTypesByName.get(paramName); 358 359 if (!parameters.containsKey(paramName)) 360 throw new JDOUserException("Required parameter " + paramName + " not provided"); 361 if (paramType == null) 362 throw new JDOUserException("Undeclared parameter " + paramName + " occurred in SQL text"); 363 364 Mapping m = dba.getMapping(paramType); 365 Object paramValue = parameters.get(paramName); 366 367 if (!(m instanceof ColumnMapping)) 368 throw new JDOUserException("Illegal parameter type, param = " + paramName + " type = " + paramType.getName()); 369 370 ((ColumnMapping)m).setObject(pm, ps, stmtParamNum++, paramValue); 371 } 372 373 long startTime = System.currentTimeMillis(); 374 375 ResultSet rs = ps.executeQuery(); 376 377 if (LOG.isDebugEnabled()) 378 LOG.debug("Time = " + (System.currentTimeMillis() - startTime) + " ms: " + jdbcSqlText); 379 380 try 381 { 382 int[] columnNumbersByField = new int[fieldMappings.length]; 383 ResultSetMetaData rsmd = rs.getMetaData(); 384 HashSet remainingColumnNames = new HashSet (fieldColumnNames); 385 386 int colCount = rsmd.getColumnCount(); 387 388 for (int colNum = 1; colNum <= colCount; ++colNum) 389 { 390 String colName = rsmd.getColumnName(colNum); 391 int fieldNumber = fieldColumnNames.indexOf(colName); 392 393 if (fieldNumber >= 0) 394 { 395 columnNumbersByField[fieldNumber] = colNum; 396 remainingColumnNames.remove(colName); 397 } 398 } 399 400 if (!remainingColumnNames.isEmpty()) 401 throw new JDOUserException("Expected columns missing from result set: " + remainingColumnNames); 402 403 404 qr = new QueryResult(this, 405 new TransientIDROF(pm, 406 candidateClass, 407 fieldNumbers, 408 fieldMappings, 409 columnNumbersByField), 410 rs); 411 } 412 finally 413 { 414 if (qr == null) 415 rs.close(); 416 } 417 } 418 finally 419 { 420 if (qr == null) 421 ps.close(); 422 } 423 } 424 finally 425 { 426 pm.releaseConnection(conn); 427 } 428 } 429 catch (SQLException e) 430 { 431 throw dba.newDataStoreException("Error executing query: " + jdbcSqlText, e); 432 } 433 434 queryResults.add(qr); 435 436 return qr; 437 } 438 } 439 | Popular Tags |