Changeset 642
- Timestamp:
- Sep 5, 2010, 8:06:53 PM (14 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/groovy/HqlBuilder.groovy
r641 r642 16 16 /** 17 17 * Provides a DSL for building and managing HQL strings. 18 * For usage examples see the HqlBuilderTests.18 * For more usage examples see the HqlBuilderTests. 19 19 * HQL reference see http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html 20 * 21 * DML reference see http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/batch.html#batch-direct 22 * DML-style clauses: "update, delete, insert into" may not be well tested, the "set" clause has basic implementation. 23 * So double check the q.query output for these cases. 20 24 * 21 25 * Primary goals: … … 25 29 * 4. Easy to change a clause and execute again. 26 30 * 31 * Basic usage: 32 * def q = new HqlBuilder().query { 33 * select 'count(distinct book)' 34 * from 'Book as book' 35 * where 'book.id > 100' 36 * and 'book.inStock = true' 37 * } 38 * def totalCount = Book.executeQuery(q.query, q.namedParams, q.paginateParams)[0].toInteger() 39 * q.select = 'distinct book' 40 * def list = Book.executeQuery(q.query, q.namedParams, q.paginateParams) 41 * 42 * def bookList = new PagedResultList(list, totalCount) 43 * log.debug '\n' + q.printFormattedQuery 44 * 27 45 * @author Gavin Kromhout 28 * @version Draft A46 * @version DraftB 29 47 * 30 48 */ 31 49 class HqlBuilder { 32 50 33 // Queryclauses.34 // Each clause is a key with a list of terms.51 // HQL clauses. 52 // Each clause is a map key with a list of terms. 35 53 def clauses = [:] 36 54 … … 42 60 def paginateParams = [max: 1000, offset: 0] 43 61 44 // It is easier and more flexible to simply add order as a clause, e.g: order 'by id asc' 45 //def sort = "" // e.g. instanceName.id 46 //def order = "" // e.g. asc or desc 47 48 def HqlBuilder(debug = false) { 62 // The where clause terms are handled separately from other clauses 63 // and are a list of logicalTerms. 64 // The where clause is built by buildWhereClause. 65 def whereClauseTerms = [] 66 67 // LogicalIndexStack holds indexes of the current whereClauseTerm nesting. 68 def logicalIndexStack = [] 69 70 def logicalBuilders = [AND: 'and', 71 OR: 'or'] 72 73 def nestingStack = [] 74 75 // Sort and Order. 76 // It is easier and more flexible to simply add order as a clause, e.g: order 'by name desc, id asc' 77 // def sort = "" // e.g. instanceName.id 78 // def order = "" // e.g. asc or desc 79 80 /** 81 * Constructor. 82 * Any property that exists (or responds) in the class may be supplied as an argument. 83 * E.g: max:20, offset:10, debug:true 84 * The debug property does not really exist, but if true and no external log property 85 * has been setup then the internal mockLogger will be configured in debug mode. 86 * 87 * @param args A map of arguments, defaults to an empty map. 88 * 89 */ 90 def HqlBuilder(Map args = [:]) { 91 args.each { arg -> 92 def argKey = arg.key.toLowerCase() 93 if(super.hasProperty(argKey)) 94 this[argKey] = arg.value 95 } 49 96 if(!super.metaClass.hasMetaProperty('log')) 50 mockLogging( debug)97 mockLogging(args.debug) 51 98 log.debug "HqlBuilder()" 52 99 } 53 100 101 /** 102 * Call with no args. 103 * Has no real use other than to prevent obscure errors. 104 */ 54 105 def call() { 55 106 log.debug "call()" … … 69 120 * Has no real use other than to prevent obscure errors 70 121 * when user makes a call to query() and Groovy calls query(Closure cl) 122 * 71 123 * @returns This object. 124 * 72 125 */ 73 126 def query() { … … 78 131 /** 79 132 * Domain specific build method. 80 * A typically used build call, e.g: def q = new HqlBuilder().query { }81 * 82 * @param cl The supplied Closure.133 * The recommended build call, e.g: def q = new HqlBuilder().query { } 134 * 135 * @param cl The closure that will be used to build the query. 83 136 * @returns This object. 84 137 * … … 92 145 /** 93 146 * InvokeMethod resolves all undefined methods. 94 * Which include the clause methods, e.g select 'book' is equivalent to select('book').147 * Which include the clause methods, e.g: select 'book' is equivalent to select('book'). 95 148 * Note that defined methods will be called directly since this class does not implement GroovyInterceptable. 96 149 * If class was "HqlBuilder implements GroovyInterceptable" then even println would be intercepted and 97 150 * several exlusions might be needed. e.g: if(methodName != 'call' && methodName != 'println') 98 151 */ 99 def invokeMethod(String name, args) {152 def invokeMethod(String methodName, args) { 100 153 101 154 log.debug "invokeMethod(${methodName}, ${args})" 102 155 103 if(!this.clauses[name]) 104 this.clauses[name] = [] 156 // Call any closures first, that way the nesting is handled and we just keep a reference. 157 if(args[-1] instanceof Closure) { 158 handleClosure(args[-1], methodName) 159 args = args.minus(args[-1]) 160 } 161 162 if(!clauses.containsKey(methodName) && !isLogicalBuilder(methodName)) 163 clauses[methodName] = [] 164 165 if(args) { 166 if(isWhereClauseBuilder(methodName)) { 167 logicalBuilder(methodName, args) 168 return 169 } 170 } 105 171 106 172 for(arg in args) { 107 if(arg instanceof String) 108 this.clauses[name] << arg 109 } 110 111 if(args[-1] instanceof Closure) 112 handleClosure(args[-1]) 173 if(arg instanceof String || arg instanceof GString) 174 clauses[methodName] << arg 175 } 113 176 114 177 } // invokeMethod() … … 119 182 * and clauses to be removed, e.g: q.order = null 120 183 */ 121 def propertyMissing(String name, value) {184 def propertyMissing(String propertyName, value) { 122 185 log.debug "propertyMissing(${propertyName}, ${value})" 186 123 187 if(value == null) { 124 clauses.remove(name) 188 removeClause(propertyName) 189 if(propertyName.toLowerCase() == 'where') 190 whereClauseTerms.clear() 125 191 return 126 192 } 127 193 128 if(value instanceof String) 129 clauses[name] = [value] 130 } 194 if(!clauses.containsKey(propertyName)) 195 clauses[propertyName] = [] 196 197 // Occurs when user assigns to where clause, e.g: q.where = 'book.id > 100' 198 if(propertyName.toLowerCase() == 'where') { 199 whereClauseTerms.clear() 200 logicalBuilder(propertyName, [value]) 201 return 202 } 203 204 if(value instanceof String || value instanceof GString) 205 clauses[propertyName] = [value] 206 } // propertyMissing(String propertyName, value) 131 207 132 208 /** … … 135 211 * Since clauses is a Map null is simply returned for a non-existant clause. 136 212 */ 137 def propertyMissing(String name) { 138 log.debug "propertyMissing(${name})" 139 clauses[name] 213 def propertyMissing(String propertyName) { 214 log.debug "propertyMissing(${propertyName})" 215 216 if(!clauses.containsKey(propertyName)) 217 clauses[propertyName] = [] 218 219 // Occurs when user performs an operation on where clause. 220 // E.g: q.where << "book.id = 100" which is actually NOT a supported operation since 221 // calling the method provides the correct function e.g: q.where "book.id > 100". 222 // Also allows `println q.where` to be short hand for `println q.whereClauseTerms` 223 if(propertyName.toLowerCase() == 'where') { 224 return whereClauseTerms 225 } 226 227 clauses[propertyName] 228 } // propertyMissing(String propertyName) 229 230 def setMax(Integer value) { 231 paginateParams.max = value 232 } 233 234 def getMax() { 235 paginateParams.max 236 } 237 238 def setOffset(Integer value) { 239 paginateParams.offset = value 240 } 241 242 def getOffset() { 243 paginateParams.offset 244 } 245 246 /** 247 * RemoveClause. 248 * Allows clauses to be removed, e.g: q.removeClause('order') 249 * 250 * @param clauseName The clause to remove. 251 * 252 */ 253 def removeClause(String clauseName) { 254 clauses.remove(clauseName) 255 } 256 257 /** 258 * BuildWhereClause. 259 * Build the where clause from whereClauseTerms. 260 */ 261 def buildWhereClause(printFormat = false) { 262 //log.debug "buildWhereClause()" 263 264 if(!whereClauseTerms) 265 return '' 266 267 def whereClause = 'where ' 268 269 def buildExpression // declared separately to allow recurrsion. 270 buildExpression = { term -> 271 def result = '' 272 def termCount = term.expressions.size() 273 if(termCount > 1) { 274 term.expressions.eachWithIndex { t, index -> 275 if(index == 0) 276 result += buildExpression(t) 277 else if(printFormat) 278 result += " \n\t${t.logic} ${buildExpression(t)}" 279 else 280 result += " ${t.logic} ${buildExpression(t)}" 281 282 } 283 result = "( "+result+" )" 284 } 285 else { 286 if(term.expressions[0] instanceof Map) 287 result += "${term.expressions[0].expressions[0]}" 288 else 289 result += "${term.expressions[0]}" 290 } 291 return result 292 } 293 294 whereClauseTerms.eachWithIndex { tm, index -> 295 if(index == 0) 296 whereClause += buildExpression(tm) 297 else if(printFormat) 298 whereClause += " \n\t${tm.logic} ${buildExpression(tm)}" 299 else 300 whereClause += " ${tm.logic} ${buildExpression(tm)}" 301 } 302 303 return whereClause 304 } // buildWhereClause(printFormat = false) 305 306 /** 307 * LogicalBuilder. 308 * Build the whereClauseTerms 309 * by appending logicalTerms to the appropriate expressions. 310 */ 311 def logicalBuilder(logicalName, args) { 312 log.debug "logicalBuilder(${logicalName}, ${args})" 313 log.debug "logicalIndexStack: ${logicalIndexStack}" 314 315 def logic = getLogicalString(logicalName) 316 317 for(arg in args) { 318 if(arg instanceof String || arg instanceof GString) { 319 arg = arg.trim() 320 if(arg) { // prevent empty strings being added. 321 if(logicalIndexStack.size() > 0) { 322 // Append to current index position. 323 whereClauseTerms[logicalIndexStack[-1]].expressions << logicalTerm(logic, arg) 324 } 325 else { 326 // Append to 'root'. 327 whereClauseTerms << logicalTerm(logic, null) // empty expression logicalTerm. 328 whereClauseTerms[-1].expressions << logicalTerm(logic, arg) // append logicalTerm to expressions 329 } 330 } // if(arg) 331 } // if(arg instanceof) 332 } // for 333 334 } // logicalBuilder(logicalName, args) 335 336 /** 337 * LogicalTerm. 338 * A logicalTerm is a map object that holds the logic and list of expressions of a whereClauseTerm. 339 */ 340 def logicalTerm = { logic, expression -> 341 expression = expression ? [expression] : [] 342 ['logic': getLogicalString(logic), 'expressions': expression] 343 } 344 345 /** 346 * GetLogicalString. 347 * 348 * @param logicalName The name to get the matching logicalBuilder string for. 349 */ 350 private getLogicalString(logicalName) { 351 352 switch(logicalName.toLowerCase()) { 353 case 'where': 354 logicalBuilders.AND 355 break 356 case logicalBuilders.AND: 357 logicalBuilders.AND 358 break 359 case logicalBuilders.OR: 360 logicalBuilders.OR 361 break 362 } 363 140 364 } 141 365 … … 144 368 * Setting delegate and DELEGATE_FIRST allows closure to access this object's properties first. 145 369 */ 146 private handleClosure(Closure cl) { 370 private handleClosure(Closure cl, String methodName = 'root') { 371 log.debug "handleClosure(${cl.toString()}, ${methodName})" 372 if(isWhereClauseBuilder(methodName)) { 373 whereClauseTerms << logicalTerm(getLogicalString(methodName), null) 374 logicalIndexStack << whereClauseTerms.size()-1 375 } 376 nestingStack.push(methodName) 147 377 cl.delegate = this 148 378 cl.resolveStrategy = Closure.DELEGATE_FIRST 149 379 cl.call() 380 //log.debug "nestingStack: $nestingStack" 381 nestingStack.pop() 382 if(isWhereClauseBuilder(methodName)) { 383 logicalIndexStack.pop() 384 } 150 385 } 151 386 … … 166 401 167 402 /** 403 * IsLogicalBuilder. 404 * Determine if a method is a logicalBuilder. 405 */ 406 private isLogicalBuilder(String methodName) { 407 logicalBuilders.find{ it.value == methodName.toLowerCase()} ? true:false 408 } 409 410 /** 411 * IsWhereClauseBuilder. 412 * Determine if a method is a where clause builder. 413 */ 414 private isWhereClauseBuilder(String methodName) { 415 methodName = methodName.toLowerCase() 416 if(methodName == 'where' || isLogicalBuilder(methodName)) 417 return true 418 else 419 return false 420 } 421 422 /** 168 423 * GetQuery. 169 424 * Assemble and return the query in a format that can be directly executed. … … 171 426 */ 172 427 def getQuery() { 173 this.clauses.collect { 174 it.key + ' ' + it.value.join(' ') 428 clauses.collect { clause -> 429 switch (clause.key.toLowerCase()) { 430 case 'select': 431 clause.key + ' ' + clause.value.join(', ') 432 break 433 case 'set': 434 clause.key + ' ' + clause.value.join(', ') 435 break 436 case 'where': 437 buildWhereClause() 438 break 439 case 'order': 440 clause.key + ' ' + clause.value.join(', ') 441 break 442 case 'group': 443 clause.key + ' ' + clause.value.join(', ') 444 break 445 default: 446 clause.key + ' ' + clause.value.join(' ') 447 } 175 448 }.join(' ') 176 } 449 } // getQuery() 177 450 178 451 /** … … 182 455 */ 183 456 def getPrintFormattedQuery() { 184 this.clauses.collect { 185 it.key + ' ' + it.value.join(' \n') 457 clauses.collect { clause -> 458 switch (clause.key.toLowerCase()) { 459 case 'select': 460 clause.key + ' ' + clause.value.join(', \n\t') 461 break 462 case 'set': 463 clause.key + ' ' + clause.value.join(', \n\t') 464 break 465 case 'where': 466 buildWhereClause(true) 467 break 468 case 'order': 469 clause.key + ' ' + clause.value.join(', \n\t') 470 break 471 case 'group': 472 clause.key + ' ' + clause.value.join(', \n\t') 473 break 474 default: 475 clause.key + ' ' + clause.value.join(' \n\t') 476 } 186 477 }.join(' \n') 187 } 478 } // getPrintFormattedQuery() 188 479 189 480 } // end class -
trunk/test/unit/HqlBuilderTests.groovy
r641 r642 21 21 * 22 22 * @author Gavin Kromhout 23 * @version Draft A23 * @version DraftB 24 24 * 25 25 */ … … 27 27 28 28 def n = '\n' 29 def t = '\t' 29 30 def savedMetaClass 30 31 … … 33 34 savedMetaClass = HqlBuilder.metaClass 34 35 def emc = new ExpandoMetaClass(HqlBuilder, true, true) 35 //emc.log = LogFactory.getLog(getClass())36 emc.log = LogFactory.getLog(getClass()) 36 37 emc.initialize() 37 38 GroovySystem.metaClassRegistry.setMetaClass(HqlBuilder, emc) … … 44 45 } 45 46 46 void test SelectBasic() {47 void testBasicUsage() { 47 48 48 49 def q = new HqlBuilder().query { … … 58 59 assert q.printFormattedQuery == 'select distinct book \nfrom Book as book \nwhere book.id > 100' 59 60 60 } // testSelectBasic() 61 62 void testSelectAdditional() { 63 64 def q = new HqlBuilder().query { 65 select 'distinct book' 66 from 'Book as book' 67 left 'join book.group as group', 61 } // testBasicUsage() 62 63 void testBasicUsageAlternateForm() { 64 65 def q = new HqlBuilder() 66 67 q { 68 select 'distinct book' 69 from 'Book as book' 70 where 'book.id > 100' 71 } 72 73 assert q.query == 'select distinct book from Book as book where book.id > 100' 74 75 } // testBasicUsageAlternateForm() 76 77 void testPaginateParams() { 78 79 def q = new HqlBuilder(max: 99, offset: 11).query { 80 select 'distinct book' 81 from 'Book as book' 82 where 'book.id > 100' 83 } 84 85 assert q.max == 99 86 assert q.offset == 11 87 88 } // testPaginateParams() 89 90 void testPaginateParamsAlternateForm() { 91 92 def q = new HqlBuilder().query { 93 max = 99 94 offset = 11 95 select 'distinct book' 96 from 'Book as book' 97 where 'book.id > 100' 98 } 99 100 assert q.max == 99 101 assert q.offset == 11 102 103 } // testPaginateParamsAlternateForm() 104 105 void testNamedParams() { 106 def startId = 13 107 def endId = 23 108 109 def q = new HqlBuilder().query { 110 namedParams.startId = startId 111 select 'distinct book' 112 from 'Book as book' 113 where 'book.id > :startId' 114 and 'book.id < :endId' 115 } 116 117 q.namedParams.endId = endId 118 119 assert q.namedParams.startId == startId 120 assert q.namedParams.endId == endId 121 122 } // testNamedParams() 123 124 void testMultipleTerms() { 125 126 def q = new HqlBuilder().query { 127 select 'book.name', 128 'type.name' 129 from 'Book as book', 130 'left join book.group as group', 68 131 'left join group.type as type' 69 where 'book.id > 100', 70 'and group = :group' 71 } 72 73 assert q.query == 'select distinct book from Book as book left join book.group as group left join group.type as type where book.id > 100 and group = :group' 74 75 } // testSelectAdditional() 76 77 void testSelectAlternate() { 78 79 def q = new HqlBuilder() 80 81 q { 82 select 'distinct book' 83 from 'Book as book' 84 where(/book.name like '%Ned%'/) // Slashy string literals have to be protected when calling a function. 85 where 'and book.description like "Head"' 86 } 87 88 assert q.query == /select distinct book from Book as book where book.name like '%Ned%' and book.description like "Head"/ 89 assert q.printFormattedQuery == /select distinct book ${n}from Book as book ${n}where book.name like '%Ned%' ${n}and book.description like "Head"/ 90 91 } // testSelectAlternate() 92 93 void testSelectWithPlaceHolder() { 94 95 def q = new HqlBuilder().query { 96 select 'distinct book' 97 from 'Book as book' 98 where '' // Place holder. 132 where "book.name like '%Ned%'", 133 'group = :group' 134 } 135 136 def expectedQuery = /select book.name, type.name from Book as book left join book.group as group left join group.type as type/ 137 expectedQuery += / where book.name like '%Ned%' and group = :group/ 138 assert q.query == expectedQuery 139 140 def expectedPrintFormat = /select book.name, ${n}${t}type.name/ 141 expectedPrintFormat += / ${n}from Book as book ${n}${t}left join book.group as group ${n}${t}left join group.type as type/ 142 expectedPrintFormat += / ${n}where book.name like '%Ned%' ${n}${t}and group = :group/ 143 assert q.printFormattedQuery == expectedPrintFormat 144 145 } // testMultipleTerms() 146 147 void testMultipleTermsAlternateForm() { 148 149 def q = new HqlBuilder().query { 150 select 'book.name' // Create clause and append arg to clause's term list. 151 select 'type.name' // Method arg is appended to existing clause's term list. 152 from 'Book as book' 153 left 'join book.group as group' 154 left 'left join group.type as type' // 'left join' has to be repeated since left is an existing clause. 155 where(/book.name like '%Ned%'/) // Slashy string literals have to be protected when calling a method. 156 where 'group = :group' 157 } 158 159 def expectedQuery = /select book.name, type.name from Book as book left join book.group as group left join group.type as type/ 160 expectedQuery += / where book.name like '%Ned%' and group = :group/ 161 assert q.query == expectedQuery 162 163 def expectedPrintFormat = /select book.name, ${n}${t}type.name/ 164 expectedPrintFormat += / ${n}from Book as book ${n}left join book.group as group ${n}${t}left join group.type as type/ 165 expectedPrintFormat += / ${n}where book.name like '%Ned%' ${n}${t}and group = :group/ 166 assert q.printFormattedQuery == expectedPrintFormat 167 168 } // testMultipleTermsAlternateForm() 169 170 void testPlaceHolder() { 171 172 def q = new HqlBuilder().query { 173 select 'distinct book' 174 from 'Book as book' 175 where // Place holder as propertyMissing call. 99 176 order 'by book.name asc' 100 177 } 101 178 102 // Insert to place holder which is in the middle ofquery string.103 q.where = /book.name like '%Ned%'/ // Slashy string literals don't need protecting when assigning.179 // Assign to place holder which is in the middle of the query string. 180 q.where = /book.name like '%Ned%'/ 104 181 105 182 assert q.query == /select distinct book from Book as book where book.name like '%Ned%' order by book.name asc/ 106 183 107 } // testSelectWithPlaceHolder() 108 109 void testSelectWithClauseRemoval() { 184 } // testPlaceHolder() 185 186 void testPlaceHolderAlternateForm() { 187 188 def q = new HqlBuilder().query { 189 select 'distinct book' 190 from 'Book as book' 191 where '' // Place holder as method call, tests for nulls when also using append method call bellow. 192 order 'by book.name asc' 193 } 194 195 // Append to place holder which is in the middle of the query string. 196 q.where(/book.name like '%Ned%'/) 197 198 assert q.query == /select distinct book from Book as book where book.name like '%Ned%' order by book.name asc/ 199 200 } // testPlaceHolderAlternateForm() 201 202 void testClauseRemoval() { 110 203 111 204 def q = new HqlBuilder().query { … … 116 209 } 117 210 118 q.order = null // Remove clause , since order by makes no sense when selecting a count ;-)211 q.order = null // Remove clause. 119 212 assert q.query == /select count(distinct book) from Book as book where book.name like '%Ned%'/ 120 213 121 } // testSelectWithClauseRemoval() 214 } // testClauseRemoval() 215 216 void testClauseRemovalAlternateForm() { 217 218 def q = new HqlBuilder().query { 219 select 'count(distinct book)' 220 from 'Book as book' 221 where = /book.name like '%Ned%'/ // Slashy string literals don't need protecting when assigning. 222 order 'by book.name asc' 223 } 224 225 q.removeClause('order') // Remove clause, alternate form. 226 assert q.query == /select count(distinct book) from Book as book where book.name like '%Ned%'/ 227 228 } // testClauseRemovalAlternateForm() 229 230 void testLogicalBuilder() { 231 232 def q = new HqlBuilder().query { 233 from 'Book as book' 234 where "book.name like '%Ned%'" 235 or "book.onSpecial = true" 236 } 237 238 assert q.query == /from Book as book where book.name like '%Ned%' or book.onSpecial = true/ 239 240 } // testLogicalBuilder() 241 242 void testLogicalBuilderNesting() { 243 244 def q = new HqlBuilder().query { 245 from 'Book as book' 246 where "book.name like '%Ned%'" 247 or { 248 where "book.onSpecial = true" 249 and 'book.inStock = true' 250 } 251 } 252 253 assert q.query == /from Book as book where book.name like '%Ned%' or ( book.onSpecial = true and book.inStock = true )/ 254 255 } // testLogicalBuilderNesting() 256 257 void testLogicalBuilderNestingLoop() { 258 def range = 1..2 259 260 def q = new HqlBuilder().query { 261 from 'Book as book' 262 where 'book.inStock = true' 263 and { 264 range.each { 265 or "book.id = $it" 266 } 267 } 268 } 269 270 assert q.query == /from Book as book where book.inStock = true and ( book.id = 1 or book.id = 2 )/ 271 272 } // testLogicalBuilderNestingLoop() 273 274 void testWhereClosure() { 275 276 def q = new HqlBuilder().query { 277 from 'Book as book' 278 where { 279 and 'book.id = 1' 280 } 281 } 282 283 // Only 1 expression so no brackets. 284 assert q.query == /from Book as book where book.id = 1/ 285 286 } // testWhereClosure() 287 288 void testWhereClosureAlternate() { 289 290 def q = new HqlBuilder().query { 291 from 'Book as book' 292 } 293 294 q.where { 295 and 'book.id = 1', 296 'book.inStock = true' 297 } 298 299 // More than 1 expression so brackets are included. 300 assert q.query == /from Book as book where ( book.id = 1 and book.inStock = true )/ 301 302 } // testWhereClosureAlternate() 303 304 // This is very likely to be a common usage error as it may seem like a natural way to write the where clause. 305 // Is it possible to intercept the String & GString constructors just inside the closure and call where 'book.id = 1'? 306 // Perhaps by internally creating a new Closure and using something like this: 307 // http://groovy.codehaus.org/JN3515-Interception ??? 308 // Or is it possible to examine each statment of a closure? 309 void testWhereClosureWithNewString() { 310 311 def q = new HqlBuilder().query { 312 from 'Book as book' 313 where { 314 'book.id = 1' // This statement is missing a method call and hence will simply be excluded. 315 and 'book.inStock = true' 316 } 317 } 318 319 // Would be nice if the first case was true. 320 assertFalse q.query == /from Book as book where ( book.id = 1 and book.inStock = true )/ 321 assert q.query == /from Book as book where book.inStock = true/ 322 323 } // testSelectWhereClosureWithNewString() 324 325 void testWithConditionals() { 326 def y = true 327 def n = false 328 329 def q = new HqlBuilder().query { 330 select 'distinct book' 331 from 'Book as book' 332 if(y) 333 where(/book.name like '%Ned%'/) 334 if(n) 335 order '' 336 else 337 order 'by book.name asc' 338 } 339 340 assert q.query == /select distinct book from Book as book where book.name like '%Ned%' order by book.name asc/ 341 342 } // testWithConditionals() 343 344 void testSelectWithLooping() { 345 def selections = ['id', 'name', 'description'] 346 347 def q = new HqlBuilder().query { 348 for(s in selections) { 349 select "book.${s}" 350 } 351 from 'Book as book' 352 } 353 354 assert q.query == /select book.id, book.name, book.description from Book as book/ 355 356 } // testSelectWithLooping() 357 358 void testWhereWithLooping() { 359 def range = 1..3 360 361 def q = new HqlBuilder().query { 362 from 'Book as book' 363 where 'book.inStock = true' 364 range.each { 365 or "book.id = $it" 366 } 367 } 368 369 assert q.query == /from Book as book where book.inStock = true or book.id = 1 or book.id = 2 or book.id = 3/ 370 371 } // testWhereWithLooping() 372 373 void testWhereDirectlyWithLoops() { 374 def range = 1..3 375 376 def q = new HqlBuilder().query { 377 from 'Book as book' 378 where 379 range.each { 380 or "book.id = $it" 381 } 382 } 383 384 assert q.query == /from Book as book where book.id = 1 or book.id = 2 or book.id = 3/ 385 386 } // testWhereDirectlyWithLoops() 387 388 void testWhereNodeWithLoops() { 389 def range = 1..3 390 391 def q = new HqlBuilder().query { 392 from 'Book as book' 393 where { 394 range.each { 395 or "book.id = $it" 396 } 397 } 398 } 399 400 assert q.query == /from Book as book where ( book.id = 1 or book.id = 2 or book.id = 3 )/ 401 402 } // testWhereNodeWithLoops() 403 404 void testOrderByMultipleTerms() { 405 406 def q = new HqlBuilder().query { 407 from 'Book as book' 408 where 'book.id > 100' 409 order 'by book.name asc', 410 'book.id desc' 411 } 412 413 assert q.query == 'from Book as book where book.id > 100 order by book.name asc, book.id desc' 414 415 assert q.printFormattedQuery == 'from Book as book \nwhere book.id > 100 \norder by book.name asc, \n\tbook.id desc' 416 417 } // testOrderByMultipleTerms() 418 419 void testGroupByMultipleTerms() { 420 421 def q = new HqlBuilder().query { 422 from 'Book as book' 423 where 'book.id > 100' 424 group 'by book.name asc', 425 'book.id desc' 426 } 427 428 assert q.query == 'from Book as book where book.id > 100 group by book.name asc, book.id desc' 429 430 assert q.printFormattedQuery == 'from Book as book \nwhere book.id > 100 \ngroup by book.name asc, \n\tbook.id desc' 431 432 } // testGroupByMultipleTerms() 433 434 void testUpdate() { 435 def q = new HqlBuilder().query { 436 update 'Book b' 437 set 'b.name = :newName', 438 'b.inStock = true' 439 where 'b.name = :oldName' 440 } 441 442 assert q.query == 'update Book b set b.name = :newName, b.inStock = true where b.name = :oldName' 443 444 assert q.printFormattedQuery == 'update Book b \nset b.name = :newName, \n\tb.inStock = true \nwhere b.name = :oldName' 445 446 } // testUpdate() 447 448 void testDelete() { 449 def q = new HqlBuilder().query { 450 delete 'Book b' 451 where 'b.name = :oldName' 452 } 453 454 assert q.query == 'delete Book b where b.name = :oldName' 455 456 assert q.printFormattedQuery == 'delete Book b \nwhere b.name = :oldName' 457 458 } // testDelete() 459 460 void testInsertInto() { 461 def q = new HqlBuilder(debug:true).query { 462 insert 'into ArchiveBook (id, name)' 463 select 'b.id', 464 'b.name' 465 from 'Book b' 466 where 'b.name = :oldName' 467 } 468 469 assert q.query == 'insert into ArchiveBook (id, name) select b.id, b.name from Book b where b.name = :oldName' 470 471 assert q.printFormattedQuery == 'insert into ArchiveBook (id, name) \nselect b.id, \n\tb.name \nfrom Book b \nwhere b.name = :oldName' 472 473 } // testInsertInto() 122 474 123 475 } // end class
Note: See TracChangeset
for help on using the changeset viewer.