MentaBean

Rev

Rev 182 | Rev 204 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
149 erico 1
package org.mentabean.jdbc;
2
 
151 erico 3
import java.sql.PreparedStatement;
149 erico 4
import java.sql.ResultSet;
151 erico 5
import java.sql.SQLException;
6
import java.util.ArrayList;
152 erico 7
import java.util.HashMap;
149 erico 8
import java.util.Iterator;
151 erico 9
import java.util.List;
152 erico 10
import java.util.Map;
149 erico 11
 
12
import org.mentabean.BeanConfig;
13
import org.mentabean.BeanException;
14
import org.mentabean.DBField;
15
import org.mentabean.sql.Condition;
151 erico 16
import org.mentabean.sql.HasParams;
152 erico 17
import org.mentabean.sql.Sentence;
182 erico 18
import org.mentabean.sql.param.DefaultParamHandler;
151 erico 19
import org.mentabean.sql.param.Param;
182 erico 20
import org.mentabean.sql.param.ParamHandler;
152 erico 21
import org.mentabean.sql.param.ParamValue;
149 erico 22
import org.mentabean.util.PropertiesProxy;
151 erico 23
import org.mentabean.util.SQLUtils;
149 erico 24
 
25
/**
26
 * Fluent QueryBuilder useful to create SQL queries
27
 *
28
 * @author margel
29
 * @author erico
30
 *
31
 */
32
public class QueryBuilder {
33
 
163 erico 34
        private StringBuilder sb = new StringBuilder();
149 erico 35
        private final AnsiSQLBeanSession session;
151 erico 36
        private List<Object> paramValues = new ArrayList<Object>();
182 erico 37
        private List<Alias<?>> selectAliases;
38
        private List<Alias<?>> createdAliases = new ArrayList<Alias<?>>();
152 erico 39
        private Map<String, Sentence> sentences = new HashMap<String, Sentence>();
153 erico 40
        private Alias aliasFrom;
163 erico 41
        private int parenthesis = 0;
151 erico 42
        private boolean clauseIf;
182 erico 43
        private ParamHandler paramHandler;
149 erico 44
 
45
        protected QueryBuilder(final AnsiSQLBeanSession session) {
46
                this.session = session;
182 erico 47
                this.paramHandler = new DefaultParamHandler();
149 erico 48
        }
49
 
182 erico 50
        protected QueryBuilder(final AnsiSQLBeanSession session, ParamHandler paramHandler) {
51
                this.session = session;
52
                this.paramHandler = paramHandler;
53
        }
54
 
55
        public List<Alias<?>> getCreatedAliases() {
56
                return createdAliases;
57
        }
58
 
150 erico 59
        /**
161 erico 60
         * Builds an initial <i>SELECT</i> statement with given aliases
61
         * @param as - Alias(es) with properties that will be retrieved
62
         * @return A new <code>Select</code> object
150 erico 63
         */
149 erico 64
        public Select select(Alias<?>... as){
65
 
182 erico 66
                return new Select(null, as);
149 erico 67
        }
152 erico 68
 
182 erico 69
        public Select selectDistinct(Alias<?>... as){
70
 
71
                return new Select("DISTINCT", as);
72
        }
73
 
161 erico 74
        /**
168 erico 75
         * Builds an initial <i>SELECT alias FROM alias</i> statement.<br>
76
         * Same as <code>select(alias).from(alias)</code>
77
         * @param as - Alias with properties that will be retrieved
78
         * @return A new <code>From</code> object
79
         * @see #select(Alias...)
80
         * @see Select#from(Alias)
81
         */
82
        public From selectFrom(Alias<?> as){
83
 
169 erico 84
                return select(as).from(as);
168 erico 85
        }
86
 
182 erico 87
        public From selectDistinctFrom(Alias<?> as){
88
 
89
                return new Select("DISTINCT", as).from(as);
90
        }
91
 
168 erico 92
        /**
161 erico 93
         * Builds an initial <i>SELECT</i> statement with given sentences
94
         * @param sentences - Sentence(s) to insert in <i>SELECT</i> clause
95
         * @return A new <code>Select</code> object
96
         */
152 erico 97
        public Select select(Sentence... sentences){
98
 
182 erico 99
                return new Select(null, sentences);
152 erico 100
        }
182 erico 101
 
102
        public Select selectDistinct(Sentence... sentences){
103
 
104
                return new Select("DISTINCT", sentences);
105
        }
151 erico 106
 
150 erico 107
        /**
108
         * Creates a new QueryBuilder with the same session. It's useful to build sub-queries from the main query
109
         * @return The new QueryBuilder instance
110
         */
151 erico 111
        public QueryBuilder subQuery() {
182 erico 112
                QueryBuilder qb = new QueryBuilder(session);
113
                //qb.selectAliases = selectAliases;
114
                qb.createdAliases = createdAliases;
115
                return qb;
149 erico 116
        }
117
 
118
        /**
150 erico 119
         * Creates an alias to be used in this QueryBuilder
149 erico 120
         *
121
         * @param clazz - Bean class that will be mapped to a BeanConfig
122
         * @param alias - String indicating the alia's name
123
         * @return - The alias object
124
         */
169 erico 125
        public <T> Alias<T> aliasTo(Class<? extends T> clazz, String alias){
149 erico 126
 
127
                return new Alias<T>(clazz, alias);
128
        }
151 erico 129
 
149 erico 130
        /**
150 erico 131
         * Creates an alias to be used in this QueryBuilder. The alia's name is the simple name of the bean class
149 erico 132
         *
133
         * @param clazz - Bean class that will be mapped to a BeanConfig
134
         * @return - The alias object
135
         */
169 erico 136
        public <T> Alias<T> aliasTo(Class<? extends T> clazz){
151 erico 137
 
149 erico 138
                return new Alias<T>(clazz, clazz.getSimpleName().toLowerCase());
139
        }
151 erico 140
 
141
        private void appendTable(Alias<?> a) {
142
 
143
                sb.append(a.config.getTableName()).append(' ').append(a.aliasStr);
144
        }
145
 
152 erico 146
        private void append(Param param) {
151 erico 147
 
155 erico 148
                if (param != null)
149
                        sb.append(param.paramInQuery());
152 erico 150
                add(param);
149 erico 151
        }
152
 
182 erico 153
        private void add(Object param) {
149 erico 154
 
182 erico 155
                if (param instanceof HasParams) {
156
                        HasParams hasParams = (HasParams) param;
157
                        Param[] params = hasParams.getParams();
158
                        if (params != null) {
159
                                for (Param p : params) {
160
                                        add(p);
161
                                }
151 erico 162
                        }
182 erico 163
                }else {
164
                        Param p = paramHandler.findBetter(this, param);
165
                        if (p != null && p.values() != null && p.values().length > 0) {
166
 
167
                                for (Object value : p.values()) {
168
                                        if (value != null) {
169
                                                paramValues.add(value);
170
                                        }
171
                                }
172
                        }
149 erico 173
                }
174
        }
175
 
163 erico 176
        private void applyRegex() {
177
                remove("(AND|OR|\\([\\s]*?\\)|WHERE|HAVING)[\\s]*?$");
149 erico 178
        }
179
 
163 erico 180
        private void remove(String regex) {
181
 
182
                sb = new StringBuilder(sb.toString().replaceAll(regex, ""));
151 erico 183
 
163 erico 184
        }
185
 
186
        private void addAnd() {
187
                if (clauseIf) {
188
                        applyRegex();
189
                        sb.append(" AND ");
151 erico 190
                }
149 erico 191
        }
192
 
163 erico 193
        private void addOr() {
194
                if (clauseIf) {
195
                        applyRegex();
196
                        sb.append(" OR ");
197
                }
198
        }
199
 
200
        private void openPar() {
201
                parenthesis++;
202
                sb.append('(');
203
        }
204
 
205
        private void closePar() {
206
                applyRegex();
207
                parenthesis--;
208
                sb.append(')');
209
                applyRegex();
210
        }
211
 
182 erico 212
        private Alias<?> findAlias() {
213
 
214
                Object[] instances = PropertiesProxy.getBeanInstances();
215
                if (instances == null || instances.length == 0) throw new IllegalStateException("Cannot find proxy instance!");
216
 
217
                Object instance = instances[0];
218
 
219
                //TODO PropertiesProxy.addBeanInstance(instance);
220
 
221
                for (Alias<?> a : createdAliases) {
222
                        if (a.pxy() == instance)
223
                                return a;
224
                }
225
 
226
                throw new IllegalStateException("Cannot find alias for " + instance);
227
        }
228
 
149 erico 229
        public class Alias<T> {
230
 
231
                private BeanConfig config;
232
                private String aliasStr;
233
                private T pxy;
234
                private String[] returns;
235
                private String[] returnMinus;
155 erico 236
                private Map<Key, Alias> joined = new HashMap<Key, Alias>();
149 erico 237
 
169 erico 238
                private Alias(Class<? extends T> clazz, String aliasStr) {
149 erico 239
 
240
                        this.aliasStr = aliasStr;
241
                        this.config = session.getConfigFor(clazz);
151 erico 242
 
149 erico 243
                        if (this.config == null)
244
                                throw new BeanException("BeanConfig not found for "+clazz);
182 erico 245
 
246
                        this.pxy = (T) PropertiesProxy.create(config.getBeanClass());
247
 
248
                        createdAliases.add(this);
149 erico 249
                }
250
 
155 erico 251
                /**
252
                 * Defines the properties that will return. In other words, <b>only</b> these properties will be populated
253
                 * @param returns
254
                 */
149 erico 255
                public void setReturns(Object... returns){
151 erico 256
 
149 erico 257
                        this.returns = AnsiSQLBeanSession.getProperties(returns);
258
                }
151 erico 259
 
155 erico 260
                /**
261
                 * Defines the properties that will <b>NOT</b> return. In other words, these properties will be not populated
262
                 * @param returns
263
                 */
149 erico 264
                public void setReturnMinus(Object... returns){
151 erico 265
 
149 erico 266
                        this.returnMinus = AnsiSQLBeanSession.getProperties(returns);
267
                }
268
 
150 erico 269
                /**
182 erico 270
                 * Returns the proxy for bean class
161 erico 271
                 * @return The proxy
150 erico 272
                 */
149 erico 273
                public T pxy(){
274
 
275
                        return pxy;
276
                }
277
 
150 erico 278
                /**
279
                 * Convert the given property to database column
280
                 * @param property - The bean property (can be through proxy)
281
                 * @return The database column name
282
                 */
149 erico 283
                public String toColumn(Object property){
284
 
285
                        return session.propertyToColumn(config.getBeanClass(), property, aliasStr);
286
                }
287
 
150 erico 288
                /**
289
                 * Populates the bean according to ResultSet
290
                 * @param rs
291
                 * @param bean
292
                 */
149 erico 293
                public void populateBean(ResultSet rs, T bean){
294
 
150 erico 295
                        session.populateBeanImpl(rs, bean, aliasStr, returns, returnMinus, false);
149 erico 296
                }
153 erico 297
 
298
                public void populateAll(ResultSet rs, T bean) {
154 erico 299
 
155 erico 300
                        populateBean(rs, bean);
301
 
302
                        for (Map.Entry<Key, Alias> m : joined.entrySet()) {
303
 
304
                                //only if alias is in SELECT clause
182 erico 305
                                if (selectAliases.contains(m.getValue())) {
155 erico 306
 
169 erico 307
                                        Object value = session.getPropertyBean(bean, m.getKey().property, m.getKey().forceInstance);
155 erico 308
 
309
                                        if (value != null) {
310
                                                m.getValue().populateAll(rs, value);
153 erico 311
                                        }
312
                                }
155 erico 313
 
153 erico 314
                        }
315
                }
155 erico 316
 
317
                @Override
318
                public String toString() {
319
                        return "Alias "+aliasStr+" of "+config.getBeanClass();
320
                }
321
 
322
                /**
323
                 * Configures a property name to receive data from alias. When <code>forceIntance</code> is <b>true</b>
324
                 * it will force the creation of a new instance for the property, in other words,
325
                 * the value will never be <code>null</code>
326
                 * @param property - Bean property to populate
327
                 * @param forceInstance
328
                 * @param alias - Alias
329
                 */
330
                private void put(Object property, boolean forceInstance, Alias<?> alias) {
331
                        joined.put(new Key().property(property).forceInstance(forceInstance), alias);
332
                }
333
 
334
                private class Key {
335
 
336
                        private String property;
337
                        private boolean forceInstance;
338
 
339
                        public Key property(Object property) {
340
                                this.property = AnsiSQLBeanSession.getProperties(new Object[] {property})[0];
182 erico 341
                                //this.property = PropertiesProxy.getPropertyName();
155 erico 342
                                return this;
343
                        }
344
 
345
                        public Key forceInstance(boolean force) {
346
                                this.forceInstance = force;
347
                                return this;
348
                        }
349
                }
151 erico 350
 
149 erico 351
        }
352
 
353
        public class Select implements Appendable<Select> {
354
 
182 erico 355
                private boolean init = false;
356
 
357
                private Select(String str) {
358
                        sb.append("SELECT "+(str == null || str.isEmpty() ? "" : str+" "));
359
                        selectAliases = new ArrayList<Alias<?>>();
360
                }
361
 
362
                private Select(String str, Alias<?>... as) {
363
                        this(str);
152 erico 364
                        this.add(as);
365
                }
366
 
182 erico 367
                private Select(String str, Sentence... sentences) {
368
                        this(str);
152 erico 369
                        this.add(sentences);
370
                }
371
 
155 erico 372
                /**
373
                 * Add the sentences in <code>SELECT</code> clause
374
                 * @param sentences
375
                 * @return this
376
                 */
152 erico 377
                public Select add(Sentence... sentences) {
378
 
379
                        for (Sentence s : sentences) {
380
 
154 erico 381
                                if (s.getName() == null || s.getName().isEmpty())
382
                                        throw new BeanException("The sentence ("+s.build()+") in SELECT clause must have a name");
383
 
182 erico 384
                                if(init)
152 erico 385
                                        sb.append(",");
386
 
387
                                sb.append(s.build()).append(' ').append(s.getName());
388
                                QueryBuilder.this.sentences.put(s.getName(), s);
389
                                QueryBuilder.this.add(s);
182 erico 390
                                init = true;
152 erico 391
                        }
392
 
393
                        return this;
394
                }
395
 
155 erico 396
                /**
397
                 * Add the alias columns in <code>SELECT</code> clause
398
                 * @param as
399
                 * @return this
400
                 */
152 erico 401
                public Select add(Alias<?>... as) {
149 erico 402
 
403
                        for (Alias<?> alias: as) {
404
 
182 erico 405
                                if(init)
149 erico 406
                                        sb.append(",");
407
 
182 erico 408
                                selectAliases.add(alias);
152 erico 409
                                sb.append(session.buildSelectImpl(alias.config.getBeanClass(), alias.aliasStr, alias.returns, alias.returnMinus, false, true));
182 erico 410
                                init = true;
149 erico 411
                        }
152 erico 412
 
413
                        return this;
149 erico 414
                }
151 erico 415
 
155 erico 416
                /**
417
                 * Creates the <b>FROM</b> keyword for given alias appending the table name in SQL query.
418
                 * @param alias
161 erico 419
                 * @return A new <code>From</code> object
155 erico 420
                 */
149 erico 421
                public From from(Alias<?> alias){
151 erico 422
 
149 erico 423
                        return new From(alias);
424
                }
151 erico 425
 
149 erico 426
                @Override
152 erico 427
                public Select append(Param p) {
151 erico 428
 
152 erico 429
                        QueryBuilder.this.append(p);
149 erico 430
                        return this;
431
                }
432
        }
151 erico 433
 
152 erico 434
        public class From extends Query implements CanOrder, CanLimit, CanGroupBy, Appendable<From> {
151 erico 435
 
149 erico 436
                private From(Alias<?> alias) {
154 erico 437
 
438
                        aliasFrom = alias;
149 erico 439
                        sb.append(" FROM ").append(alias.config.getTableName()).append(" ").append(alias.aliasStr);
440
                }
151 erico 441
 
149 erico 442
                private From() {}
443
 
155 erico 444
                /**
445
                 * Creates a <b>LEFT JOIN</b> sentence using the given alias
446
                 * <br><br>
447
                 * <b>E.g.:</b>
448
                 * <ul>
449
                 * <code>
450
                 *      builder.select(...)<br><ul>
451
                 *              .from(a)<br>
452
                 *              .leftJoin(b)<br>
453
                 *              ...</ul>
454
                 * </code>
455
                 * </ul>
456
                 *
457
                 * @param a - The alias to join
458
                 * @return a new {@link On} object
459
                 */
149 erico 460
                public On leftJoin(Alias<?> a){
154 erico 461
                        return join(a, "LEFT JOIN");
149 erico 462
                }
151 erico 463
 
155 erico 464
                /**
465
                 * Creates a <b>RIGHT JOIN</b> sentence using the given alias
466
                 * <br><br>
467
                 * <b>E.g.:</b>
468
                 * <ul>
469
                 * <code>
470
                 *      builder.select(...)<br><ul>
471
                 *              .from(a)<br>
472
                 *              .rightJoin(b)<br>
473
                 *              ...</ul>
474
                 * </code>
475
                 * </ul>
476
                 *
477
                 * @param a - The alias to join
478
                 * @return a new {@link On} object
479
                 */
149 erico 480
                public On rightJoin(Alias<?> a){
154 erico 481
                        return join(a, "RIGHT JOIN");
149 erico 482
                }
151 erico 483
 
155 erico 484
                /**
485
                 * Creates a <b>JOIN</b> sentence using the given alias
486
                 * <br><br>
487
                 * <b>E.g.:</b>
488
                 * <ul>
489
                 * <code>
490
                 *      builder.select(...)<br><ul>
491
                 *              .from(a)<br>
492
                 *              .join(b)<br>
493
                 *              ...</ul>
494
                 * </code>
495
                 * </ul>
496
                 *
497
                 * @param a - The alias to join
498
                 * @return a new {@link On} object
499
                 */
149 erico 500
                public On join(Alias<?> a){
154 erico 501
                        return join(a, "JOIN");
502
                }
503
 
157 erico 504
                /**
505
                 * Creates a join using the given join type
506
                 * @param a - Alias to join
507
                 * @param joinType - The join type (E.g: <code>"CROSS JOIN"</code> or <code>"LEFT OUTER JOIN"</code>)
508
                 * @return a new {@link On} object
509
                 */
510
                public On join(Alias<?> a, String joinType) {
154 erico 511
                        sb.append(" "+joinType+" ");
149 erico 512
                        appendTable(a);
154 erico 513
                        return new On(a, true);                
149 erico 514
                }
515
 
516
                public Where where() {
517
                        return new Where(true);
518
                }
519
 
520
                @Override
151 erico 521
                public Order orderBy() {
522
 
523
                        return new Order();
149 erico 524
                }
525
 
526
                @Override
182 erico 527
                public Limit limit(Object lim) {
149 erico 528
 
151 erico 529
                        return new Limit(lim);
149 erico 530
                }
531
 
532
                @Override
152 erico 533
                public From append(Param p) {
151 erico 534
 
152 erico 535
                        QueryBuilder.this.append(p);
149 erico 536
                        return this;
537
                }
152 erico 538
 
539
                @Override
540
                public GroupBy groupBy(Alias<?>... aliases) {
541
                        return new GroupBy(aliases);
542
                }
543
 
544
                @Override
159 erico 545
                public GroupBy groupByProp(Alias<?> alias, Object... properties) {
152 erico 546
                        return new GroupBy(alias, properties);
547
                }
548
 
549
                @Override
550
                public GroupBy groupBy() {
551
                        return new GroupBy();
552
                }
181 erico 553
 
554
                @Override
555
                public GroupBy groupBy(Param... p) {
556
                        return new GroupBy(p);
557
                }
149 erico 558
        }
151 erico 559
 
155 erico 560
        /**
561
         * Class representing a 'pos-join' operation
562
         * @author erico
563
         *
564
         */
149 erico 565
        public class On {
151 erico 566
 
153 erico 567
                private Alias<?> aliasPK;
151 erico 568
 
153 erico 569
                private On(Alias<?> aliasPK, boolean init) {
151 erico 570
 
149 erico 571
                        sb.append(init ? " ON " : " AND ");
153 erico 572
                        this.aliasPK = aliasPK;
149 erico 573
                }
151 erico 574
 
149 erico 575
                public OnEquals on(Object property) {
153 erico 576
                        sb.append(aliasPK.toColumn(property));
577
                        return new OnEquals(aliasPK);
149 erico 578
                }
151 erico 579
 
155 erico 580
                public UsingPK pkOf(Alias<?> alias) {
151 erico 581
                        return new UsingPK(alias);
582
                }
583
 
584
        }
585
 
586
        public class UsingPK {
587
 
588
                private Alias<?> aliasPK;
149 erico 589
 
151 erico 590
                private UsingPK(Alias<?> aliasPK) {
591
                        this.aliasPK = aliasPK;
592
                }
593
 
153 erico 594
                public PopulateUsing in(Alias<?> aliasFK) {
151 erico 595
                        buildOn(aliasPK, aliasFK);
153 erico 596
                        return new PopulateUsing(aliasPK, aliasFK);
151 erico 597
                }
598
 
599
                private void buildOn(Alias<?> aPK, Alias<?> aFK){
600
 
601
                        Iterator<DBField> pks = aPK.config.pks();
602
                        DBField df = pks.next();
603
 
604
                        sb.append(aPK.aliasStr).append(".").append(df.getDbName()).append(" = ")
605
                        .append(aFK.aliasStr).append(".").append(df.getDbName());
606
 
607
                        while (pks.hasNext()){
608
 
609
                                df = pks.next();
610
 
611
                                sb.append(" AND ").append(aPK.aliasStr).append(".").append(df.getDbName())
612
                                .append(" = ").append(aFK.aliasStr).append(".").append(df.getDbName());
613
 
614
                        }
615
                }
149 erico 616
        }
153 erico 617
 
618
        public class PopulateUsing extends From {
619
 
620
                private Alias<?> aliasPK, aliasFK;
621
 
622
                public PopulateUsing(Alias<?> aliasPK, Alias<?> aliasFK) {
623
                        this.aliasFK = aliasFK;
624
                        this.aliasPK = aliasPK;
625
                }
626
 
155 erico 627
                /**
628
                 * Defines the property of foreign bean <b>(specified on {@link UsingPK#in(Alias)} method)</b>
629
                 * that will receive the value from alias PK <b>(specified on {@link On#pkOf(Alias)} method)</b> and
630
                 * force the creation of a new instance if bean property is not set.
631
                 * <br><br>
632
                 * <b>E.g.:</b>
633
                 * <ul>
634
                 * <code>
635
                 *      builder.select(user, city)<br><ul>
636
                 *              .from(user)<br>
637
                 *              .join(city)<br>
638
                 *              .pkOf(city).in(user)<br>
639
                 *              <b>.inPropertyForcingInstance(user.pxy().getCity())</b><br>
640
                 *              ...</ul>
641
                 * </code>
642
                 * </ul>
643
                 * @param propertyBean
644
                 * @return this
645
                 * @see Alias#put(Object, boolean, Alias)
646
                 */
647
                public From inPropertyForcingInstance(Object propertyBean) {
648
                        aliasFK.put(propertyBean, true, aliasPK);
649
                        return this;
650
                }
651
 
652
                /**
653
                 * Defines the property of foreign bean <b>(specified on {@link UsingPK#in(Alias)} method)</b>
654
                 * that will receive the value from alias PK <b>(specified on {@link On#pkOf(Alias)} method)</b>
655
                 * <br><br>
656
                 * <b>E.g.:</b>
657
                 * <ul>
658
                 * <code>
659
                 *      builder.select(user, city)<br><ul>
660
                 *              .from(user)<br>
661
                 *              .join(city)<br>
662
                 *              .pkOf(city).in(user)<br>
663
                 *              <b>.inProperty(user.pxy().getCity())</b><br>
664
                 *              ...</ul>
665
                 * </code>
666
                 * </ul>
667
                 * @param propertyBean
668
                 * @return this
669
                 * @see Alias#put(Object, boolean, Alias)
670
                 */
154 erico 671
                public From inProperty(Object propertyBean) {
155 erico 672
                        aliasFK.put(propertyBean, false, aliasPK);
153 erico 673
                        return this;
674
                }
155 erico 675
 
676
                /**
677
                 * Defines the property of primary bean <b>(specified on {@link On#pkOf(Alias)} method)</b>
678
                 * that will receive the value from foreign alias (aliasFK) <b>(specified on {@link UsingPK#in(Alias)} method)</b> and
679
                 * force the creation of a new instance if bean property is not set.
680
                 * <br><br><b>Note: </b>The <b>pkProperty</b> is generally used in 1x1 relationship.
681
                 * <br><br>
682
                 * <b>E.g.:</b>
683
                 * <ul>
684
                 * <code>
685
                 *      builder.select(person, identity)<br><ul>
686
                 *              .from(person)<br>
687
                 *              .join(identity)<br>
688
                 *              .pkOf(person).in(identity)<br>
689
                 *              <b>.pkPropertyForcingInstance(person.pxy().getIdentity())</b><br>
690
                 *              ...</ul>
691
                 * </code>
692
                 * </ul>
693
                 * @param propertyBean
694
                 * @return this
695
                 * @see Alias#put(Object, boolean, Alias)
696
                 */
697
                public From pkPropertyForcingInstance(Object propertyBean) {
698
                        aliasPK.put(propertyBean, true, aliasFK);
699
                        return this;
700
                }
701
 
702
                /**
703
                 * Defines the property of primary bean <b>(specified on {@link On#pkOf(Alias)} method)</b>
704
                 * that will receive the value from foreign alias (aliasFK) <b>(specified on {@link UsingPK#in(Alias)} method)</b>
705
                 * <br><br><b>Note: </b>The <b>pkProperty</b> is generally used in 1x1 relationship.
706
                 * <br><br>
707
                 * <b>E.g.:</b>
708
                 * <ul>
709
                 * <code>
710
                 *      builder.select(person, identity)<br><ul>
711
                 *              .from(person)<br>
712
                 *              .join(identity)<br>
713
                 *              .pkOf(person).in(identity)<br>
714
                 *              <b>.pkProperty(person.pxy().getIdentity())</b><br>
715
                 *              ...</ul>
716
                 * </code>
717
                 * </ul>
718
                 * @param propertyBean
719
                 * @return this
720
                 * @see Alias#put(Object, boolean, Alias)
721
                 */
722
                public From pkProperty(Object propertyBean) {
723
                        aliasPK.put(propertyBean, false, aliasFK);
724
                        return this;
725
                }
153 erico 726
        }
727
 
149 erico 728
        public class OnEquals {
151 erico 729
 
153 erico 730
                private Alias<?> aliasPK;
151 erico 731
 
153 erico 732
                private OnEquals(Alias<?> aliasPK) {
149 erico 733
                        sb.append('=');
153 erico 734
                        this.aliasPK = aliasPK;
149 erico 735
                }
151 erico 736
 
155 erico 737
                /**
182 erico 738
                 * Equals the given property to the property defined in <b>on</b> method
155 erico 739
                 * <br><br>
740
                 * <b>E.g.:</b>
741
                 * <ul>
742
                 * <code>
743
                 *      builder.select(a, b)<br><ul>
744
                 *              .from(a)<br>
745
                 *              .join(b)<br>
746
                 *              .on(b.pxy().getSomething())<br>
182 erico 747
                 *              <b>.eq(a.pxy().getSomething())</b><br>
155 erico 748
                 *              ...</ul>
749
                 * </code>
750
                 * </ul>
751
                 * @param property
161 erico 752
                 * @return A new <code>Equals</code> object
155 erico 753
                 */
182 erico 754
                public Equals eq(Object property) {
153 erico 755
 
182 erico 756
                        Alias<?> alias = findAlias();
757
 
155 erico 758
                        sb.append(alias.toColumn(property));
759
                        return new Equals(this.aliasPK, alias);
149 erico 760
                }
153 erico 761
 
149 erico 762
        }
153 erico 763
 
149 erico 764
        public class Equals extends From {
151 erico 765
 
153 erico 766
                private Alias<?> aliasFK, aliasPK;
151 erico 767
 
153 erico 768
                private Equals(Alias<?> aliasPK, Alias<?> aliasFK) {
769
                        this.aliasPK = aliasPK;
770
                        this.aliasFK = aliasFK;
149 erico 771
                }
153 erico 772
 
155 erico 773
                /**
774
                 * Defines the property of bean <b>specified as alias on {@link OnEquals#eq(Alias, Object)} method</b>
775
                 * that will receive the value from alias <b>specified on {@link From#join(Alias)} method</b> and
776
                 * force the creation of a new instance if bean property is not set.
777
                 * <br><br>
778
                 * <b>E.g.:</b>
779
                 * <ul>
780
                 * <code>
781
                 *      builder.select(user, city)<br><ul>
782
                 *              .from(user)<br>
783
                 *              .join(city)<br>
784
                 *              .on(city.pxy().getId())<br>
785
                 *              .eq(user, user.pxy().getCity().getId())<br>
786
                 *              <b>.inPropertyForcingInstance(user.pxy().getCity())</b><br>
787
                 *              ...</ul>
788
                 * </code>
789
                 * </ul>
790
                 * @param propertyBean
791
                 * @return this
792
                 * @see Alias#put(Object, boolean, Alias)
793
                 */
794
                public Equals eqPropertyForcingInstance(Object propertyBean) {
795
                        aliasFK.put(propertyBean, true, aliasPK);
153 erico 796
                        return this;
797
                }
155 erico 798
 
799
                /**
800
                 * Defines the property of bean <b>specified as alias on {@link OnEquals#eq(Alias, Object)} method</b>
801
                 * that will receive the value from alias <b>specified on {@link From#join(Alias)} method</b>
802
                 * <br><br>
803
                 * <b>E.g.:</b>
804
                 * <ul>
805
                 * <code>
806
                 *      builder.select(user, city)<br><ul>
807
                 *              .from(user)<br>
808
                 *              .join(city)<br>
809
                 *              .on(city.pxy().getId())<br>
810
                 *              .eq(user, user.pxy().getCity().getId())<br>
811
                 *              <b>.inProperty(user.pxy().getCity())</b><br>
812
                 *              ...</ul>
813
                 * </code>
814
                 * </ul>
815
                 * @param propertyBean
816
                 * @return this
817
                 * @see Alias#put(Object, boolean, Alias)
818
                 */
819
                public Equals eqProperty(Object propertyBean) {
820
                        aliasFK.put(propertyBean, false, aliasPK);
821
                        return this;
822
                }
151 erico 823
 
149 erico 824
                public OnEquals and(Object property) {
153 erico 825
                        return new On(aliasPK, false).on(property);
149 erico 826
                }
827
        }
151 erico 828
 
155 erico 829
        public class Where implements Appendable<Where>, HasInitClause<InitClauseWhere> {
151 erico 830
 
149 erico 831
                private Where (boolean init) {
832
                        if (init) {
833
                                sb.append(" WHERE ");
834
                        }
835
                }
155 erico 836
 
837
                @Override
182 erico 838
                public InitClauseWhere clauseIf(boolean clauseIf, Object param) {
155 erico 839
                        QueryBuilder.this.clauseIf = clauseIf;
840
                        return new InitClauseWhere(param);
841
                }
151 erico 842
 
150 erico 843
                @Override
152 erico 844
                public Where append(Param p) {
151 erico 845
 
152 erico 846
                        QueryBuilder.this.append(p);
149 erico 847
                        return this;
848
                }
151 erico 849
 
850
                @Override
182 erico 851
                public InitClauseWhere clause(Object param) {
155 erico 852
                        return clauseIf(true, param);
853
                }
151 erico 854
 
163 erico 855
                /**
856
                 * Insert a left parenthesis '(' in query
857
                 * @return this
858
                 */
859
                public Where openPar() {
860
                        QueryBuilder.this.openPar();
861
                        return this;
862
                }
151 erico 863
 
149 erico 864
        }
151 erico 865
 
150 erico 866
        public class InitClauseWhere extends InitClause implements Appendable<InitClauseWhere>, HasEndClause<EndClauseWhere> {
151 erico 867
 
182 erico 868
                private InitClauseWhere(Object param) {
155 erico 869
 
870
                        super(param);
871
                }
156 erico 872
 
149 erico 873
                @Override
152 erico 874
                public InitClauseWhere append(Param p) {
151 erico 875
 
152 erico 876
                        QueryBuilder.this.append(p);
149 erico 877
                        return this;
878
                }
151 erico 879
 
150 erico 880
                @Override
881
                public EndClauseWhere condition(String condition) {
151 erico 882
 
150 erico 883
                        return new EndClauseWhere(condition);
149 erico 884
                }
151 erico 885
 
150 erico 886
                @Override
887
                public EndClauseWhere condition(Condition condition) {
151 erico 888
 
150 erico 889
                        return new EndClauseWhere(condition);
149 erico 890
                }
151 erico 891
 
149 erico 892
        }
151 erico 893
 
150 erico 894
        public class InitClauseHaving extends InitClause implements Appendable<InitClauseHaving>, HasEndClause<EndClauseHaving> {
155 erico 895
 
182 erico 896
                private InitClauseHaving(Object param) {
155 erico 897
 
898
                        super(param);
899
                }
151 erico 900
 
150 erico 901
                @Override
152 erico 902
                public InitClauseHaving append(Param p) {
151 erico 903
 
152 erico 904
                        QueryBuilder.this.append(p);
150 erico 905
                        return this;
906
                }
151 erico 907
 
150 erico 908
                @Override
909
                public EndClauseHaving condition(String condition) {
151 erico 910
 
150 erico 911
                        return new EndClauseHaving(condition);
912
                }
151 erico 913
 
150 erico 914
                @Override
915
                public EndClauseHaving condition(Condition condition) {
151 erico 916
 
150 erico 917
                        return new EndClauseHaving(condition);
918
                }
151 erico 919
 
150 erico 920
        }
151 erico 921
 
150 erico 922
        public class InitClause {
155 erico 923
 
182 erico 924
                private InitClause (Object param) {
155 erico 925
 
182 erico 926
                        Param p = paramHandler.findBetter(QueryBuilder.this, param);
927
 
151 erico 928
                        if (clauseIf) {
182 erico 929
//                              System.out.println(p.getClass()+" - "+p.paramInQuery());
930
//                              if (p.values() != null)
931
//                              for (Object o : p.values()) {
932
//                                      if (o != null)
933
//                                      System.out.println("type: "+o.getClass());
934
//                              }
935
 
936
                                add(p);
937
                                sb.append(' ').append(p.paramInQuery());
151 erico 938
                        }
155 erico 939
 
150 erico 940
                }
151 erico 941
 
150 erico 942
        }
151 erico 943
 
152 erico 944
        public class EndClauseWhere extends EndClause implements Appendable<EndClauseWhere>, CanGroupBy {
151 erico 945
 
150 erico 946
                private EndClauseWhere(Condition condition) {
151 erico 947
 
948
                        if (clauseIf) {
949
                                add(condition);
950
                                init(condition.build());
951
                        }
150 erico 952
                }
151 erico 953
 
150 erico 954
                private EndClauseWhere(String condition) {
151 erico 955
 
956
                        if (clauseIf) {
957
                                init(condition);
958
                        }
150 erico 959
                }
163 erico 960
 
961
                /**
962
                 * Insert a left parenthesis '(' in query
963
                 * @return this
964
                 */
965
                public EndClauseWhere openPar() {
966
                        QueryBuilder.this.openPar();
967
                        return this;
968
                }
969
 
970
                /**
971
                 * Insert a right parenthesis ')' in query
972
                 * @return this
973
                 */
974
                public EndClauseWhere closePar() {
975
                        QueryBuilder.this.closePar();
976
                        return this;
977
                }
151 erico 978
 
149 erico 979
                public Where and() {
151 erico 980
 
163 erico 981
                        addAnd();
149 erico 982
                        return new Where(false);
983
                }
151 erico 984
 
149 erico 985
                public Where or() {
156 erico 986
 
163 erico 987
                        addOr();
149 erico 988
                        return new Where(false);
989
                }
151 erico 990
 
149 erico 991
                @Override
152 erico 992
                public EndClauseWhere append(Param p) {
151 erico 993
 
152 erico 994
                        QueryBuilder.this.append(p);
150 erico 995
                        return this;
996
                }
151 erico 997
 
152 erico 998
                @Override
999
                public GroupBy groupBy(Alias<?>... aliases) {
1000
                        return new GroupBy(aliases);
1001
                }
181 erico 1002
 
1003
                @Override
1004
                public GroupBy groupBy(Param... params) {
1005
                        return new GroupBy(params);
1006
                }
152 erico 1007
 
1008
                @Override
159 erico 1009
                public GroupBy groupByProp(Alias<?> alias, Object... properties) {
152 erico 1010
                        return new GroupBy(alias, properties);
1011
                }
1012
 
1013
                @Override
1014
                public GroupBy groupBy() {
1015
                        return new GroupBy();
1016
                }
1017
 
150 erico 1018
        }
151 erico 1019
 
150 erico 1020
        public class EndClauseHaving extends EndClause implements Appendable<EndClauseHaving> {
151 erico 1021
 
150 erico 1022
                private EndClauseHaving(Condition condition) {
151 erico 1023
 
1024
                        if (clauseIf) {
1025
                                add(condition);
1026
                                init(condition.build());
1027
                        }
150 erico 1028
                }
151 erico 1029
 
150 erico 1030
                private EndClauseHaving(String condition) {
151 erico 1031
 
1032
                        if (clauseIf) {
1033
                                init(condition);
1034
                        }
150 erico 1035
                }
163 erico 1036
 
1037
                /**
1038
                 * Insert a left parenthesis '(' in query
1039
                 * @return this
1040
                 */
1041
                public EndClauseHaving openPar() {
1042
 
1043
                        QueryBuilder.this.openPar();
1044
                        return this;
1045
                }
1046
 
1047
                /**
1048
                 * Insert a right parenthesis ')' in query
1049
                 * @return this
1050
                 */
1051
                public EndClauseHaving closePar() {
1052
 
1053
                        QueryBuilder.this.closePar();
1054
                        return this;
1055
                }
151 erico 1056
 
150 erico 1057
                public Having and() {
155 erico 1058
 
163 erico 1059
                        addAnd();
150 erico 1060
                        return new Having(false);
1061
                }
151 erico 1062
 
150 erico 1063
                public Having or() {
155 erico 1064
 
163 erico 1065
                        addOr();
150 erico 1066
                        return new Having(false);
1067
                }
151 erico 1068
 
150 erico 1069
                @Override
152 erico 1070
                public EndClauseHaving append(Param p) {
151 erico 1071
 
152 erico 1072
                        QueryBuilder.this.append(p);
150 erico 1073
                        return this;
1074
                }
151 erico 1075
 
150 erico 1076
        }
151 erico 1077
 
1078
        public abstract class EndClause extends Query implements CanOrder, CanLimit {
1079
 
150 erico 1080
                protected void init(String condition) {
151 erico 1081
 
150 erico 1082
                        sb.append(' ').append(condition);
1083
                }
151 erico 1084
 
150 erico 1085
                @Override
149 erico 1086
                public Order orderBy() {
151 erico 1087
 
1088
                        return new Order();
149 erico 1089
                }
1090
 
1091
                @Override
182 erico 1092
                public Limit limit(Object lim) {
151 erico 1093
 
1094
                        return new Limit(lim);
149 erico 1095
                }
1096
 
151 erico 1097
        }
149 erico 1098
 
151 erico 1099
        public class Limit extends Query implements CanOrder, Appendable<Limit> {
1100
 
182 erico 1101
                private Limit(Object lim) {
151 erico 1102
 
182 erico 1103
                        Param param = paramHandler.findBetter(QueryBuilder.this, lim);
1104
 
1105
                        if (param != null) {
163 erico 1106
                                applyRegex();
182 erico 1107
                                add(param);
1108
 
1109
                                Object numberObj = paramValues.get(paramValues.size()-1);
1110
                                if (numberObj instanceof Number) {
1111
                                        Number numberLimit = (Number) numberObj;
1112
                                        if (numberLimit.longValue() <= 0) {
1113
                                                paramValues.remove(paramValues.size()-1);
1114
                                                return;
1115
                                        }
1116
                                }
1117
 
1118
                                sb.append(" LIMIT ").append(param.paramInQuery());
151 erico 1119
                        }
149 erico 1120
                }
152 erico 1121
 
149 erico 1122
                @Override
1123
                public Order orderBy() {
151 erico 1124
 
1125
                        return new Order();
149 erico 1126
                }
151 erico 1127
 
1128
                public Offset offset(Integer offset) {
1129
 
149 erico 1130
                        return new Offset(offset);
1131
                }
1132
 
156 erico 1133
                public Offset offset(Param param) {
149 erico 1134
 
156 erico 1135
                        return new Offset(param);
149 erico 1136
                }
1137
 
1138
                @Override
152 erico 1139
                public Limit append(Param p) {
151 erico 1140
 
152 erico 1141
                        QueryBuilder.this.append(p);
149 erico 1142
                        return this;
1143
                }
1144
        }
1145
 
151 erico 1146
        public class Offset extends Query implements Appendable<Offset> {
1147
 
163 erico 1148
                public Offset(Number offset) {
1149
                        this(offset != null && offset.longValue() > 0 ? new ParamValue(offset) : null);
149 erico 1150
                }
1151
 
156 erico 1152
                public Offset(Param param) {
149 erico 1153
 
163 erico 1154
                        if (param != null) {
1155
                                add(param);
1156
                                sb.append(" OFFSET ").append(param.paramInQuery());
1157
                        }
149 erico 1158
                }
151 erico 1159
 
1160
                public Order orderBy() {
1161
 
1162
                        return new Order();
1163
                }
1164
 
149 erico 1165
                @Override
152 erico 1166
                public Offset append(Param p) {
151 erico 1167
 
152 erico 1168
                        QueryBuilder.this.append(p);
149 erico 1169
                        return this;
1170
                }
1171
 
1172
        }
151 erico 1173
 
154 erico 1174
        public class Order implements Appendable<Order> {
151 erico 1175
 
149 erico 1176
                private Order() {
151 erico 1177
 
163 erico 1178
                        applyRegex();
149 erico 1179
                        sb.append(" ORDER BY ");                       
1180
                }
154 erico 1181
 
1182
                public Ordering asc(Param param) {
1183
 
1184
                        return new Ordering().asc(param);
1185
                }
1186
 
1187
                public Ordering desc(Param param) {
1188
 
1189
                        return new Ordering().desc(param);
1190
                }
151 erico 1191
 
154 erico 1192
                public Ordering asc(Alias<?> alias, Object... properties){
1193
 
1194
                        return new Ordering().asc(alias, properties);
1195
                }
1196
 
1197
                public Ordering desc(Alias<?> alias, Object... properties){
1198
 
1199
                        return new Ordering().desc(alias, properties);
1200
                }
1201
 
1202
                @Override
1203
                public Order append(Param p) {
1204
 
1205
                        QueryBuilder.this.append(p);
1206
                        return this;
1207
                }
1208
        }
1209
 
1210
        public class Ordering extends Query implements CanLimit, Appendable<Ordering> {
1211
 
1212
                private boolean alreadyOrder = false;
1213
 
1214
                private void initOrder() {
1215
 
1216
                        if (alreadyOrder) {
1217
                                sb.append(',');
1218
                        }
1219
 
1220
                        alreadyOrder = true;
1221
                }
1222
 
1223
                public Ordering asc(Param param) {
1224
 
1225
                        initOrder();
1226
                        add(param);
1227
                        sb.append(param.paramInQuery()).append(" ASC ");
1228
                        return this;
1229
                }
1230
 
1231
                public Ordering desc(Param param) {
1232
 
1233
                        initOrder();
1234
                        add(param);
1235
                        sb.append(param.paramInQuery()).append(" DESC ");
1236
                        return this;
1237
                }
1238
 
1239
                public Ordering asc(Alias<?> alias, Object... properties){
149 erico 1240
                        iterateOrderBy(" ASC ", alias, properties);
1241
                        return this;
1242
                }
1243
 
154 erico 1244
                public Ordering desc(Alias<?> alias, Object... properties){
149 erico 1245
                        iterateOrderBy(" DESC ", alias, properties);
1246
                        return this;
1247
                }
154 erico 1248
 
149 erico 1249
                @Override
182 erico 1250
                public Limit limit(Object lim) {
151 erico 1251
 
1252
                        return new Limit(lim);
149 erico 1253
                }
1254
 
1255
                private void iterateOrderBy(String orderType, Alias<?> alias, Object[] properties){
151 erico 1256
 
149 erico 1257
                        String[] props = AnsiSQLBeanSession.getProperties(properties);
151 erico 1258
 
154 erico 1259
                        initOrder();
151 erico 1260
 
149 erico 1261
                        for(String prop : props){
151 erico 1262
 
149 erico 1263
                                sb.append(alias.toColumn(prop)).append(orderType).append(",");
1264
                        }
151 erico 1265
 
149 erico 1266
                        sb.setCharAt(sb.length()-1, ' ');
1267
                }
1268
 
1269
                @Override
154 erico 1270
                public Ordering append(Param p) {
152 erico 1271
                        QueryBuilder.this.append(p);
149 erico 1272
                        return this;
1273
                }
1274
        }
151 erico 1275
 
152 erico 1276
        public class GroupBy extends Query implements Appendable<GroupBy>, CanLimit, CanOrder {
151 erico 1277
 
150 erico 1278
                private void init() {
152 erico 1279
 
163 erico 1280
                        applyRegex();
150 erico 1281
                        sb.append(!sb.toString().endsWith(" GROUP BY ") ? " GROUP BY " : ",");
1282
                }
151 erico 1283
 
152 erico 1284
                private GroupBy(Alias<?> alias, Object... properties) {
1285
                        init();
150 erico 1286
                        add(alias, properties);
1287
                }
151 erico 1288
 
152 erico 1289
                private GroupBy(Alias<?>... alias) {
1290
                        init();
150 erico 1291
                        add(alias);
1292
                }
152 erico 1293
 
181 erico 1294
                private GroupBy(Param... params) {
1295
                        init();
1296
                        add(params);
1297
                }
1298
 
152 erico 1299
                private GroupBy() {
1300
                        init();
182 erico 1301
                        add(selectAliases.toArray(new Alias<?>[0]));
152 erico 1302
                }
151 erico 1303
 
150 erico 1304
                public GroupBy add(Alias<?> alias, Object... properties) {
152 erico 1305
 
156 erico 1306
                        if (!sb.toString().endsWith(" GROUP BY ")) {
152 erico 1307
                                sb.append(',');
156 erico 1308
                        }
1309
 
152 erico 1310
                        sb.append(session.buildSelectImpl(alias.config.getBeanClass(), alias.aliasStr,
1311
                                        AnsiSQLBeanSession.getProperties(properties), null, false, false));
156 erico 1312
 
150 erico 1313
                        return this;
1314
                }
151 erico 1315
 
152 erico 1316
                public GroupBy add(Alias<?>... aliases) {
1317
 
1318
                        for (Alias<?> alias : aliases) {
1319
 
1320
                                if (!sb.toString().endsWith(" GROUP BY "))
1321
                                        sb.append(',');
1322
 
1323
                                sb.append(session.buildSelectImpl(alias.config.getBeanClass(), alias.aliasStr,
1324
                                                alias.returns, alias.returnMinus, false, false));
1325
                        }
1326
 
150 erico 1327
                        return this;
1328
                }
181 erico 1329
 
1330
                public GroupBy add(Param... params) {
1331
 
1332
                        for (Param p : params) {
1333
                                if (!sb.toString().endsWith(" GROUP BY "))
1334
                                        sb.append(',');
1335
 
1336
                                append(p);
1337
                        }
1338
 
1339
                        return this;
1340
                }
151 erico 1341
 
150 erico 1342
                public Having having() {
1343
                        return new Having(true);
1344
                }
1345
 
1346
                @Override
152 erico 1347
                public GroupBy append(Param p) {
1348
                        QueryBuilder.this.append(p);
150 erico 1349
                        return this;
1350
                }
152 erico 1351
 
1352
                @Override
1353
                public Order orderBy() {
1354
                        return new Order();
1355
                }
1356
 
1357
                @Override
182 erico 1358
                public Limit limit(Object lim) {
152 erico 1359
                        return new Limit(lim);
1360
                }
1361
 
149 erico 1362
        }
151 erico 1363
 
155 erico 1364
        public class Having extends Query implements Appendable<Having>, HasInitClause<InitClauseHaving>{
151 erico 1365
 
150 erico 1366
                private Having (boolean init) {
1367
                        if (init) {
1368
                                sb.append(" HAVING ");
1369
                        }
1370
                }
151 erico 1371
 
150 erico 1372
                @Override
182 erico 1373
                public InitClauseHaving clauseIf(boolean clauseIf, Object param){
156 erico 1374
 
155 erico 1375
                        QueryBuilder.this.clauseIf = clauseIf;
1376
                        return new InitClauseHaving(param);
1377
                }
1378
 
1379
                @Override
152 erico 1380
                public Having append(Param p) {
1381
                        QueryBuilder.this.append(p);
150 erico 1382
                        return this;
1383
                }
1384
 
1385
                @Override
182 erico 1386
                public InitClauseHaving clause(Object param) {
155 erico 1387
                        return clauseIf(true, param);
1388
                }
151 erico 1389
 
163 erico 1390
                /**
1391
                 * Insert a left parenthesis '(' in query
1392
                 * @return this
1393
                 */
1394
                public Having openPar() {
1395
                        QueryBuilder.this.openPar();
1396
                        return this;
1397
                }
151 erico 1398
 
150 erico 1399
        }
151 erico 1400
 
155 erico 1401
        public interface HasInitClause<T extends InitClause> {
151 erico 1402
 
155 erico 1403
                /**
1404
                 * Insert the param as a clause in query (same of <code>clauseIf(true, param)</code>).
1405
                 * <p>
1406
                 * <b>E.g.:</b>
1407
                 * <ul>
1408
                 * <code>
1409
                 *              ...<br>
1410
                 *              .where()<br>
1411
                 *              .clause(new ParamNative("anything"))<br>
1412
                 *              .condition(...)<br>
1413
                 *              ...<br>
1414
                 *
1415
                 * </code>
1416
                 * </ul>
1417
                 * </p>
1418
                 * @param param
1419
                 */
182 erico 1420
                public T clause(Object param);
155 erico 1421
 
1422
                /**
1423
                 * Insert the param as a clause in query if and only if the flag <b>clauseIf</b> is <b>true</b>
1424
                 * @param clauseIf - Flag indicating if this clause will be inserted in SQL query
1425
                 * @param param
1426
                 * @see #clause(Param)
1427
                 */
182 erico 1428
                public T clauseIf(boolean clauseIf, Object param);
150 erico 1429
 
1430
        }
151 erico 1431
 
150 erico 1432
        public interface HasEndClause<T extends EndClause> {
151 erico 1433
 
150 erico 1434
                public T condition(String condition);
151 erico 1435
 
150 erico 1436
                public T condition(Condition condition);
151 erico 1437
 
150 erico 1438
        }
151 erico 1439
 
149 erico 1440
        public interface CanOrder {
151 erico 1441
 
149 erico 1442
                public Order orderBy();
1443
        }
151 erico 1444
 
149 erico 1445
        public interface CanLimit {
151 erico 1446
 
182 erico 1447
                public Limit limit(Object lim);
149 erico 1448
        }
151 erico 1449
 
150 erico 1450
        public interface CanGroupBy {
151 erico 1451
 
152 erico 1452
                public GroupBy groupBy(Alias<?>... aliases);
181 erico 1453
 
1454
                public GroupBy groupBy(Param... p);
151 erico 1455
 
159 erico 1456
                public GroupBy groupByProp(Alias<?> alias, Object... properties);
152 erico 1457
 
156 erico 1458
                /**
1459
                 * Group by all aliases fields used in <i>SELECT</i> clause
1460
                 * @return this
1461
                 */
152 erico 1462
                public GroupBy groupBy();
1463
 
150 erico 1464
        }
151 erico 1465
 
149 erico 1466
        public interface Appendable<T> {
151 erico 1467
 
156 erico 1468
                /**
1469
                 * Appends the parameter directly in query
1470
                 * @param p - Param
1471
                 * @return this
1472
                 */
152 erico 1473
                public T append(Param p);
149 erico 1474
        }
1475
 
182 erico 1476
        public void finish() {
1477
 
1478
                for (Alias<?> a : selectAliases) {
1479
                        a.joined.clear();
1480
                }
1481
 
1482
                paramValues.clear();
1483
                sb = new StringBuilder();                      
1484
        }
1485
 
155 erico 1486
        /**
1487
         * Represents a query ready to execute
1488
         * @author erico
1489
         *
1490
         */
151 erico 1491
        public class Query {
1492
 
1493
                private Query() {}
1494
 
155 erico 1495
                /**
156 erico 1496
                 * Returns a <code>PreparedStatement</code> setting all given parameters in order.
155 erico 1497
                 *
1498
                 * @param params
161 erico 1499
                 * @return A <code>PreparedStatement</code> using this session connection
155 erico 1500
                 * @see #getSQL()
1501
                 * @see SQLUtils#prepare(java.sql.Connection, String, Object...)
1502
                 */
182 erico 1503
                private PreparedStatement prepare(Object... params) {
151 erico 1504
 
1505
                        try {
1506
 
158 erico 1507
                                PreparedStatement ppst = SQLUtils.prepare(session.getConnection(), getSQL(), params);
1508
 
1509
                                if (AnsiSQLBeanSession.DEBUG_NATIVE) {
1510
                                        System.out.println("CUSTOM QUERY (NATIVE): "+ppst);
1511
                                }
1512
 
1513
                                return ppst;
1514
 
151 erico 1515
                        } catch (SQLException e) {
1516
 
1517
                                throw new BeanException("Error preparing statement", e);
1518
                        }
1519
                }
1520
 
156 erico 1521
                /**
1522
                 * Prepares a statement with paramValues to execute the query manually.
161 erico 1523
                 * @return A <code>PreparedStatement</code> using this session connection
156 erico 1524
                 */
151 erico 1525
                public PreparedStatement prepare() {
1526
 
1527
                        return prepare(paramValues.toArray());
1528
                }
1529
 
156 erico 1530
                /**
1531
                 * Executes the query returning a <code>List</code> of beans declared in <b>FROM</b> clause.
1532
                 *
161 erico 1533
                 * @return A list containing all beans retrieved by <code>ResultSet</code>
156 erico 1534
                 * @see Alias#populateAll(ResultSet, Object)
1535
                 */
153 erico 1536
                public <T> List<T> executeQuery() {
1537
 
1538
                        PreparedStatement ppst = null;
1539
 
1540
                        try {
1541
                                ppst = prepare();
1542
 
1543
                                ResultSet rs = ppst.executeQuery();
1544
 
1545
                                List<T> list = new ArrayList<T>();
1546
                                T bean;
1547
 
1548
                                while (rs.next()) {
154 erico 1549
 
153 erico 1550
                                        bean = (T) aliasFrom.config.getBeanClass().newInstance();
1551
                                        aliasFrom.populateAll(rs, bean);
154 erico 1552
 
1553
                                        for (Sentence s : sentences.values()) {
169 erico 1554
                                                session.injectValue(bean, s.getProperty(),
154 erico 1555
                                                                s.getValue(rs), s.getReturnType().getTypeClass());
1556
                                        }
1557
 
153 erico 1558
                                        list.add(bean);
1559
                                }
1560
 
1561
                                return list;
1562
 
1563
                        } catch (Exception e) {
1564
 
181 erico 1565
                                throw new BeanException("Unable to execute query from QueryBuilder\n"+
1566
                                                e.getMessage(), e);
153 erico 1567
                        }finally {
1568
 
182 erico 1569
                                finish();
1570
 
153 erico 1571
                                SQLUtils.close(ppst);
1572
                        }
1573
                }
1574
 
158 erico 1575
                /**
1576
                 * Executes the query returning a single value according returnType of sentence in query.
161 erico 1577
                 * @return The value returned by query
158 erico 1578
                 */
1579
                public <T> T executeSentence() {
1580
 
1581
                        if (sentences.values().size() != 1)
1582
                                throw new BeanException("This query must have exactly one sentence to execute");
1583
 
1584
                        PreparedStatement ppst = null;
1585
 
1586
                        try {
1587
                                ppst = prepare();
1588
 
1589
                                ResultSet rs = ppst.executeQuery();
1590
 
1591
                                if (rs.next()) {
1592
 
1593
                                        if (!rs.isLast()) {
1594
                                                throw new BeanException("The query returns more than one result");
1595
                                        }
1596
                                        Sentence s = sentences.values().iterator().next();
160 erico 1597
                                        return (T) s.getValue(rs);
158 erico 1598
 
1599
                                }
1600
 
203 erico 1601
                                return null;
158 erico 1602
 
1603
                        } catch (Exception e) {
1604
 
181 erico 1605
                                throw new BeanException("Unable to execute sentence from QueryBuilder\n"+
1606
                                                e.getMessage(), e);
158 erico 1607
                        }finally {
182 erico 1608
 
1609
                                finish();
158 erico 1610
 
1611
                                SQLUtils.close(ppst);
1612
                        }
1613
                }
1614
 
152 erico 1615
                @SuppressWarnings("unchecked")
1616
                public <T> T getValueFromResultSet(ResultSet rs, String name) throws SQLException {
1617
 
1618
                        Sentence s = sentences.get(name);
1619
 
1620
                        if (s == null)
1621
                                throw new BeanException("The sentence name '"+name+"' is not included in query");
1622
 
1623
                        return (T) s.getValue(rs);
1624
                }
1625
 
155 erico 1626
                /**
1627
                 * Returns the SQL generated by QueryBuilder
161 erico 1628
                 * @return The String SQL
155 erico 1629
                 */
151 erico 1630
                public String getSQL() {
1631
 
163 erico 1632
                        if (parenthesis != 0) {
1633
                                throw new BeanException("Invalid parenthesis");
1634
                        }
151 erico 1635
 
163 erico 1636
                        applyRegex();
1637
 
151 erico 1638
                        if (AnsiSQLBeanSession.DEBUG) {
1639
                                System.out.println("CUSTOM QUERY: "+sb.toString());
1640
                        }
1641
 
1642
                        return sb.toString();
1643
                }
1644
 
1645
                public List<Object> getParamValues() {
1646
                        return paramValues;
1647
                }
1648
 
1649
        }
1650
 
149 erico 1651
}